skills/canonical-markdown-authoring/SKILL.md
Convert plain markdown contract drafts into OpenAgreements' canonical template.md authoring format — YAML frontmatter, Kind|Label|Value|Show When cover-term tables, oa:clause directives, [[Defined Term]] paragraphs, and oa:signer directives that compile to validated JSON specs and DOCX artifacts. Use when the user says "convert this to canonical markdown," "author a new OpenAgreements template," "migrate template to template.md," or "write a canonical-form contract."
npx skillsauth add open-agreements/open-agreements canonical-markdown-authoringInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Convert plain markdown contract drafts into OpenAgreements' canonical template.md
authoring format. The canonical form is a single lawyer-editable file that compiles
to a validated JSON contract spec plus a rendered DOCX via the shared template
renderer.
Use this skill when the user wants to:
template.mdnpm run generate:templatesoa:clause directives, or signer metadataThis skill assumes:
content/templates/<template-id>/template.md.scripts/template_renderer/canonical-source.mjs)
and the cover-standard-signature-v1 layout are present.A canonical template.md has these parts in this order:
# Title — H1 matching document.title in frontmatter.## Cover Terms — short subtitle paragraph followed by a
Kind | Label | Value | Show When table with {snake_case} field
placeholders.recitals section — <!-- oa:section type=recitals -->
followed by an H2 such as ## Recitals and one or more recital paragraphs.<!-- oa:section type=standard_terms --> followed
by an author-chosen H2 such as ## Standard Terms, ## Resolutions, or
## Terms. Clauses live here, each preceded by <!-- oa:clause id=... -->.
The first clause should be Defined Terms with type=definitions when a
definitions clause is needed.<!-- oa:section type=signature --> followed by the
signature H2 and then <!-- oa:signature-mode arrangement=... --> plus the
signer blocks.---
template_id: openagreements-<slug> # kebab-case, must match the directory slug
layout_id: cover-standard-signature-v1
style_id: openagreements-default-v1
outputs:
docx: content/templates/openagreements-<slug>/template.docx
document:
title: <Document title> # MUST match the H1 below
label: <Catalog label, e.g. "OpenAgreements XYZ">
version: "1.0"
license: Free to use under CC BY 4.0
include_cloud_doc_line: true
defined_term_highlight_mode: definition_site_only # see "Highlight modes"
cover_row_height: 700
sections:
cover_terms:
section_label: Cover Terms
heading_title: Cover Terms
standard_terms:
section_label: Standard Terms
heading_title: Standard Terms
signature:
section_label: Signature Page
heading_title: Signatures
---
Notes:
source_json. npm run generate:templates
auto-discovers any content/templates/<slug>/template.md whose frontmatter
declares template_id, layout_id, and style_id, then writes the
generated JSON to content/templates/<slug>/.template.generated.json
(a hidden, do-not-edit-by-hand artifact).output_markdown_path or outputs.markdown — the canonical
compiler rejects them. The canonical template.md is the source.oa:section is the
rendered heading source of truth. Keep the frontmatter sections metadata in
place, but treat the body heading as authoritative for the visible section
title.# <Document title>
Keep the H1 text identical to document.title. There is no automatic
cross-validation today, but drift between the two is a known footgun.
If the rendered title or section heading differs from the raw markdown, see "Sharp edges and renderer transforms" below for the body-vs.-frontmatter source-of-truth map.
Open with one short subtitle paragraph, then the Kind | Label | Value | Show When
table:
## Cover Terms
The terms below are incorporated into and form part of this agreement.
| Kind | Label | Value | Show When |
| --- | --- | --- | --- |
| row | Company | {company_name} | always |
| row | Employee | {employee_name} | always |
| row | Effective Date | {effective_date} | always |
| group | Confidentiality | | always |
| subrow | Trade Secrets Duration | {trade_secret_duration} | always |
| subrow | Other Confidential Information Duration | {other_confidentiality_duration} | confidentiality_other_included |
Rules:
row, group, or subrow.
row — single-line cover term.group — section header with no value (leave the Value column blank).subrow — child of the most recent group; rendered indented.{snake_case} field placeholder. Field
names must match ^[a-z_][a-z0-9_]*$.always — always rendered.<field_name> — rendered only when the boolean field is truthy. The
field's name must satisfy ^[a-z_][a-z0-9_]*$.Every operative section starts with an oa:section directive. The H2 that
follows may be author-chosen. Every clause inside the operative section then
gets an oa:clause directive, a ### Heading, and prose paragraphs:
<!-- oa:section type=standard_terms -->
## Standard Terms
<!-- oa:clause id=assignment-of-inventions -->
### Assignment of Inventions
Employee hereby assigns and agrees to assign to Company all right, title, and
interest in Covered Inventions, to the extent permitted by law.
<!-- oa:clause id=non-competition when=noncompete_included omitted="[Intentionally Omitted.]" -->
### Non-Competition
During the Restricted Period, Employee must not engage in any Competitive Business
within the Restricted Territory.
If the document needs standalone WHEREAS-style recitals, author them in a
separate recitals section before the operative section:
<!-- oa:section type=recitals -->
## Recitals
**WHEREAS**, Company is considering a financing transaction.
**WHEREAS**, the board has reviewed the proposed terms.
<!-- oa:section type=standard_terms -->
## Resolutions
oa:section types:
standard_terms — required operative clause sectionsignature — required signature sectionrecitals — optional recital-only sectionDirective attributes:
id — slug, kebab-case, unique within the document. Used for stable
machine references.when=<field_name> (optional) — clause is only included when the named
boolean field is truthy.omitted="<text>" (optional) — text rendered in place of the body when
when evaluates false.If a clause transfers inventions, work product, patent rights, copyrights, or
other IP ownership, use a present-tense operative grant such as hereby assigns.
Employee hereby assigns to Company all right, title, and interest in ...... and will sign additional documents reasonably requested to confirm ownership.agrees to assign or will assign without a present-tense
grant. Federal Circuit contract law applied in the Stanford/Roche line of
cases (FilmTec, DDB Techs, Rasmussen Instruments, the Federal Circuit
decision in Stanford v. Roche, 583 F.3d 832) treats future-tense language
as a promise to assign rather than a present transfer of rights — which
diligence reviewers flag as a gap.hereby irrevocably assigns) is optional belt-and-
suspenders; "hereby assigns" is the doctrinal minimum.Use a Defined Terms clause as the first clause in the
oa:section type=standard_terms operative section when a capitalized concept
needs an explicit anchor or when a longer definition materially shortens later
operative clauses.
Before adding a definition, ask:
Do not add pointer-only redefinitions for cover-page-only labels such as
Company, Employee, Effective Date, Governing Law, or Venue. Those
add a second lookup site without improving readability.
If a Cover Terms value contains the real substance of a defined concept, keep
all substantive content there. Use the Defined Terms clause only as a short
anchor when repeated capitalized use improves readability. Do not split one
definition across two sites by writing
[[X]] has the meaning given in Cover Terms ... and includes ....
If you remove a formal definition, do not leave behind an unexplained
capitalized pseudo-term. Either use the exact Cover Terms label in prose
(The Prior Inventions and Excluded Inventions identified in Cover Terms)
or rewrite descriptively in lower-case prose.
<!-- oa:clause id=defined-terms type=definitions -->
### Defined Terms
[[Covered Inventions]] means inventions, software, works of authorship,
discoveries, designs, data models, and related intellectual property created
during employment that arise from Company work, use Company resources, or
relate to Company actual or anticipated business.
[[Confidential Information]] means non-public information that Employee
learns, accesses, or develops during employment, including business strategies,
customer and prospect data, trade secrets, technical and product information,
financial information, personnel information, and any additional information
described in Cover Terms under Confidential Information Definition.
Confidential Information does not include information that (a) was publicly
known when Employee learned it, (b) becomes publicly known through no fault
of Employee, (c) was lawfully known to Employee before employment without
confidentiality restriction, or (d) Employee independently develops outside
the scope of employment without using Confidential Information.
Definition rules:
[[...]] span — that span declares the canonical term.A, An, The) before the first [[...]] is allowed
and is stripped from the canonical term.(Aliases: [[Alias 1]], [[Alias 2]]). Aliases never render in the legal
output.Anywhere outside the definitions clause, [[Term]] is an explicit reference and
must resolve to a canonical term, an alias, or a clause ID via
[[clause:<id>]]. Plain-text mentions are valid — you do not need to wrap
every "Confidential Information" in brackets. Use [[...]] only when you want
explicit linking semantics.
<!-- oa:section type=signature -->
## Signatures
<!-- oa:signature-mode arrangement=entity-plus-individual -->
By signing this agreement, each party acknowledges and agrees to the obligations
above.
<!-- oa:signer id=company kind=entity capacity=through_representative label="Company" -->
**Company**
Signature: _______________
Print Name: {company_name}
Title: _______________
Date: _______________
<!-- oa:signer id=employee kind=individual capacity=personal label="Employee" -->
**Employee**
Signature: _______________
Print Name: {employee_name}
Title: _______________
Date: _______________
Strict rules (the cover-standard-signature-v1 layout enforces these):
signature, print-name, title, date) in the same
order. The renderer unions row IDs in declaration order.label and hint must match. Mismatches are rejected with a clear
error.arrangement is entity-plus-individual or stacked. Defaults to stacked.kind is entity, individual, or acknowledging-individual.capacity is through_representative, personal, or acknowledging.A few parts of canonical template.md are structural-only in source or
normalized by the renderer. If the artifact looks different from the raw
markdown, check these first:
# ... line readable for humans
navigating the raw .md, but the rendered title comes from frontmatter
document.title. If they drift, the artifact follows frontmatter.<!-- oa:section --> becomes the in-document section
heading. Post-#285, that H2 is the visible section title in the body, so
choose names that read well in the output. Example:
<!-- oa:section type=standard_terms --> + ## Resolutions renders
Resolutions as the in-document heading; the same directive +
## Standard Terms renders Standard Terms. Note that on
cover-standard-signature-v1, the running page header is separately
driven by frontmatter sections.<name>.section_label — if you rename the
body H2 but leave section_label alone, the page header will not change.[Signature Page Follows] is renderer-inserted. Authors do not write
that string in canonical source. The traditional-consent-v1 layout always
inserts [Signature Page Follows] between the operative section and the
signature section, so seeing it in output but not source is expected.$
auto-prefixed. In a repeat-backed signature section, write
{stockholder.name} in canonical source. The parser rewrites it to
{$stockholder.name} in generated artifacts, so do not "fix" the $
you see there. This rewrite is scoped to signer row values; it does not
fire for other loop contexts.Use this quick source-of-truth map when deciding where authored text will show up:
# <H1> -> anchor-only for raw .md; not rendered in the artifact## <heading> immediately after <!-- oa:section type=... --> ->
rendered as the in-document section heading### <clause heading> plus following paragraphs -> renderedsections.<name>.section_label -> running page header in
cover-standard-signature-v1 (independent of the body H2)<!-- oa:section type=recitals --> ->
rendered in the optional recital section (currently only traditional-consent-v1
emits a recital section; cover-standard-signature-v1 ignores it)document.title -> rendered as the document titledocument.opening_recital -> rendered before the operative
section by traditional-consent-v1; not consumed by
cover-standard-signature-v1document.label and document.version -> rendered in the
layout footer; exact placement varies by layoutdocument.license -> consumed in the Markdown export, but
both current DOCX layouts hardcode the footer license string as
"Free to use under CC BY 4.0." rather than reading frontmatter. All
first-party templates currently use that license so the output happens
to match, but don't expect a non-CC-BY-4.0 license to flow through to
the DOCX footer today.npm run generate:templates
The canonical compiler:
template.md → normalized model.content/templates/<slug>/.template.generated.json.outputs.docx.After generation:
git diff content/templates/<slug>/.template.generated.json — should be empty
if your source matches the previously committed JSON.npx vitest run integration-tests/canonical-source-sync.test.ts — proves the
canonical → JSON projection is in sync.npx vitest run integration-tests/canonical-source-authoring.test.ts integration-tests/template-renderer-json-spec.test.ts packages/contract-templates-mcp/tests/tools.test.ts — broader coverage.document.defined_term_highlight_mode controls how defined terms are visually
highlighted in the rendered DOCX:
| Mode | Behavior | When to use |
| --- | --- | --- |
| definition_site_only | Highlights only at the definition paragraph; brackets are stripped from prose. | Default for templates with a Defined Terms clause. Avoids "too much blue" in body text. |
| all_instances | Highlights every occurrence of any term in style.defined_terms. | Templates without a Defined Terms clause that still want emphasis on a small fixed list (e.g. Company, Employee). |
| none | No highlighting. | Plain-text outputs. |
If you add a Defined Terms clause, switch to definition_site_only. The
template-wide style.defined_terms list still applies as a fallback for
all_instances mode.
Working canonical templates already in the repo:
content/templates/openagreements-restrictive-covenant-wyoming/template.md —
feature-rich: substantive defined terms paired with narrow cover-term anchors
for operative concepts, group/subrow cover terms, and conditional clauses.content/templates/openagreements-employee-ip-inventions-assignment/template.md —
simpler: 2 substantive defined terms (Covered Inventions, Confidential Information), present-tense IP assignment language, and all-row cover terms.When migrating a JSON-sourced template, copy the closer of these two as a shape reference and adapt.
| Pitfall | Symptom | Fix |
| --- | --- | --- |
| Edited template.md but forgot to regenerate | canonical-source-sync.test.ts fails | Run npm run generate:templates and commit the regenerated .template.generated.json diff. |
| H1 doesn't match document.title | Silent drift; renders the frontmatter title | Keep them in sync manually until cross-validation lands. |
| output_markdown_path left in frontmatter | Compiler throws | Remove outputs.markdown and any top-level output_markdown_path. |
| source_json left in frontmatter | Quietly ignored today; will become an error | Remove source_json — JSON paths are now derived from the slug. |
| Same-id signer rows with different labels | Layout throws "mismatched labels" error | Make both signers' row labels identical, or rename one row's ID. |
| Three signers | cover-standard-signature-v1 rejects at render | Reduce to two signers, or add a new layout that supports N signers. |
| Field placeholder uses camelCase | Validation rejects | Use snake_case matching ^[a-z_][a-z0-9_]*$. |
| Definitions paragraph without a [[...]] declaration | Compiler throws | Every paragraph in a definitions clause must declare a canonical term. |
| Alias collides with a canonical term or another alias | Compiler throws | Choose a unique alias. |
| Adding [[...]] everywhere in prose | "Too much blue" in DOCX | Reserve [[...]] for definition sites and explicit references; let plain prose stay plain. |
| Pointer-only redefinition of a cover-page-only term | Second lookup site with no readability gain | Omit the defined-term entry; use the Cover Terms label directly in prose. |
| One term split across Cover Terms and Defined Terms | Reader must combine two sources for one definition | Put all substantive content in one place — usually a substantive body definition with optional Cover Terms supplementation. |
| Capitalized pseudo-term with no exact anchor | Reader cannot tell where the term is defined | Keep an exact anchor (identified in Cover Terms) or rewrite in descriptive lower-case prose. |
| Bare agrees to assign in an IP assignment clause | Diligence flag — not a present transfer of rights | Use hereby assigns and agrees to assign (see Step 4A). |
---
template_id: openagreements-example-nda
layout_id: cover-standard-signature-v1
style_id: openagreements-default-v1
outputs:
docx: content/templates/openagreements-example-nda/template.docx
document:
title: Example Mutual NDA
label: OpenAgreements Example Mutual NDA
version: "1.0"
license: Free to use under CC BY 4.0
include_cloud_doc_line: true
defined_term_highlight_mode: definition_site_only
cover_row_height: 600
sections:
cover_terms:
section_label: Cover Terms
heading_title: Cover Terms
standard_terms:
section_label: Standard Terms
heading_title: Standard Terms
signature:
section_label: Signature Page
heading_title: Signatures
---
# Example Mutual NDA
## Cover Terms
The terms below are incorporated into and form part of this agreement.
| Kind | Label | Value | Show When |
| --- | --- | --- | --- |
| row | Disclosing Party | {disclosing_party_name} | always |
| row | Receiving Party | {receiving_party_name} | always |
| row | Effective Date | {effective_date} | always |
| row | Confidentiality Term | {confidentiality_term} | always |
<!-- oa:section type=standard_terms -->
## Standard Terms
<!-- oa:clause id=defined-terms type=definitions -->
### Defined Terms
[[Confidential Information]] means non-public information disclosed by a party
that is identified as confidential at the time of disclosure or that a
reasonable person would understand to be confidential given the nature of the
information and the circumstances of disclosure.
<!-- oa:clause id=use-restrictions -->
### Use Restrictions
The Receiving Party identified in Cover Terms will use Confidential Information
solely to evaluate the business relationship contemplated between the parties
and will not disclose Confidential Information to third parties without the
Disclosing Party's written consent.
<!-- oa:clause id=term-and-survival -->
### Term and Survival
Each party's confidentiality obligations apply for the Confidentiality Term
listed in Cover Terms, beginning on the Effective Date. Trade-secret
obligations survive indefinitely.
<!-- oa:section type=signature -->
## Signatures
<!-- oa:signature-mode arrangement=stacked -->
By signing this agreement, each party acknowledges and agrees to the obligations above.
<!-- oa:signer id=disclosing kind=entity capacity=through_representative label="Disclosing Party" -->
**Disclosing Party**
Signature: _______________
Print Name: {disclosing_party_name}
Title: _______________
Date: _______________
<!-- oa:signer id=receiving kind=entity capacity=through_representative label="Receiving Party" -->
**Receiving Party**
Signature: _______________
Print Name: {receiving_party_name}
Title: _______________
Date: _______________
template.md form
and be picked up automatically by npm run generate:templates.For setup of the OpenAgreements repo / CLI, see CONNECTORS.md.
testing
Explain U.S. state-by-state consumer data-privacy law (CCPA/CPRA, TDPSA, VCDPA, CPA, and the other comprehensive state acts) — who a law covers, applicability thresholds, privacy-policy requirements, consumer rights and opt-outs, private rights of action, and who enforces. Reads a bundled, source-cited snapshot per state. Use when the user says "CCPA," "CPRA," "state privacy law," "privacy policy," "data subject request," "consumer rights request," "opt-out of sale," "data broker," "sensitive data," asks "do I need to comply with <state>'s privacy law," or names a U.S. state together with privacy.
development
Explain U.S. state-by-state (and select international) non-compete and restrictive-covenant law — whether a non-compete is enforceable, blue-pencil reformation, tolling, choice of law, independent-contractor reach, and recent bans. Reads a bundled, source-cited snapshot per jurisdiction. Use when the user says "non-compete," "noncompete contract," "restrictive covenant," "non-solicit," "garden leave," "covenant not to compete," "employment agreement," asks "is my non-compete enforceable," or names a U.S. state.
testing
Draft and fill NVCA model documents — stock purchase agreement, certificate of incorporation, investors rights agreement, voting agreement, ROFR, co-sale, indemnification, management rights letter. Series A and venture financing templates. Produces signable DOCX files. Use when user says "Series A documents," "NVCA," "stock purchase agreement," "investors rights agreement," "voting agreement," or "venture financing docs."
development
Assess SOC 2 Type II readiness. Map Trust Services Criteria to controls, identify gaps, and build a remediation plan. Uses NIST SP 800-53 (public domain) as canonical reference with SOC 2 criterion cross-mapping. Use when user says "SOC 2 readiness," "SOC 2 preparation," "SOC 2 gap analysis," or "prepare for SOC 2 audit."