Rigorous PRD-writing methodology for NetAlertX — challenge the idea, verify every claim against actual code, trace every downstream consumer of a mechanism, evaluate performance impact against the schema/indexes that actually exist, record rejected alternatives and open-issue decisions explicitly, and do a dedicated final-check pass before calling it done. Use this when asked to write, draft, or review a PRD, design doc, or feature proposal.
68
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
The canonical home for this skill is prd-writing in netalertx/NetAlertX
Triggered by: "write a PRD", "draft a design doc", "spec out this feature", "create a PRD for X". Reserve this for changes where getting the design wrong is expensive to unwind — new cross-cutting mechanisms, schema changes, anything touching multiple subsystems. A one-file bug fix doesn't need this process.
A UI feature that needs to survive a page reload, coordinate across tabs, or react to a backend push/poll is a cross-cutting mechanism even when it looks like "just a small UI feature." It's easy to start implementing straight away because the visible surface (a badge, an icon) looks trivial - but the hard part is always the state-propagation mechanism underneath, and that mechanism usually has hidden dependencies on other code that already touches the same data. A real case: a "settings still applying" indicator went through three full implementation-and-break cycles (a cookie with a guessed timeout, a new PHP endpoint duplicating existing logic, a localStorage/polling redesign with its own bugs) before anyone traced every existing consumer of app_state.json and found that front/js/sse_manager.js's handleStateUpdate() already pushed the exact signal needed to every open page. Step 4 ("trace every downstream consumer") below would have caught this on attempt #1 if it had been applied before writing any code, not after three failures.
Every sentence that asserts something about how the code currently works must be checked against the actual code before it goes in — not written from memory, not inferred from a plugin's name or reputation, not assumed because it sounds plausible. Two failure patterns to watch for:
Both are plausible, well-written, and wrong. Reading the code first catches both.
file:line for every claim about current behavior. Delegate to an Explore/general-purpose agent for breadth if the surface area is large, but treat its findings as a starting point to spot-check, not a finished citation — verify anything load-bearing yourself before it goes in the PRD. The same applies to a prior audit or PRD this one continues from: re-read its full detail section for the specific finding, not just a one-line summary-table row, before citing or extending it — a summary row can omit a caveat ("already indexed," "already fixed elsewhere") that only the detail text states, and citing the row alone can reintroduce a claim the detail text already corrected.mylog("none", ...) in this codebase — the always-shown floor, matching how real errors are already logged) is close to a hard requirement whenever such an event is detected at all; a user-facing alert (write_notification()) is a materially bigger decision — it's persistent, unprompted, and (per existing precedent, e.g. api_server_start.py's unauthorized-access alert) not rate-limited anywhere in this codebase, so a repeat offender can spam it. Don't let "should we detect/handle this" and "should we alert the user about it" collapse into one decision — record the logging as close to a given, and the user-facing alert as its own, explicit, separately-decidable (and separately deferrable) open issue.### Rejected: X). Without this, a future reader — or your own future self — re-proposes the rejected idea because the "why not" only ever existed in a conversation, not in the document.test/db_test_helpers.py fixtures is the template for the next one, not a reason to build new test infrastructure). When execution actually starts, write that test code before the implementation, run it against the pre-fix code, and confirm it fails for the right reason before writing a line of the fix. What "the right reason" means depends on what's being tested: a regression test against existing behavior must fail with a real behavioral assertion mismatch, not a collection/import error - a test that never failed red that way can't be trusted to have caught anything, and is usually the first sign the fixture doesn't actually distinguish broken from fixed behavior (a real case: a single-device DB fixture passed against both the buggy and the fixed code, because nothing in it could tell "this row matches" from "any row matches" - see scan-pipeline Gotcha 8). A test for a brand-new contract (a function/interface that doesn't exist yet) legitimately fails with a missing-interface error instead (AttributeError, ImportError) before it's written - that's the expected red for that case, not a sign the test is wrong. Writing the test first also tends to surface implementation code that's awkward to exercise in isolation - treat that as a refactor signal, not friction to route around.server/api_server/*_endpoint.py), GraphQL (graphql_endpoint.py), and a Prometheus /metrics endpoint (prometheus_endpoint.py). Skipping this question doesn't mean "no API access needed"; it means the answer gets silently decided later by whoever first wants to query the new data externally, usually as its own separate feature request re-litigating a design this PRD already had full context to settle. Not everything needs exposure: purely internal/diagnostic state with no plausible external consumer doesn't, but say so explicitly, with the reason, rather than leaving it unaddressed.CREATE INDEX for every index on the tables involved, not just the first one you find — a column can have both a plain index and a separate expression index (e.g. idx_eve_mac_date_type ON Events(eveMac, ...) alongside idx_eve_lower_mac_date_type ON Events(LOWER(eveMac), ...)), and missing the second one produces a wrong verdict. Then weigh cost by how often the query runs (once is nothing; every few minutes forever is a standing cost) and by real scale — production users run 10,000+ devices, not a homelab handful. A CurrentScan with 2-5 rows per device (one per contributing plugin) is routinely 20,000-50,000+ rows in one cycle; reason about that number, not a smaller hopeful one. A correlated EXISTS/subquery re-evaluated per outer row is fine if the correlated column is indexed — e.g. current_scan_presence_condition() (server/scan/presence.py) is exactly this shape against CurrentScan.scanMac, covered by idx_currentscan_scanmac. The real risk is an unindexed correlated lookup: an accidental self-join scanning the full inner table per outer row, which looks fine and passes tests at small scale but isn't at production scale. Check with EXPLAIN QUERY PLAN at a realistic row count, built against the complete real index set (copy every CREATE INDEX for the table, or run it against an actual app.db) rather than a hand-picked subset — a partial index set produces a misleading plan in either direction, not just "looks worse than it is." If it comes back unindexed for real, a GROUP BY aggregate is the usual fix.### Rejected: X subsection for any alternative seriously considered (step 5).file:function:line references, not bare filenames.EXPLAIN QUERY PLAN check at realistic scale if the Performance impact section found a genuine risk).If a skill already documents the subsystem the feature touches, load it before researching from scratch — don't re-derive call graphs or mechanism details that are already written down. If the feature touches a subsystem with no such skill, and understanding it required significant re-derivation from raw code, that's a signal to write one afterward so the next PRD in that area doesn't start from zero.
Add the Implementation checklist (see Structure above) to the PRD file as the first edit, before touching any other code - an unchecked, visible checklist in the file you're actively working in is the forcing function; a rule read once at skill-load time is not.
Before writing a single line of implementation code, write an explicit plan (via EnterPlanMode or an equivalent written plan) covering which files change, in what order, and which test(s) get written first. Understanding the PRD's design is not the same as planning its implementation - in practice these have been conflated, going straight from "I've read and understood this PRD" into code edits without ever writing either the plan or the test required below.
Then follow step 7 above literally, not just in spirit, at implementation time too: write the test, run it against the pre-fix code, confirm it fails for the right reason (a real behavioral mismatch, or a legitimate missing-interface error for a brand-new contract - not a setup bug), and only then write the fix. Writing the test after the implementation and asserting it "would have failed" is not equivalent - it's an unverified claim. If you catch yourself having implemented first, the correct recovery is to actually revert the implementation, confirm the test fails red against the reverted code, and only then reapply the fix - not to just add tests afterward and call it done.
Never reference a PRD's filename, path, or the word "PRD" in committed code, comments, docstrings, or commit messages - PRDs/ is gitignored and local-only, so a reader of the committed code later has no way to find what it's pointing at, and the reference rots the moment the PRD file is deleted or moved. Explain the why self-containedly in the code itself (a design PRD's rationale restated in the comment, not a pointer to go read the PRD) instead.
.gemini/internal-docs/PRDs/<kebab-case-name>.md, unless the user specifies otherwise. Mark the status line (**Status:** Draft — pending review) so it's clear this hasn't been approved yet, and keep the author line accurate about who made the calls (a design discussion with an assistant is not sole assistant authorship).
netalertx/NetAlertX
f669502
Canonical home
Also appears in
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.