Skip to content
Back to skills

Crane Natspec

ASecurity

This skill should be used when the user asks about "natspec", "documentation", "include-tag", "selector", "cast", "@custom:signature", "@custom:selector", "@custom:topiczero", "@custom:interfaceid", "AsciiDoc", or needs guidance on documenting Crane contracts with NatSpec and AsciiDoc include-tags.

  • 9 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 10, 2026
testinggobashtestinggitapisecuritydocumentation

Works with

  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 10, 2026

npx -y skills add cyotee/indexedex --skill crane-natspec --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Crane Natspec?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Crane Natspec
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/cyotee-crane-natspec/badge)](https://www.skillsdirectory.com/skills/cyotee-crane-natspec)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: crane-natspec
description: This skill should be used when the user asks about "natspec", "documentation", "include-tag", "selector", "cast", "@custom:signature", "@custom:selector", "@custom:topiczero", "@custom:interfaceid", "AsciiDoc", or needs guidance on documenting Crane contracts with NatSpec and AsciiDoc include-tags.
license: MIT
---

# Crane NatSpec & Documentation Standards

Crane uses NatSpec combined with AsciiDoc include-tags for accurate, extractable documentation.

**Critical Alignment Requirements (LR-3):**
- Skills reflect finalized standards per PRD: NatSpec verification **exclusively** uses the dedicated Foundry Script (`scripts/foundry/ComputeNatSpecValues.s.sol`) + central values file (NEVER ad-hoc `cast` or manual for authoritative @custom values).
- Use `CENTRALLY_COMPUTED_NATSPEC_VALUES.md` **exclusively** (ONLY those values) for all `@custom:selector`, `@custom:topiczero`, `@custom:interfaceid` in examples/docs.
- Full scope (LR-1): applies to **all Solidity code** including production + **every test file** (`.t.sol`, `TestBase_*.sol`, `Behavior_*.sol`, handlers, stubs, comparators, etc.).
- ERC1967: when documenting storage Repos, use/show the compliant `DEFAULT_SLOT = bytes32(uint256(keccak256(abi.encode("hier.name"))) - 1)` form (LR-6); cross-ref `crane-architecture`.
- Ties to GitBook/LR-2: accurate centrally-verified NatSpec enables required content on Create3FactoryDFPkg chain setup, reusable `DiamondPackageCallBackFactory` (interfaceId 0x949da331 from central), registries, ported protocol TestBases+Behaviors+utilities, and general utilities (Sets etc.).
- Test NatSpec (LR-7): public surfaces on test infrastructure must carry exact // tag:: + @custom using central values + Behavior declaration tests validate the documented surfaces.
- References: `docs/development/natspec.md`, PRD LR-1/LR-3/LR-7, AGENTS.md (NatSpec section), `crane-testing`/`crane-deployment` skills. Update this skill on standard evolution.

## AsciiDoc Include-Tags

Wrap documented symbols with include-tags for documentation extraction:

```solidity
// tag::MySymbol[]
/// @notice Description of the symbol
function myFunction() external { ... }
// end::MySymbol[]
```

### Tag Format Rules

- Tag markers must match exactly (no extra spaces inside `[]`)
- Tag name should match the symbol being documented
- Use PascalCase for tag names matching type names
- Use camelCase for function tag names

### Example Usage

```solidity
// tag::transfer[]
/// @notice Transfers tokens to a recipient
/// @param to_ The recipient address
/// @param amount_ The amount to transfer
/// @return success True if transfer succeeded
/// @custom:signature transfer(address,uint256)
/// @custom:selector 0xa9059cbb
function transfer(address to_, uint256 amount_) external returns (bool success);
// end::transfer[]
```

## Custom NatSpec Tags

### Functions

| Tag | Purpose | Example |
|-----|---------|---------|
| `@custom:signature` | Canonical signature string | `transfer(address,uint256)` |
| `@custom:selector` | bytes4 selector | `0xa9059cbb` |

```solidity
/// @notice Transfers tokens
/// @custom:signature transfer(address,uint256)
/// @custom:selector 0xa9059cbb
function transfer(address to_, uint256 amount_) external returns (bool);
```

### Errors

| Tag | Purpose | Example |
|-----|---------|---------|
| `@custom:signature` | Canonical error signature | `NotOwner(address)` |
| `@custom:selector` | bytes4 selector | `0x30cd7471` |

```solidity
/// @notice Thrown when caller is not the owner
/// @custom:signature NotOwner(address)
/// @custom:selector 0x30cd7471
error NotOwner(address caller);
```

### Events

| Tag | Purpose | Example |
|-----|---------|---------|
| `@custom:signature` | Canonical event signature | `Transfer(address,address,uint256)` |
| `@custom:topiczero` | bytes32 topic0 hash | `0xddf252ad...` |

```solidity
/// @notice Emitted on token transfer
/// @custom:signature Transfer(address,address,uint256)
/// @custom:topiczero 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
event Transfer(address indexed from, address indexed to, uint256 amount);
```

**Note**: Events use `@custom:topiczero` (bytes32), not `@custom:selector` (bytes4).

### ERC-165 Interfaces

| Tag | Purpose | Example |
|-----|---------|---------|
| `@custom:interfaceid` | bytes4 interface ID | `0x01ffc9a7` |

```solidity
/// @title IERC165
/// @custom:interfaceid 0x01ffc9a7
interface IERC165 {
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
```

## Full Scope (incl. Tests) and Verification Script

Per LR-1:
- **Scope**: All Solidity code: interfaces, contracts, libraries (Repos, Targets, Services, Facets, DFPkgs, AwareRepos), **and all test files** (TestBase_*, Behavior_*, IHandler, handlers, stubs, comparators, *.t.sol etc.).
- Test code that exposes public APIs must receive the same NatSpec + exact // tag::[] + @custom:* (using ONLY central values) as production code (see LR-7 point 10).
- Declaration tests (Behavior_IFacet etc.) must validate the documented values (e.g. `facetInterfaces()` returns selectors matching the @custom ones tagged in the interface/impl).

The verification script (`scripts/foundry/ComputeNatSpecValues.s.sol`) is the committed tool for this (see run instructions above). It itself uses // tag::ComputeNatSpecValues[] etc. and documents the process.

## NatSpec on Test Code (LR-1 + LR-7)

Example for a Behavior or TestBase public surface (use central IFacet values):

```solidity
// tag::Behavior_IFacet[]
/// @title Behavior_IFacet
/// @notice Validation helpers for IFacet (see central for selectors e.g. facetInterfaces 0x2ea80826)
library Behavior_IFacet {
    // tag::areValid_IFacet_facetInterfaces[]
    /// @notice ...
    /// @custom:signature areValid_IFacet_facetInterfaces(IFacet,bytes4[],bytes4[])
    /// @custom:selector 0x... (use central or script)
    function areValid_IFacet_facetInterfaces(...) public view returns (bool) { ... }
    // end::areValid_IFacet_facetInterfaces[]
}
// end::Behavior_IFacet[]
```

## Verification Script and Central Values Process (Mandatory per LR-1)

**Primary method**: Use the dedicated **Foundry Script** (committed in repo) for all authoritative values. Run:

```bash
forge script scripts/foundry/ComputeNatSpecValues.s.sol --sig "run()" -vvv
```

- This script uses compiler (`type(IInterface).interfaceId` for IDs; Solidity `keccak256` for selectors/topic0) -- reproducible, CI-auditable.
- Output the exact bytes; copy to `CENTRALLY_COMPUTED_NATSPEC_VALUES.md` (single source of truth) and/or into `@custom:*` tags.
- Helper `scripts/compute_natspec_values.sh` exists for quick exploration (uses cast internally) but **always verify final values via the Foundry Script**.
- Re-run after any interface/symbol addition or change.

**Central Values Rule (use ONLY)**:
- Always pull `@custom:selector` / `@custom:topiczero` / `@custom:interfaceid` **exclusively** from `docs/reports/gap/CENTRALLY_COMPUTED_NATSPEC_VALUES.md`.
- Current central pass date: 2026-07-02 (example values below; always consult the file).
- Subagents must follow strict read order before work: gap report + CENTRALLY... + PRD (LR sections) + AGENTS.md + source files.

Examples of central values (use ONLY from the file, do not recompute):

- IFacet interface surface (selectors):
  - `facetName()` : 0x5b6f4d01
  - `facetInterfaces()` : 0x2ea80826
  - `facetFuncs()` : 0x574a4cff
  - `facetMetadata()` : 0xf10d7a75
  - `supportsInterface(bytes4)` : 0x01ffc9a7

- IDiamondPackageCallBackFactory (interfaceId 0x949da331 from central):
  - `deploy(address,bytes)` : 0xe97fac05
  - `initAccount(address,bytes)` : 0x8e85783e
  - ...

- IOperable (interfaceId 0xa7f11160):
  - `isOperator(address)` : 0x6d70f7ae
  - `NotOperator(address)` selector: 0x76c6c93a
  - `NewGlobalOperatorStatus(address,bool)` topic0: 0x26ba28058a3c072a70c8fd315037fe9b3957237cef5c61a9652a8da41c673daa
  - ...

See `docs/development/natspec.md` "Verification Script Requirement" and "Central Values Process" + the script itself (which carries NatSpec + tags using the process).

## Computing Values (for exploration only)

`cast` (or manual) may be used for quick local checks, but final authoritative values for code/docs **must** come from the Foundry Script + CENTRALLY_COMPUTED... (cast matches keccak for selectors/topics but the script is the required verifier).

### Function Selector (bytes4)

```bash
cast sig "transfer(address,uint256)"
# Output: 0xa9059cbb
```

### Error Selector (bytes4)

```bash
cast sig "NotOwner(address)"
# Output: 0x30cd7471
```

### Event Topic0 (bytes32)

```bash
cast keccak "Transfer(address,address,uint256)"
# Output: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
```

### Interface ID (bytes4)

For interface IDs, prefer the script or:

```solidity
bytes4 interfaceId = type(IMyInterface).interfaceId;
```

Or compute manually by XOR-ing all function selectors (cross-check with script output).

## Complete Documentation Example

Use exact values from `CENTRALLY_COMPUTED_NATSPEC_VALUES.md` (ONLY). Gold standard tag formats use hyphenated params for overload disambiguation (see AGENTS.md + ERC8023 examples).

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

// tag::IOperable[]
/// @title IOperable
/// @notice Interface for operator-based access control
/// @custom:interfaceid 0xa7f11160
interface IOperable {

    // tag::NewGlobalOperatorStatus(address-bool)[]
    /// @notice Emitted when global operator status changes
    /// @param operator The operator address
    /// @param status The new status
    /// @custom:signature NewGlobalOperatorStatus(address,bool)
    /// @custom:topiczero 0x26ba28058a3c072a70c8fd315037fe9b3957237cef5c61a9652a8da41c673daa
    event NewGlobalOperatorStatus(address indexed operator, bool indexed status);
    // end::NewGlobalOperatorStatus(address-bool)[]

    // tag::NotOperator(address)[]
    /// @notice Thrown when caller is not an operator
    /// @param caller The unauthorized caller
    /// @custom:signature NotOperator(address)
    /// @custom:selector 0x76c6c93a
    error NotOperator(address caller);
    // end::NotOperator(address)[]

    // tag::isOperator(address)[]
    /// @notice Checks if an address is a global operator
    /// @param query_ The address to check
    /// @return isOperator_ True if operator
    /// @custom:signature isOperator(address)
    /// @custom:selector 0x6d70f7ae
    function isOperator(address query_) external view returns (bool isOperator_);
    // end::isOperator(address)[]

    // tag::setOperator(address-bool)[]
    /// @notice Sets operator status
    /// @param operator_ The address
    /// @param status_ The status
    /// @custom:signature setOperator(address,bool)
    /// @custom:selector 0x558a7297
    function setOperator(address operator_, bool status_) external;
    // end::setOperator(address-bool)[]
}
// end::IOperable[]
```

For IFacet (central values: facetName 0x5b6f4d01, facetInterfaces 0x2ea80826 etc):

```solidity
// tag::IFacet[]
/// @title IFacet
/// @custom:interfaceid (computed via XOR in script; see central)
interface IFacet {
    // tag::facetInterfaces()[]
    /// @notice ...
    /// @custom:signature facetInterfaces()
    /// @custom:selector 0x2ea80826
    function facetInterfaces() external view returns (bytes4[] memory);
    // end::facetInterfaces()[]
}
// end::IFacet[]
```

## Validation Checklist

For each documented symbol, verify:

- [ ] Include-tags wrap the symbol correctly (exact `// tag::Name(params)[]` ... `// end::Name(params)[]`, hyphenated params for overloads/events)
- [ ] Tag names match symbol names
- [ ] `@custom:signature` matches actual signature exactly
- [ ] `@custom:selector` / `@custom:topiczero` / `@custom:interfaceid` taken **ONLY** from `CENTRALLY_COMPUTED_NATSPEC_VALUES.md` (or freshly generated by running the Foundry Script `forge script scripts/foundry/ComputeNatSpecValues.s.sol --sig "run()" -vvv`)
- [ ] Rich NatSpec present (`@notice`, `@param`, `@return`, `@custom:emits`, `@custom:throws` as applicable)
- [ ] Repos document **both** parameterized (`_layoutStruct(bytes32)`) and default overloads with distinct tags
- [ ] Facets implement/document full `IFacet` using central values (e.g. 0x2ea80826)
- [ ] Test surfaces (Behavior, TestBase, handlers) also tagged when public

## ERC1967 in NatSpec Context (LR-6 tie-in for LR-3)

When documenting storage libraries, the NatSpec + tags must reflect the canonical ERC1967-derived slot:

```solidity
// tag::DEFAULT_SLOT[]
bytes32 internal constant DEFAULT_SLOT = bytes32(uint256(keccak256(abi.encode("crane.example.slot"))) - 1);
// end::DEFAULT_SLOT[]
```

See `crane-architecture` skill and LR-6. Old direct-keccak forms without `- 1` are non-compliant.

## Additional Resources

### Reference Files

- **`references/natspec-examples.md`** - More complete examples
- `docs/development/natspec.md` (full LR-1 details, verification script, central process, gold standard ERC8023 files)
- `scripts/foundry/ComputeNatSpecValues.s.sol` (the dedicated LR-1 Foundry Script)
- `docs/reports/gap/CENTRALLY_COMPUTED_NATSPEC_VALUES.md` (use ONLY these values)

### Quick Reference

| Symbol Type | Selector Tag | Hash Type |
|-------------|--------------|-----------|
| Function | `@custom:selector` | bytes4 |
| Error | `@custom:selector` | bytes4 |
| Event | `@custom:topiczero` | bytes32 |
| Interface | `@custom:interfaceid` | bytes4 (XOR of selectors) |

### LR Ties (for Agents)

- LR-1 primary: this process + Foundry Script + full scope (tests included) + central.
- LR-2: verified NatSpec supports GitBook extraction for chain setup (Create3FactoryDFPkg), DiamondPackageCallBackFactory reuse (0x949da331), registries, protocol utilities + TestBases, Sets/math.
- LR-3: keep this skill + others in sync; global + repo `.claude/skills/`.
- LR-4: documented, centrally-verified, reusable facets/packages enable "deploy once, attach everywhere" (security via verified reuse for agents; cost via no re-deploy).
- LR-7: declaration tests + NatSpec on test code use these values + Behaviors.

Update this skill (and its gap report) whenever standards evolve (LR-3). Always follow the strict read order for any NatSpec work.

Files in this skill

  • SKILL.md14 KB
  • references/natspec-examples.md7.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…