Help us improve
Share bugs, ideas, or general feedback.
From pharaoh
Generates requirement RST directives from one source file's observable behavior, typed by target_level. Queries Papyrus for canonical terms; enforces strict shall-clause rules excluding internals, architecture, plans, FMEA.
npx claudepluginhub useblocks/pharaoh --plugin pharaohHow this skill is triggered — by the user, by Claude, or both
Slash command
/pharaoh:pharaoh-req-from-codeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The seven rules below govern what a CREQ's body looks like. All seven apply to every emission; violations of any rule mean the emission is a draft, not a valid CREQ.
Verifies a single drafted requirement (CREQ) against its cited source file, checking exceptions, triggers, types, symbols, identifiers, grounding density, adjectives, quantifiers, and branches; outputs per-axis JSON findings.
Authors, updates, and validates atomic functional and non-functional requirements with traceability matrices, validation packs, and explicit human-in-the-loop approvals. Use for creating or reviewing requirements as source of truth.
Guides deployment workflows with CI/CD patterns, rolling/blue-green/canary strategies, multi-stage Dockerfiles for Node.js, health checks, rollbacks, and production checklists for web apps.
Share bugs, ideas, or general feedback.
The seven rules below govern what a CREQ's body looks like. All seven apply to every emission; violations of any rule mean the emission is a draft, not a valid CREQ.
The grammatical subject of the shall clause is either:
artefact-catalog.yaml.Never a Python function, class, private method, or file.
Code-narration subjects (❌) vs component subjects (✅):
| Bad | Good |
|---|---|
from_source_a shall call check_license | The Source A Connector shall reject unlicensed use |
_apply_cli_overrides shall override credentials | The Source A CLI shall accept server credentials on the command line |
FooClient._classify_connection_error shall raise FooAuthenticationError on HTTP 401 | The Source A Connector shall signal an authentication failure when the server rejects the configured credentials |
ItemLoader shall load items from ImporterConfig.input_path via load_items | The Importer shall read items from the configured input path |
Tests: the component form is falsifiable by a tester who can't read the source (black-box), stable across refactors (renaming _apply_cli_overrides does not invalidate the CREQ), and traceable to the feat via :satisfies:.
No internal / private function names, no leading-_ methods, no class-dot-method references, no file paths, no line numbers ever (around line 165, in commands/foo.py, at the top of the module — all banned). Traceability to code lives in :source_doc:; the shall clause carries behavior. These two jobs stay separate.
Known prior failures this rule catches:
"record_key drives the create-vs-update decision" — names an internal field AND gets the mechanism wrong (actual decision variable is a different id attribute on the same record)."parse_timestamp raises on unparseable input" — names an internal function AND misstates the behavior (the function returns None; the caller raises).A clean behavioral shall with zero backticks and one :source_doc: is preferred over a code-narration shall with ten backticks.
:source_doc: emission is tailoring-drivenIf the project's <tailoring_path>/artefact-catalog.yaml declares source_doc in optional_fields or required_fields for the directive's type (target_level), emit it pointing at the implementing source code file. If the project does NOT declare source_doc for the type, do NOT emit it.
Rationale: emitting undeclared fields produces sphinx-build warnings under -W and breaks the build. Section 8 of #13 wired pharaoh-setup to populate the catalog from [needs.fields.X] declarations. This skill MUST honour the catalog on output.
When the project declares source_doc and the CREQ behavior spans multiple source files, pick the file that owns the primary observable (usually the converter module, not a CLI dispatcher). pharaoh-req-code-grounding-check axis #8 (source_doc_resolves) fails if the cited file is the spec RST or missing entirely.
When the project does NOT declare source_doc, code grounding moves to a backref comment in the source file via pharaoh-req-codelink-annotate (mode: backref). The CREQ stays clean of paths.
A CREQ whose shall clause paraphrases the feat capability with the same subject, verb, and scope is tautological and MUST NOT be emitted.
Test before every emission: what constraint does this CREQ impose that the feat body alone does not? Answers that count: a concrete pre-condition, a post-condition, an error contract, a default value, an ordering guarantee, a quantitative bound, a specific field / flag / command name. Answers that don't: just naming a sub-capability in the imperative.
Bad (tautology) — feat says "The CSV Connector enables bidirectional exchange between Sphinx-Needs and CSV files"; CREQ says "The CSV Importer shall convert a user-supplied CSV file into a needs.json file when the user invokes <cli> <subcmd> from-csv" — zero added constraint.
Good — "The CSV Importer shall fail with a non-zero exit code and a single-line error message on the first row whose mapped id column is missing or empty, without writing a partial needs.json" — specific precondition, specific post-condition, specific boundary observable.
For each :source_doc: file, enumerate and emit one CREQ per boundary-observable structure:
FooError when ."Expected floor per typical connector module (200-500 LOC, 3-8 exception classes, 5-10 config keys, 1-3 public functions): 12-20 CREQs per module. Under-decomposition below this floor means structures got bundled into compound shalls — split them.
Each emitted block's body has exactly one shall clause. Zero intra-clause conjunctions joining modal-verb phrases (, and shall / and shall / or raise / , or — all splits). Multiple observable behaviors = multiple CREQs. Intra-clause conjunctions are a hard fail regardless of behavioral quality; split the block before returning.
:verification: emission is tailoring-drivenverification in this skill names whichever of two tailoring slots the project declares it as:
<project_root>/ubproject.toml under [[needs.extra_links]] with name verification (or a project-renamed equivalent declared in pharaoh.toml under [skill.req_from_code.verification_link_name]). When declared, emit :verification: tc__TBD (or the project's declared placeholder convention).<tailoring_path>/artefact-catalog.yaml under optional_fields or required_fields for the directive's type. When declared, emit :verification: <value>.If neither slot declares verification for the directive's type, do NOT emit :verification:. The req-to-test relation is then declared elsewhere. For example, pharaoh-vplan-draft backlinks via :verifies: from the test side.
Resolution order: link declaration in ubproject.toml takes precedence over field declaration in the catalog. A project declaring both is over-specified. Warn but emit the link form.
Backticks signal "copy this string verbatim — it is a code symbol, config key, or protocol token". NOT for format acronyms (CSV, JSON, XML, TOML, HTML), document-type nouns (document, file, row), or emphasis (default, required).
Test: would a tester copy-paste this string into test code or configuration? If yes, backtick it. If not, leave it bare.
Backticks ARE acceptable on external-surface identifiers: CLI flags (--host), env vars (APP_LICENSE_KEY), TOML config keys ([myapp.export_config], links_delimiter), HTTP routes (/itemtypes), protocol tokens (HMAC-SHA256).
Example: a consumer module that reads self.config.output_format cites output_format (its own reference form), not the default-value literal (default_format) which lives in the config module. Citing the default-value form creates a false paper-trail — the grounding-check axis will fail because the shall clause names a symbol the cited file does not contain.
Invoke with a single source file (any language) assigned to this agent and (optionally) a shared Papyrus workspace for cross-agent terminology coordination. Emit one requirement (of type target_level) per distinct boundary-observable behavior expressed in the file. Do NOT emit reqs for behavior not grounded in the file (that is drafting, not reverse-engineering). Do NOT attempt architecture, verification plans, or FMEA — those are separate skills.
Two axes are tailored, both read at runtime from the consumer project's ubproject.toml / pharaoh.toml:
Type axis — need types and ID conventions are project-specific. Read [[needs.types]] entries from ubproject.toml (or .pharaoh/project/id-conventions.yaml if present) — each has directive and prefix. Do NOT hardcode comp_req as the only acceptable type. The caller passes target_level — use it verbatim as the directive name (in rst emit) or as the type field (in codelinks_comment emit).
Emit axis — whether to emit RST directive blocks or sphinx-codelinks-compatible one-line comments. Resolution order:
emit_override input (per-call).pharaoh.toml [pharaoh.codelink_comments].mode — "codelinks" → codelinks_comment; "backref" or absent → rst.ubproject.toml contains [codelinks.projects.*] → "codelinks_comment"; otherwise "rst"."rst".If on_missing_config == "prompt" (default) AND tailoring is missing (no target_level in [[needs.types]], or emit mode unresolvable), the skill returns {status: "needs_confirmation", proposal: {...}} with a tailoring patch the caller can confirm. Caller confirms → tailoring gets written (typically via pharaoh-tailor-fill) → re-invoke with on_missing_config="use_default" for silent proceed.
rst OR codelinks_comment).{file_path, target_level, tailoring_path, shared_context_path?, papyrus_workspace?, reporter_id, parent_feat_ids?, emit_override?, codelinks_project_name?, on_missing_config?, allowed_ids?, split_strategy?}. Output: single JSON object {"reqs": [{"id", "title", "type", "body", "source_doc", "satisfies", "verification", "raw_rst"}, ...]} for emit=rst, or {"codelinks": [str, ...]} for emit=codelinks_comment. On missing tailoring with on_missing_config=prompt: single JSON object {status: "needs_confirmation", proposal}.test_fixture.<ext> (.py / .cpp / .rs / .ts) containing exactly 3 named symbols (FooBar, BazQux, Quux), emitted reqs must mention all 3 by canonical name. Directive name must equal target_level. If parent_feat_ids is non-empty, every emitted block MUST contain :satisfies: <id1>, <id2>, ... with all parents comma-joined.pharaoh-arch-draft, pharaoh-fmea, pharaoh-plan.file_path: absolute path to the source file (any language).target_level: requirement artefact directive name as declared in the consumer project's ubproject.toml (e.g. "comp_req", "impl", "spec"). ID prefix is target_level + __ unless [[needs.types]].prefix overrides.tailoring_path: absolute path to the project's .pharaoh/project/ directory. Resolved per skills/shared/tailoring-access.md. Used by Step 1 to read artefact-catalog.yaml and the project's ubproject.toml once and cache the per-target_level declarations of source_doc and verification consumed by Rules 3 and 6.shared_context_path (optional): companion source file read by all agents in the fan-out (e.g. common.cpp). Read but NOT reverse-engineered.papyrus_workspace (optional): path to .papyrus/ for canonical-term coordination. Absent → no-memory mode (skip Steps 1 and 3).reporter_id: short identifier for this agent (e.g. req-from-code:csv2needs.py).parent_feat_ids (optional): list of parent feature IDs. When non-empty, every emitted block gets :satisfies: <id1>, <id2>, ... comma-joined.allowed_ids (optional): pre-allocated ID list. When provided, emitter MUST NOT invent IDs outside this list; emits only len(allowed_ids) reqs max; overflow logged as a warning comment.split_strategy (optional): "single" (default, whole file as one scope, target 1-5 reqs), "top_level_symbols" (per top-level symbol, target 1-3 reqs/symbol), or "sections" (per # --- / // === horizontal-rule marker, target 1-3 reqs/section). Plans supply this via ${heuristics.split_strategy(...)}.A single JSON object. The top-level key names the emit mode: reqs for emit=rst, codelinks for emit=codelinks_comment. Downstream skills key off the presence of one or the other.
emit=rst{
"reqs": [
{
"id": "<id_prefix><snake_case_id>",
"title": "<short_title>",
"type": "<target_level>",
"body": "The <Component subject> shall <observable behavior>.",
"source_doc": "<path to implementing source file>",
"satisfies": ["<parent_1>", "<parent_2>"],
"verification": "tc__TBD",
"raw_rst": ".. <target_level>:: <short_title>\n :id: ...\n :status: draft\n :satisfies: ...\n :source_doc: ...\n :verification: tc__TBD\n\n <body>\n"
}
]
}
Both source_doc and verification are conditional emit. source_doc MUST be omitted (from both the JSON object and the raw_rst block) if the project's artefact-catalog.yaml does not declare source_doc for the directive's type. verification MUST be omitted if neither [[needs.extra_links]] in ubproject.toml nor the catalog declares verification for the type. See Rules 3 and 6.
Field semantics:
id — <id_prefix><snake_case_id>. <id_prefix> defaults to target_level (comp_req → comp_req__foo_01). If [[needs.types]].prefix declares "CREQ_", use CREQ_foo_01.type — equals input target_level.satisfies — list of parent feat ids. Empty list when parent_feat_ids was empty. Always present (use []).raw_rst — exactly the RST directive block as it would appear in an .rst file. Downstream review / annotation skills read raw_rst when they need the directive text; helpers that consume reqs (e.g. by-stem grouping) read id / source_doc.emit=codelinks_comment{
"codelinks": [
"@ <title>, <id>, <target_level>, [<parent_1>, <parent_2>, ...]"
]
}
Each codelinks[i] is one comment line matching the project's [codelinks.projects.<name>.analyse.oneline_comment_style]:
start_sequence (default @).field_split_char (default ,) with surrounding spaces.needs_fields.pharaoh-req-codelink-annotate's job).status == "needs_confirmation"When tailoring is missing and on_missing_config == "prompt", output is a single JSON object {"status": "needs_confirmation", "proposal": {...}}. Downstream consumers check for this shape before parsing as reqs / codelinks.
Validated as json_obj by pharaoh-output-validate. The validator checks the top-level shape, then per-item shape against the regexes below.
Stage 1 — block recognizer (Python regex, re.MULTILINE):
^\.\. (?P<directive>[a-z_]+)::\s+(?P<title>.+)$
(?P<options>(?:^ :[a-z_]+:.*$\n?)+)
(?:^\n (?P<body>[\s\S]+?))?
(?=^\.\.\s|\Z)
Identifies one directive block bounded by the next .. at column 0 or end of input. re.MULTILINE without re.DOTALL keeps . line-bounded; options cannot leak into adjacent blocks.
Stage 2 — option enumeration (on the recognizer's options capture):
^ :(?P<option>[a-z_]+):\s*(?P<value>.*)$
re.finditer with re.MULTILINE enumerates every option/value pair.
Validator checks (per reqs[*]):
raw_rst matches Stage 1 + Stage 2 — block is well-formed.raw_rst directive name equals type and equals input target_level.raw_rst yields at least id and status. Whichever of source_doc and verification the project's tailoring declares for the directive's type MUST also appear. The other MUST NOT.parent_feat_ids was provided: satisfies field is non-empty and lists every parent id; raw_rst :satisfies: (or tailored child→parent link name) value matches.raw_rst is either declared in ubproject.toml [[needs.types]], a built-in sphinx-needs option, or a Pharaoh convention option. Reject unknown names (catches typos like subsatisfies).allowed_ids was provided: every reqs[*].id is a member of allowed_ids.emit=codelinks_comment — each codelinks[*] string must parse via sphinx-codelinks oneline_parser.parse_line() against the tailored oneline_comment_style.
Resolve tailoring_path per skills/shared/tailoring-access.md. Read <tailoring_path>/artefact-catalog.yaml and <project_root>/ubproject.toml once. Cache the per-target_level declarations of source_doc (field) and verification (link or field) for use by Rules 3 and 6.
Only applies if papyrus_workspace is provided. For each type / function / concept you may name in a req:
pharaoh-context-gather with mode="semantic". Semantic mode is required — substring recall silently misses morphological synonyms.Read file_path and, if provided, shared_context_path. Identify boundary-observable behaviors grounded in control flow and data flow. Ignore internal helpers, log messages, assertion text.
Apply split_strategy:
"single" (default): whole file as one scope. Target 1-5 reqs."top_level_symbols": enumerate top-level symbols via the patterns in ../shared/public-symbol-patterns.md. Emit per symbol. Target 1-3 reqs per symbol."sections": split at ^#\s*={3,} / ^//\s*={3,} markers. Target 1-3 reqs per section.In plan-driven runs the ${heuristics.split_strategy(...)} helper picks per-file by LOC (≤500 → single; 500-2000 with markers → sections; else → top_level_symbols).
Only applies if papyrus_workspace is provided. For each concept you will mention and that Step 1 did not return, invoke pharaoh-decision-record:
type: "fact"canonical_name: idiomatic casing for the source language (CamelCase for types; snake_case for functions/fields in Python/Rust/C; camelCase in TypeScript/Java). Preserve what the source uses.body: one sentence.reporter_id: your reporter_id input.tags: ["origin:req-from-code", "file:<basename>"].On "duplicate": a concurrent agent raced you; re-query via pharaoh-context-gather, adopt the existing spelling, rewrite your draft to match.
Read <project_root>/ubproject.toml and <project_root>/pharaoh.toml.
Type resolution: find [[needs.types]] entry where directive == target_level. Extract prefix. If not declared:
on_missing_config == "fail" → FAIL.on_missing_config == "prompt" → emit {status: "needs_confirmation", proposal: ...} and return without emitting reqs.on_missing_config == "use_default" → use <target_level>__ silently.Emit mode: per the Tailoring awareness order. Log the resolved mode on the header line.
Codelinks format (only if emit == "codelinks_comment"): resolve [codelinks.projects.<name>.analyse.oneline_comment_style] via codelinks_project_name or by matching file_path against each project's source_discover.src_dir. Zero or multiple matches with on_missing_config != "fail" → needs_confirmation. on_missing_config == "fail" → FAIL.
rst modeFor each boundary-observable behavior (per Rule 5 enumeration):
<short_title> — 3-6 word summary.:id: <id_prefix><filename_stem>_<n> — <id_prefix> resolved in Step 4. File basename (stem, snake_case) as disambiguator. Examples: comp_req__csv2needs_01, CREQ_csv2needs_01.:status: draft.:satisfies: <parent_1>, ... — iff parent_feat_ids non-empty. All parents comma-joined. If [[needs.extra_links]] declares a different outgoing name (e.g. realizes), use that instead.:source_doc: <path to implementing source file> -- emit only if the project's artefact-catalog.yaml declares source_doc for target_level per Rule 3.:verification: tc__TBD -- emit only if [[needs.extra_links]] in ubproject.toml declares verification (or the catalog declares it as a field) for target_level per Rule 6. Use the project's declared placeholder when one is configured.codelinks_comment modeFor each behavior, emit one line that sphinx-codelinks' oneline parser would read back into a need equivalent to what rst mode would produce. Follow tailored needs_fields order and escape rules. Do NOT include the language comment prefix — that is pharaoh-req-codelink-annotate's concern.
Rules 3 and 6 do not gate codelinks-mode output. The :source_doc: and :verification: RST options have no counterpart in the one-line comment string. Code grounding is implicit (the comment lives in the source file itself) and the verification relation is carried via the tailored needs_fields (e.g. as a links slot or omitted entirely). Project tailoring of which fields appear in the comment is governed by [codelinks.projects.<name>.analyse.oneline_comment_style], not by artefact-catalog.yaml or [[needs.extra_links]].
The links field renders as [<parent_1>, ...] when parent_feat_ids non-empty, else [] (or omitted if tailored default = []). The body shall-clause does NOT fit on a one-line comment — implied by the title and lost in this mode. For full shall-clause text use emit="rst".
Target: 1-5 reqs per file (per split_strategy). Fewer than 1 only if the file has no observable behavior; more than 5 suggests over-decomposition.
Emit one JSON object per the Output shape ({"reqs": [...]} for emit=rst, {"codelinks": [...]} for emit=codelinks_comment). Build each reqs[i] by populating id, title, type, body, source_doc, satisfies (use [] when empty), verification, and raw_rst (the literal RST block that would render the directive). Nothing else on stdout — no # emit=... header line, no prose wrapper, no fenced code block.
file_path not readable → return empty output (no reqs).pharaoh-context-gather errors → log and proceed as if no match found.pharaoh-decision-record returns "error" (not "duplicate") → log and proceed. Do not retry.Under pharaoh-execute-plan, a plan emitted by pharaoh-write-plan dispatches N instances of this skill via a foreach task over the file list. Per-CREQ review is scheduled as explicit top-level review_comp_reqs + grounding_check_comp_reqs + api_coverage_comp_reqs plan tasks (see pharaoh-write-plan templates); the plan DAG enforces them as dependencies of quality_gate. Direct out-of-plan invocation by a human auditor is acceptable; the caller is responsible for running the sibling reviews if coverage matters.
See shared/self-review-invariant.md for the rationale. Coverage is mechanically enforced by pharaoh-self-review-coverage-check reading the explicit plan-task output files.