Skip to content

Commit 7f37f07

Browse files
authored
docs(skill+pitfalls): four new defect classes from the 2043/2042 review round (#2069)
Pitfalls: runtime state and key material never enter commits (16), new views wire desktop AND mobile registries (17), retrofit columns on shipped stores need guarded ALTERs because the migration runner baselines pre-existing DBs without executing (18), scope deltas vs the issue are stated explicitly (19). Extended pitfall 1 with the house project auth-guard pattern and pitfall 15 with the answer-in-thread requirement. Skill: fold-first now requires in-thread replies per numbered item and bot review freshness checks; new scope-honesty subsection covers deferral notes, follow-up issues, and the git status runtime-artifact check.
1 parent 18e9928 commit 7f37f07

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

‎.claude/skills/taos-development-skill/SKILL.md‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,15 @@ A finding folded within hours merges the same day; a finding left while you open
240240
new PRs stalls the whole train behind it (the maintainer will not merge past an
241241
open finding, ever).
242242

243+
Folding means code AND a reply: answer every numbered item in the PR thread
244+
with the commit that addresses it or a concrete rebuttal. Code pushed without
245+
in-thread replies leaves the fold formally open and the verdict at HOLD.
246+
247+
Bot review freshness is part of folding: check WHICH commit a bot actually
248+
reviewed. A rate-limited or stale "SUCCESS" on an older head is not a pass -
249+
after pushing fixes, re-trigger the review (for CodeRabbit: comment
250+
`@coderabbitai review`).
251+
243252
### Rebase cadence and the stale-base rule
244253

245254
- dev moves fast. When your PR shows CONFLICTING, rebase onto current dev
@@ -265,6 +274,16 @@ A close without a successor link reads as lost work and forces the maintainer
265274
into git forensics (this happened with #1927/#1924 - both were legitimate
266275
"landed via" closures that looked like data loss for hours).
267276

277+
### Scope honesty per slice
278+
279+
- The PR body states exactly what it ships versus what its issue scopes. Any
280+
deferred part gets an explicit deferral note AND a follow-up issue filed in
281+
the same push; the parent issue never auto-closes on a partial slice.
282+
- Before committing, `git status` must show only intended source changes -
283+
runtime artifacts (anything under `data/`) never enter a commit, and a new
284+
component that writes under `data/` gitignores its directory in the same PR
285+
(pitfall 16).
286+
268287
### One PR per slice
269288

270289
- A fix and the test that proves it belong in ONE PR (pitfall 13 in

‎docs/contributor-pitfalls.md‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ session + CSRF, or the agent-token allowlist in `auth_middleware.py`) and a
1212
test asserting an unauthenticated or cross-principal caller gets 401/403.
1313
Example: PR #2036 added `GET /api/secrets/agent/{name}/github` with no guard,
1414
leaking installation IDs and repo names to any caller.
15+
For project-scoped routes, copy the house guard verbatim:
16+
`if not user.is_admin and user.user_id != p["user_id"]` followed by a masked
17+
404 (see routes/projects.py). A bare 403 without the admin bypass both locks
18+
out admins and leaks resource existence to other users (#2042).
1519

1620
**2. Bind both directions on authenticated channels.**
1721
When a message or envelope arrives over an authenticated channel, verify BOTH
@@ -37,6 +41,16 @@ and strips it. Tokens we only VERIFY are stored hashed; tokens we must PRESENT
3741
outbound are the only ones stored recoverable. Example: PR #2009 moved the
3842
GitHub App RSA key out of plaintext config.yaml.
3943

44+
**16. Never commit runtime state or key material.**
45+
Anything a store or subsystem writes under `data/` at runtime is state, not
46+
source. A PR that introduces a component writing under `data/` must add that
47+
directory to `.gitignore` in the SAME PR, and `git status` must be checked for
48+
runtime artifacts before every commit. Any private key that reaches a pushed
49+
commit is burned: regenerate it, and keep it out of the target branch's history
50+
(squash merge, or rewrite the branch). Example: `data/hub/identity.json` with
51+
live signing/encryption keys entered one branch's history and was then
52+
re-committed by a second PR (#2043 history, #2042).
53+
4054
## Correctness
4155

4256
**5. Never take element `[0]` of a collection that can hold more than one.**
@@ -74,6 +88,12 @@ share flow), state it in the PR body and file a follow-up issue. Example: PR
7488
permissions. Wire the real value or do not add the parameter yet. Example:
7589
PR #2036 `handleSaveGrants`.
7690

91+
**17. A new view must be wired into every surface it has: desktop AND mobile.**
92+
The desktop tab list and the mobile tab order are separate registries; updating
93+
one and not the other ships a view that is unreachable on phones (#2042:
94+
`TABS` updated, `mobileTabOrder` missed). Grep for every registry the sibling
95+
views appear in and update all of them.
96+
7797
## Store and schema
7898

7999
**11. SCHEMA is the frozen v1; new columns and their indexes go in MIGRATIONS.**
@@ -87,6 +107,18 @@ Running a data migration twice must be a no-op (existence checks, INSERT OR
87107
IGNORE, migrated-from markers) and there must be a test proving the second run
88108
changes nothing. Example done right: PR #2028 `test_migrate_idempotent`.
89109

110+
**18. Retrofitting a column onto an already-shipped store needs a guarded
111+
ALTER, not just a MIGRATIONS entry.**
112+
The migration runner baselines pre-existing databases at the latest version
113+
WITHOUT executing the migrations (FOOTGUN #2 in `db_migrations.py`'s own
114+
docstring), so a plain `MIGRATIONS = [(1, "ALTER TABLE ...")]` on a store that
115+
already shipped is a silent no-op on every upgraded install: fresh DBs work,
116+
upgraded DBs lose the feature at runtime. Use the guarded `_post_init` pattern
117+
(PRAGMA table_info, ALTER only when the column is absent - see
118+
`knowledge_store._migration_v1_add_user_id`) and add an upgrade test that
119+
builds the PRE-change schema first. Fresh-DB tests are structurally blind to
120+
this class (#2043: `peer_fingerprint`).
121+
90122
## Process
91123

92124
**13. A fix and its test belong in one PR.**
@@ -110,3 +142,15 @@ Findings are gated on severity of content, not review state. Address each one
110142
(fix it or rebut it concretely in the thread); never merge past an open
111143
finding, and never let a "pass" verdict from a stale commit stand in for the
112144
current head.
145+
A finding is closed only when it is ANSWERED IN-THREAD: reply to each numbered
146+
item with the commit that addresses it or a concrete rebuttal. Pushing code
147+
without replies leaves the finding open - the reviewer re-verifies blind and
148+
the verdict stays HOLD (#2043 round two).
149+
150+
**19. State scope deltas against the issue explicitly.**
151+
If a PR ships less than its issue scopes - a subfeature dropped, owner-only
152+
routes where the issue says peer-serving, a "live" view that fetches once -
153+
say so in the PR body and file the follow-up issue in the same push. Never let
154+
a partial slice close the parent issue. Silent shortfalls read as done, get
155+
caught in review anyway, and cost a full extra round (#2042: community chat
156+
and peer access absent with no deferral note).

0 commit comments

Comments
 (0)