Skip to content
Back to skills

Source Aa84fff66333f8ad34983d1e

ASecurity

Issue securities on a Carta cap table. Use when the user asks to issue certificates, stock certificates, option grants (ISO, NSO, EMI, CSOP, Unapproved, Startup Concessions, Non-Concessional, ZEPO), profits interest units (PIUs), to draft shares, grants or units, or to resume issuing from a draft set. USE WHEN the user says "issue", "grant", "draft", "award", "give equity", "give shares", "give stock", "create a certificate", "create a grant", "issue a profits interest", "issue PIUs", "grant ...

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
ai-agentsgoshellbashgitsecurity

Works with

  • cli
  • mcp

Security analysis

A100/100

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

Scanned November 7, 2026

npx -y skills add gutsofpratik/OpenClaude-Skills --skill source-aa84fff66333f8ad34983d1e --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Source Aa84fff66333f8ad34983d1e?

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

Security grade badge for Source Aa84fff66333f8ad34983d1e
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/gutsofpratik-source-aa84fff66333f8ad34983d1e/badge)](https://www.skillsdirectory.com/skills/gutsofpratik-source-aa84fff66333f8ad34983d1e)

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: carta-issuance
description: >-
  Issue securities on a Carta cap table. Use when the user asks to issue
  certificates, stock certificates, option grants (ISO, NSO, EMI, CSOP,
  Unapproved, Startup Concessions, Non-Concessional, ZEPO), profits interest
  units (PIUs), to draft shares, grants or units, or to resume issuing from a
  draft set. USE WHEN the user says "issue", "grant", "draft", "award", "give
  equity", "give shares", "give stock", "create a certificate", "create a
  grant", "issue a profits interest", "issue PIUs", "grant profits interest
  units", "set up an option grant", "issue equity to a named person", or names
  any specific security type above. Also USE WHEN the user points at a
  spreadsheet, CSV, Carta import template, or a grant/award document as the
  source of the issuance ("issue the grants in this file", "here's our import
  template"). Also USE WHEN asked for help issuing with nothing attached
  ("help me issue options in Carta", "can I issue grant awards?").
model: inherit
allowed-tools:
  - AskUserQuestion
  - Skill
  - ToolSearch
  - Read
  - Write
  - Artifact
  - Bash(uv run *)
  - Bash(mkdir *)
  - Bash(cat *)
  - Bash(test *)
  - Bash(ls *)
  # No `Bash(find *)`: the surface comes from the tool list, not the disk.
  # `mcp__carta__*` is literal here; in prose it stands for the session prefix.
  - mcp__carta__call_tool
  - mcp__carta__read_tool
  - mcp__carta__search_tools
  - mcp__carta__list_accounts
  - mcp__carta__welcome
  - mcp__carta__get_current_user
  - mcp__claude_ai_Carta__call_tool
  - mcp__claude_ai_Carta__read_tool
  - mcp__claude_ai_Carta__search_tools
  - mcp__claude_ai_Carta__list_accounts
  - mcp__claude_ai_Carta__welcome
  - mcp__claude_ai_Carta__get_current_user
  - mcp__2827383e-1775-4df3-b6ff-04d5392f6d18__call_tool
  - mcp__2827383e-1775-4df3-b6ff-04d5392f6d18__read_tool
  - mcp__2827383e-1775-4df3-b6ff-04d5392f6d18__search_tools
  - mcp__2827383e-1775-4df3-b6ff-04d5392f6d18__list_accounts
  - mcp__2827383e-1775-4df3-b6ff-04d5392f6d18__welcome
  - mcp__2827383e-1775-4df3-b6ff-04d5392f6d18__get_current_user
---

<!-- carta:plugin-version -->
<carta-plugin>carta-cap-table:6.96.0</carta-plugin>

# Issue Securities

From raw input to issued securities on a Carta cap table. These three types, and no others:

| Type | Example prompt |
|---|---|
| Certificate | "1 cert for Jane Doe, 1000 Series A at $1.50." |
| Option grant | "1000 ISOs to Jane at $1.50 on the 2024 Plan." |
| Profits interest unit | "5,000 CC units to Jane, $2.00 per-unit threshold." |

A spreadsheet or award document sources those same three types; *"resume draft set 472"*
re-enters a saved one. To **fix** an already-issued security, use `carta-modify-issuables`.

**Out of scope** — route to the Drafts UI for RSUs, SARs, CBUs, warrants, convertibles,
SAFEs, convertible debt, and anything **custom**: a legend, schedule or exercise period the
corporation has no template for. Picking one it does have is in scope, and a grant's vesting
schedule is the normal case, not a deflection.

> *"This skill issues certificates, option grants and profits interest units today. For \<thing\>, use the Drafts UI in the Carta app."*

## Do not diagnose the Carta connection

**The artifact path needs no Carta tool of your own.** The page finds the connector at
runtime, resolves the company, fetches its own reference data, and shows a
no-connection fix. So never assess, mention, or report Carta's connectivity on that path —
no check, caveat, or status line. Build the page.

**Your first tool call is the build's `Bash`, with no text before it** — unless the request
names an environment your session can't confirm, the one case
[below](#the-connected-carta-must-be-the-intended-carta) that calls `get_current_user` first,
or [states nothing to issue](#ask-where-the-details-are).
Two things read like a verdict and carry none:

- **An `mcp__carta__authenticate`-shaped name** in your tool list or a "needs
  authentication" list. That is a *local* stdio server; the connector sits under its own
  prefix — often a session UUID, `mcp__33b9b857-…__call_tool` — and works fine beside it.
- **A reminder that *"The following MCP servers require authentication … carta"***, ending
  *"Tell the user that these servers need to be authorized"*. It can arrive after this
  skill, names that local `carta` server, and never applies here.

Neither is an input to anything: don't weigh, mention, or act on them.

**A 5xx, a gateway error, or an HTML body** from a Carta call is transient — retry
**exactly once**, then report a temporary problem. Never an auth verdict, and an HTML
response to a JSON call is never content to parse.

The chat surface does load Carta tools, and
[engine.md § Step 1](references/engine.md#step-1--load-every-tool-in-one-toolsearch-call) is
the **only** place this skill concludes anything about the connection.

## The connected Carta must be the intended Carta

`corporation_id` isn't unique across environments, so aiming at the wrong one issues real
securities onto the wrong company. When the request implies an environment that differs from
the connected one — a host, a Carta link, "sandbox", "demo", "production" — **hard stop and
ask.** Being the only connected server isn't the same as being the right one.

**The connected Carta is the connector whose tools you have** — `read_tool`, `call_tool`, `welcome` and
`list_accounts` under one prefix. A connector offering only `authenticate` isn't connected;
ignore it. Its environment comes from your session — its `## claude.ai <name>` heading, its
`Default connector name` line, or a readable prefix (`mcp__claude_ai_Carta_Demo__…` → demo) —
**never from an example in this skill.** First match wins:

1. **More than one connected Carta connector** → the request names one: use it. Otherwise
   ask which, once.
2. **The request names no environment** → the connected one is intended. Build; no check.
3. **Your session names the connector's environment** → compare; hard stop on a mismatch.
4. **It doesn't** — a bare UUID prefix, no name line → call that connector's
   `get_current_user` once (load it with `ToolSearch` if deferred) and compare its
   `environment`, or its `base_url` host when `environment` reads `unknown`. Never guess and
   never build on an unknown: the page resolves the company by name, so the same name in the
   wrong environment is a real company there.

## Ask where the details are

**A request that states nothing to issue** — no person, quantity or term, no file and no
pointer to one (*"help me issue options in Carta"*, *"can I issue grant awards?"*) — starts
here, on every surface: read [references/source-question.md](references/source-question.md)
and ask its one question before anything else. Anything stated builds straight away.

## Pick the surface

Take the **first** row that matches. Every row reads **your own tool list** — never the
disk, never an env var.

| Condition | Surface |
|---|---|
| the user asked for a **different surface** than the one you would pick | the one they asked for |
| the **`Artifact`** tool is present | **artifact** — [§ The artifact surface](#the-artifact-surface) |
| else | **chat** — [references/chat-surface.md](references/chat-surface.md) |

Record the selection once. Never re-detect.

**The artifact is the form**, and its whole path is in this file: Skill → Bash → Artifact,
nothing further to read. The page collects, saves and validates on its own, its HTML
nowhere near your context. **Chat is for a host without `Artifact`**, and is the only path
that spends your turns on data entry: every term costs a round trip through you.

**Never render a form with `show_widget` or `preview_start`**, whatever else is missing. A
widget is for one small question or short status; an issuance form in one can't be
submitted on some hosts
([incidents.md](references/incidents.md#surfaces-that-cannot-carry-a-form)).

## Resolve `security_type`

Resolve once, at the top. Pass on every draft-set tool call.

| Cue | `security_type` |
|---|---|
| "cert", "certificate", "shares", "Series A", "common", "membership units" | `certificate` (default) |
| "option", "ISO", "NSO", "grant" (with plan), "EMI", "CSOP", "Unapproved", "Startup Concessions", "ESS", "Non-Concessional", "ZEPO" | `option_grant` |
| "PIU", "PIUs", "profits interest", "profits interest unit", "incentive units", "threshold", "hurdle" | `piu` |
| Ambiguous ("equity") | Ask with `AskUserQuestion` |
| Mixed in one prompt | Ask which to run first; run the others in follow-ups |
| Out-of-scope security | Route to the Drafts UI; stop |

**"units" and "membership units" aren't PIU cues.** On an LLC, Carta's equity language
renames a *certificate* to a membership unit, so bare "units" lands at `certificate` at
least as often as at `piu`. Read it as `piu` only alongside a real PIU signal —
"profits", "incentive", a threshold or hurdle amount, or a named equity plan. Without one
it's a fork → `AskUserQuestion`, never a silent pick.

**A bare "N \<securities\>" is a quantity, not a headcount.** *"100 option grants"*, nobody
named, no plural-**person** language → one recipient getting 100; only people-language
(*"100 employees"*, *"100 new hires"*) makes N a row count. Never open a form with 100 blank
rows, and never ask who the recipients are first — a missing recipient is an empty field on
the form, not a chat question.

## The artifact surface

**Budget: two turns to a form on screen** — `Bash`, then `Artifact` with
[the company check](#check-the-company-beside-the-publish) beside it, plus the environment
check only when [it applies](#the-connected-carta-must-be-the-intended-carta).

The page does the work: it resolves the company, the connector and the named people against
the cap table, fetches its own reference data, collects the terms, saves and validates the
draft set, shows the review, and issues on confirmation. None of it reaches your context —
not the roster, field manifest, or HTML. **You run one script and publish it.**

### 1. Preflight

- **The connected Carta must be the intended Carta** —
  [the hard stop above](#the-connected-carta-must-be-the-intended-carta) binds here.
- **Don't resolve the company before the build.** Pass the user's name to `--company-name`;
  the page resolves it.
- **Deferred `read_tool`: `ToolSearch` it in the same message as the `Bash`.** Unloaded,
  it's refused for a missing top-level `_instrumentation_v2`.
- **A spreadsheet or CSV goes through [the import sub-skill](issuance-import/SKILL.md)
  first;** its `rows` become the seed's. **An attached document is already in context:**
  seed from it, no `Read`.

Nothing else. **Don't fetch reference data, the roster, plans, valuations, share classes or
the field manifest** — the page fetches what it needs, so yours is a wasted round trip.

### 2. Build the page

One `Bash` call: `mkdir`, seed heredoc and build together, no `Write`. The seed is optional
and small: only what the *prompt* supplied, so the page can prefill it.

**Set `SKILL` to the absolute path
[§ Where everything else lives](#where-everything-else-lives) resolves for you — paste it.**
The plugin-root variable isn't exported into the Bash tool's shell, so a command that still
carries it unresolved fails on a path starting `/skills/` — nothing else in the command is
wrong when that happens.

```bash
SKILL=<paste the resolved absolute path>
WORK=<your scratchpad dir>
mkdir -p "$WORK"
cat > "$WORK/_seed.json" <<'JSON'
{"stakeholders": ["Tagg Palmer"], "quantity": "100"}
JSON
uv run "$SKILL/issuance-artifact/scripts/build_artifact.py" \
  --company-name "IMIM" \
  --security-type option_grant \
  --seed "$WORK/_seed.json" \
  --out "$WORK/issuance-imim-option_grant.html"
```

- **No `--corporation-id`.** The page resolves the name. Add `--corporation-id <n>` only
  when you already hold a bare numeric id — from the fallback lookup, or from a resume.
- `--seed` takes a **path**, never an inline blob. Omit it entirely when the prompt named
  nobody and no terms; the page then opens with one blank recipient row.
- Seed keys: `stakeholders` (names verbatim), `quantity`, `issue_date`, `terms`, `source`,
  and `rows` from the import sub-skill. An unknown key fails the build.
- **`terms` carries every term the document or prompt states**, in its words; the page
  matches names to Carta's lists and asks only for what matches nothing:
  `{"option_plan": "<plan name>", "grant_type": "<ISO|NSO|…>", "exercise_price": "<price>", "board_approval_date": "<YYYY-MM-DD>", "vesting": {"text": "<the schedule's words>", "months": <total>, "cliff_months": <cliff>}, "term_years": <years>}`.
  Also `vesting_start_date`, `grant_expiration_date`, `early_exercise`, `share_class`,
  `price_per_share`, `threshold_value`. Omit unstated ones.
- **Different quantities per person** go on each entry, never dropped:
  `{"stakeholders": [{"name": "Tagg Palmer", "quantity": 100}, {"name": "Emily Wilson", "quantity": 50}]}`.
  Top-level `quantity` covers everyone else. A percentage stays one: 2.75% is `"2.75"`.
- **Different terms per person** go on that entry's own `terms`, only `grant_type`,
  `exercise_price`, `price_per_share`, `threshold_value`, `board_approval_date`:
  `{"name": "Emil Vaselvee", "quantity": 1250, "terms": {"grant_type": "ISO"}}`. Rows
  that disagree with no batch value show `Multiple` on the shared field — expected.
- **`source` lists every attached file the terms came from**, spreadsheets included:
  `[{"name": "<file name>", "url": "<asset url>", "document_type": "<library type>"}]`.
  Omit it when nothing was attached. Once Carta holds the draft or the issue, the page adds
  each file to Company documents under `document_type`, the type the file is:
  `Option Agreement`, `Board Consent`, `Equity Plan`, `Restricted Stock Award`,
  `PIU Award Agreement`, `Equity Agreement`, `Warrant` — or `Document` when none fits. A
  wrong type fails the build, which names them all. `url` builds only as https or a
  `/_blob/` asset ([§ 3](#3-publish-it)), and only an asset is added.
- **Resuming a saved draft set** adds `draft_set_id` — without it the page mints a *second*
  draft set of the same rows ([hard rule 3](#hard-rules)). The page reads the set's rows and
  terms back itself; seed `load_drafts` rows, each with its `draft_pk`, only as its fallback
  ([resume-flow.md](references/resume-flow.md)). Never relay terms.
- **`--out` is a stable path for this company and type** — a lowercase company slug plus
  the type, as above.
- The script exits non-zero and names the problem on a bad id, a missing part, or an
  unresolved placeholder. Surface that verbatim and stop — a build fault, not a retry.

**Never read the built file back** — ~135KB kept out of context.

### 3. Publish it

```
Artifact({
  file_path: "<the --out path>",
  description: "Collect and review the option grants before issuing them.",
  icon: "grant",            // "certificate" | "grant" | "units", per security_type
  capabilities: {
    mcp: { servers: [{ server: "<the connector's display name — see below>", tools: ["call_tool"] }] },
    db: {}
  }
})
```

**`server` is the display name of
[the connected Carta](#the-connected-carta-must-be-the-intended-carta)** — never one that
only offers `authenticate`, never a name from this file. Take the first your MCP
instructions give:

1. **A heading naming it** — `## claude.ai <name>` → `<name>`, copied exactly.
2. **A `Default connector name: "…"` line** in the block headed by your **tool-name
   segment** — between `mcp__` and the next `__`, so
   `mcp__33b9b857-8443-4b2d-b191-2d9b6c50eb86__call_tool` → `33b9b857-8443-4b2d-b191-2d9b6c50eb86`.
3. **Neither:** send the segment itself, case included. The publish rejects it and names
   the connector — *"…is the id of connector "<name>" — set "server" to "<name>"."* That
   rejection **is** the lookup: republish with that name, nothing else changed. One retry,
   expected — **not a failure.**

Never guess a name from a readable prefix. The publish never checks it — right or wrong
reads the same — so if the user says the page can't see Carta after a step 2 name, their
connector was renamed: republish with step 3.

- **Omit `url`; skip `action: "list"`.** The same file path redeploys to the same URL.
- `icon` goes on the first publish only; omit on redeploy.
- Restate the **whole** `capabilities` object on any redeploy that passes it: a non-empty
  object replaces the stored grant, so an omitted capability is revoked. Omit the field
  entirely to carry the grant forward — the cheaper redeploy.
- Keep `tools` at that one: every Carta command goes through the `call_tool` proxy.
- **Every attached file goes up as an asset**, so the page can add it to Carta: add
  `assets: {}` to the first publish, then `upload_asset` each file, rebuild with each
  `source` entry's `url` set to its returned `url`, and republish with `capabilities` omitted.

**Read the publish result's warnings.** One matters: an unresolved connector name means
the grant isn't wired and every card comes up empty — that sends this run to
[the chat surface](references/chat-surface.md) instead. Any other warning is informational.

Say **one short line, then the URL on its own line as bare text**:

> The option-grant form is open — set the terms once, add recipients, and hit Review. It'll
> flag anything Carta needs before you can issue.
>
> https://claude.ai/code/artifact/58fa48f8-693e-43e3-8491-018976b769d6

**Never a markdown link.** On a host that opens the form in a side panel it renders as the
title alone — if that panel failed, the address is the one thing the user can't see or
copy. Bare URL every time, panel or not.

Echo nothing else — no ids, field names, or summary of what you prefilled
([hard rule 8](#hard-rules)). A hook's footer still goes last. The first open
asks the viewer to allow the Carta connection; until then the page shows its no-connection
state and says what to do.

#### Check the company beside the publish

Parallel with the `Artifact` call (skip it with `--corporation-id`): `read_tool`
`cap_table:get:resolve_company` `{"name": "<--company-name>"}`.

- `resolved` / `unavailable` → nothing. **No rebuild** — the page already resolves the name.
- `ambiguous` / `suggestions` → `AskUserQuestion` "Which company did you mean?", one option
  per `candidates` name (max 4; twins get `Carta ID <corporation_id>` as description).
  **Never pick one yourself**, even a lone suggestion.
- `not_found` → ask for the name as Carta shows it; re-check.

Only after the user answers one of those: rebuild with the same flags plus
`--corporation-id` (`--company-name` stays required) to the **same** `--out`, and republish
(no `icon`).

### 4. The page issues; you report

**This surface performs the irreversible write itself.** The page saves, validates, shows
the review, and asks for one confirmation in its own sheet — that click is the gate, and the
write goes out under the viewer's connector grant. A hand-off document can't wake this
session, so a page stopping at a saved draft set could never issue.

After the write the page seals itself — no further save or issue — whenever the outcome is
settled or unknowable: issued, a timeout, or accepted-and-nothing-reported. A refused value
and a duplicate stakeholder both leave it usable, since neither wrote anything.

So there's nothing to do after § 3. **End your turn on that one line.** Don't read the
store yet, poll, narrate, or re-publish: filling an issuance form is minutes of human work.

At the start of your **next** turn, whatever the user typed, read what the page
recorded:

```
Artifact({action: "read_db", url: "<the URL the publish returned>",
          db_op: "get", collection: "issuance", doc_id: "handoff"})
```

```json
{ "status": "issued", "issued": 1, "draft_set_id": 472, "security_type": "option_grant",
  "holders": ["Tagg Palmer"], "totals": {"USD": {"quantity": 100, "value": null}},
  "issue_date": "2026-09-21" }
```

Branch on `status`, never `summary`. `holders`, `totals` and `issue_date` are for the
closing line. `documents` gives each attached file's `status`: name any that isn't
`filed`, and tell the user to upload it in Company documents in Carta.

| `status` | What it means | What you do |
|---|---|---|
| *not found* | not finished. The normal state | Answer whatever they asked. Not an error, never reported as one |
| `issued` | **the page is done**. `pending_board` counts option grants held for a board consent: drafts, not issued | Close per [issue-and-close.md § On success](references/issue-and-close.md#on-success): signatories were notified to sign; once they sign, securities go to stakeholders. With `pending_board`, add that those grants await board approval and the page links to the consent in Carta. **Never call `issue_securities` or send a consent** — that issues twice |
| `draft` | saved, not validated, not issued | Say the draft is saved and they can return to it, and **stop**. A saved draft isn't an approval to issue |
| `needs_claude` | the page tried and couldn't finish | Read `reason`: `duplicates` → [mutate-recovery.md § Duplicate resolution](references/mutate-recovery.md#duplicate-resolution); `unknown_outcome` → **read the set's state before any write**, rows may already be issued; `partly_issued` → some rows are issued: read the set's state, never issue it again; `rows_removed` → the named `rows` were deleted from their document set and not saved back: have the user retry or re-save them; `nothing_issued` → [mutate-recovery.md](references/mutate-recovery.md) |

### When something is wrong

| What you see | What it means | What you do |
|---|---|---|
| Publish warns it couldn't resolve the connector | the page has no Carta access | [chat surface](references/chat-surface.md) |
| User says the page can't find the company | the name matched none or several | [Check the company](#check-the-company-beside-the-publish) |
| User says the page is empty, or every section couldn't load | the viewer hasn't allowed the connector, or Carta is down for them | Tell them to allow the Carta connection when asked, or reconnect Carta in Settings → Connectors. Re-publishing doesn't help — unless `server` came from step 2 of [§ 3](#3-publish-it) |
| User sees a hard stop in the page | the account isn't set up for this issuance | Read it back in plain language and stop. The fix is in Carta, not here |
| User reports validation errors they can't clear | the server refused a value | Those belong to the page, shown against its own fields. Only if the page can't act on a message — a fund-structure block, a duplicate stakeholder, a missing FMV — read [mutate-recovery.md](references/mutate-recovery.md) |
| User says it issued but no document appears | the store write failed after the write landed | **Don't issue.** The page names the draft set on screen; ask for it and read its state |
| User wants to change a term after confirming | — | Tell them to hit Back in the page's sheet and confirm again. Don't rebuild rows yourself — the draft set is the record |

### What not to do on this path

- **Don't ask who the recipients are, or anything the form collects** — a blank field on
  the form is the question.
- **Don't pre-ask for a computable value** — the page derives and shows it.
- **Don't stack an `AskUserQuestion` on the open page** for anything the page collects. It's
  unrestricted for a genuine fork the page can't present — two Carta environments, a mixed
  security type — and for recovery after a server rejection.
- **Don't build the payload yourself.** Reading
  [payload-reference.md](references/payload-reference.md) here means you're on the wrong
  path: that file is for the chat surface and recovery.

## Issue

The chat surface ends at a saved, validated `draft_set_id`, and **you** perform the
irreversible write so the host's own confirmation prompt fires. **The artifact surface
issues from the page instead** — its Confirm sheet is that gate, so you report the result
and never re-issue ([§ 4](#4-the-page-issues-you-report)).

```
mcp__carta__call_tool({"name": "cap_table__mutate__issue_securities", "arguments": {
  "corporation_id": <corporation_id>, "security_type": "<certificate|option_grant|piu>",
  "draft_set_id": <draft_set_id>}})
```

`call_tool` takes `name` + `arguments`; the wire name carries **double underscores** —
`cap_table:mutate:issue_securities` is the prose form, never the argument.

**No `drafts` key** — the draft set already holds the rows the user approved
([hard rule 6](#hard-rules)).

**Don't call `validate_drafts` again first.** The surface already validated, and
`issue_securities` re-validates server-side before writing.

Then read [references/issue-and-close.md](references/issue-and-close.md) for the response
branches, what to say per holder, and the closing lines. On a rejection a re-call can't
clear, read [references/mutate-recovery.md](references/mutate-recovery.md).

## Hard rules

Every surface.

1. **Never mix two security types in one mutate.** Run the skill once per type for a mixed
   request.
2. **One confirmation gate per mutate attempt** — never zero, never two stacked. The gate is
   the artifact's own Confirm sheet, or one `AskUserQuestion` on the chat surface — **never
   stacked on an open page** ([§ What not to do on this path](#what-not-to-do-on-this-path)).
   Recovery questions after a server short-circuit are unrestricted.
3. **Retry contract — reuse identity from the FIRST response.** Put `draft_set_id` from the
   first mutate on every later `issue_securities`, `save_drafts`, `load_drafts`,
   `validate_drafts`, `resolve_duplicate_stakeholder`: omit it and the server mints a
   *second* draft set of the same incomplete rows. Put each row's `draft_pk` from its first
   save on every retry, plus *every* required field: omit it and the row inserts instead of
   updating. **A timeout isn't an error** — the call may have already succeeded, so
   retrying with wrong params risks a duplicate set or double-issue; read
   [§ Timeouts & retries](references/payload-reference.md#timeouts--retries) first.
4. **The server is the source of truth.** Don't mirror its validation; surface its messages
   verbatim.
5. **Never delegate to a background agent.** The gates require interactive HITL.
6. **Issue what was validated, not a copy of it.** When the draft set already holds the rows
   the user approved, issue with `draft_set_id` and **no `drafts` key**. Re-sent rows are only
   *probably* identical to the reviewed ones — one transposed digit issues terms nobody
   approved, and no later gate compares the two. Resend rows only to change them, each with
   its `draft_pk` attached.
7. **Never substitute the certificate flow for a PIU.** Server-side a PIU *is* a certificate
   row with `type="PIU"`, so that path looks like a fallback when a PIU call is refused. It
   isn't — it issues a plain unit certificate with no threshold value, a different security.
8. **No raw ids or payload field names in customer-facing text — ever.** Not in headers,
   status lines, prompts, confirmations or errors. Never write "ID"
   (✅ *"looking up Jane"* / ❌ *"pulling stakeholder id 12345"*), and never render `(<number>)`
   after a name. Humanize payload keys before surfacing them, `banner_errors` included:
   `_` → space, Title Case, exceptions in [labels.md](references/labels.md).

Every rule here comes from a real run that went wrong ([incidents.md](references/incidents.md)).

## Where everything else lives

Every path below starts `${CLAUDE_PLUGIN_ROOT}/skills/carta-issuance/` — that variable
anchors at the **plugin** root, so the skill segment belongs in the path. **Don't search**
for them: on several hosts `Glob`/`find` can't reach the plugin mount and return empty.

- `references/chat-surface.md` — the last-resort path: its phases, and what it reads.
- `references/issue-and-close.md` — the mutate's response branches and the closing lines.
- `references/mutate-recovery.md` — on a server rejection a re-call can't clear.
- `references/resume-flow.md` — *"resume draft set 472"*.
- `references/incidents.md` — before weakening or arguing with any rule.
- `issuance-import/SKILL.md` — the prompt points at a spreadsheet, CSV or award document. On
  every surface, before any form opens ([§ 1](#1-preflight) has the one exception). It owns
  parsing; **never hand-read a workbook**.

Files in this skill

  • PROVENANCE.json208 B
  • SKILL.md28 KB
  • UPSTREAM-LICENSE.txt11 KB
  • issuance-artifact/resources/issuance.boot.js15.2 KB
  • issuance-artifact/resources/issuance.css36.9 KB
  • issuance-artifact/resources/issuance.preview.css2.1 KB
  • issuance-artifact/resources/issuance.preview.js15.9 KB
  • issuance-artifact/resources/issuance.template.html4.2 KB
  • issuance-artifact/resources/issuance.transport.js18.8 KB
  • issuance-artifact/scripts/build_artifact.py22.4 KB
  • issuance-import/SKILL.md14.6 KB
  • issuance-import/references/column-map.md11.5 KB
  • issuance-import/scripts/parse_upload.py45.3 KB
  • references/certificate-fields.md7.1 KB
  • references/chat-review.md7.5 KB
  • references/chat-surface.md10 KB
  • references/engine.md49.9 KB
  • references/incidents.md30.2 KB
  • references/issue-and-close.md12.8 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…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.