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.
How 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.
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.
npx claudepluginhub useblocks/pharaoh --plugin pharaohVerifies 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.
Provides CDSS development patterns for drug interaction checking, dose validation, clinical scoring (NEWS2, qSOFA), and alert classification integrated into EMR workflows.