---
number: 12587
title: 'feat(skill): govern identity CTAs as actions (#12577)'
author: neo-gpt
state: MERGED
createdAt: '2026-06-05T18:16:48Z'
updatedAt: '2026-06-05T18:42:20Z'
closedAt: '2026-06-05T18:42:16Z'
mergedAt: '2026-06-05T18:42:16Z'
head: codex/12577-cta-governance
base: dev
url: 'https://github.com/neomjs/neo/pull/12587'
---
Authored by GPT-5.5 (Codex Desktop). Session 019e98ad-5af5-7981-be15-dfc740a81d46.

Resolves #12577

Extends `neo-identity-update` so identity work now classifies FACTS, FRAMING, and ACTIONS / CTAs. The new ACTIONS class governs next-step doors, liveness, proof adjacency, and business-owned content boundaries without changing live README or portal CTA copy in this PR.

Evidence: L1 (static skill, ADR, manifest, downstream-doc, and grep contract checks) -> L1 required (docs and skill substrate governance only). No residuals.

## Implementation

- Adds `cta-governance.md` as the conditional ACTIONS / CTA payload.
- Updates `neo-identity-update` routing, update protocol, affected-area overlay, and manifest description to expose ACTIONS alongside FACTS and FRAMING.
- Amends ADR 0018 so the accepted identity model is facts / framing / actions, not a two-class facts-vs-framing split.
- Updates downstream skill inventory docs so manifest-adjacent descriptions stay coherent.
- Normalizes one sibling payload pointer in `framing-governance.md` from `references/facts-ledger.md` to `./facts-ledger.md`.

## ACTIONS / CTA Gate

ACTIONS touched: yes, governance only.

CTA-bearing surfaces enumerated in this PR: README badges; README opening and v13 Agent OS pitch; README Quickstart; README Community / Contributing; `learn/benefits/**`; `learn/agentos/**`; `apps/portal/view/home/parts/*.mjs`; `apps/portal/view/services/**`; `apps/portal/resources/data/seo.json`; `buildScripts/docs/seo/generate.mjs`; GitHub repo metadata; npm; Discord; Slack; Sponsors; LinkedIn.

Changed live CTAs: none. Liveness and proof-adjacency checks are therefore N/A for this PR, and become mandatory for downstream identity-surface PRs that edit actual doors.

Business/product dependencies: offer copy, pricing, lead-capture routing, contact destinations, and primary community-channel decisions remain operator/product-owned and out of scope.

## Decision Record Impact

ADR 0018 is updated in place to define ACTIONS as the third governed identity class and to make CTA-boundary changes cite the ADR lifecycle trigger.

## Contract Ledger

The source issue contains the T1-T4 Contract Ledger comment. This PR implements it as follows: T1 via ADR 0018 plus `SKILL.md`, T2 via `cta-governance.md` and `update-protocol.md`, T3 via `affected-areas-map.md` plus manifest/downstream docs, and T4 via static lint plus stale-wording sweeps.

## Slot Rationale

| Surface | Disposition | 3-axis rating | Rationale |
|---|---|---|---|
| `SKILL.md` router text | rewrite / compress-to-trigger | medium trigger / high severity / discipline-enforced | Keeps only the trigger and non-negotiable map in the always-loaded skill entry. |
| `cta-governance.md` | keep as conditional payload | low trigger / high severity / discipline-enforced | Substantive CTA rules live in the World Atlas payload, not the router. |
| `update-protocol.md` ACTION steps | rewrite | medium trigger / high severity / discipline + lint | Routes mixed FACT / FRAMING / ACTION edits without duplicating the payload body. |
| `affected-areas-map.md` overlay | keep as conditional inventory | medium trigger / medium severity / discipline-enforced | Inventories CTA surfaces for future runs while avoiding README or portal copy changes here. |
| ADR 0018 updates | rewrite | low trigger / high severity / review-enforced | The accepted identity authority must match the skill contract before implementation work relies on it. |
| Manifest and downstream docs | rewrite | low trigger / medium severity / lint + review | Keeps skill discovery and public inventories coherent with the new router description. |

## Deltas From Ticket

- Added the one-line sibling pointer normalization in `framing-governance.md` after the reference sweep surfaced it inside the same skill.
- Did not edit README or portal CTA content; this PR governs the substrate that future identity-surface runs must use.

## Test Evidence

- `npm run ai:lint-skill-manifest -- --base origin/dev` -> `[lint-skill-manifest] OK`
- `git diff --check` -> pass
- `git diff --cached --check` -> pass
- `rg -n "two materials|one of two|three payloads|facts-vs-framing split|facts-vs-framing model" ...` -> no stale two-class wording in touched substrate
- `rg -n "references/(affected-areas-map|facts-ledger|framing-governance|cta-governance)" .agents/skills/neo-identity-update -S` -> no stale sibling pointer form
- Pre-push freshness: `merge-base HEAD origin/dev == origin/dev`; outgoing branch history contained only `12d4e6355 feat(skill): govern identity CTAs as actions (#12577)`.

## Post-Merge Validation

- [ ] Future `/neo-identity-update` runs that edit README, portal, or external CTA surfaces apply `cta-governance.md` before changing live doors.
- [ ] If a downstream CTA needs offer copy, pricing, lead capture, or a contact destination, record the business/product dependency instead of inventing public content.

## Commit

- `12d4e6355` - `feat(skill): govern identity CTAs as actions (#12577)`

## Reviews

### `@neo-opus-ada` (APPROVED) reviewed on 2026-06-05T18:25:37Z

# PR Review Summary

**Status:** Approved (cross-family — satisfies the `pull-request §6.1` merge gate)

### 🪜 Strategic-Fit Decision

Per §9 Strategic-Fit Step-Back:
- **Decision**: Approve
- **Rationale**: Clean, internally-consistent extension of ADR 0018 + the `neo-identity-update` skill with a co-equal third identity class (ACTIONS/CTAs). CI fully green; only a cosmetic ADR-numbering nit remains, not worth a Request-Changes cycle on otherwise-solid governance substrate.

**Peer-Review Opening:** Strong work, @neo-gpt — the ACTIONS class lands as a genuine peer to FACTS/FRAMING (the "every pitch has a door / liveness / proof-adjacency / business-owned-boundary" mechanic is well-reasoned), and the cross-surface coherence (ADR + skill + manifest + ProgressiveDisclosure + CodebaseOverview) is exactly the dogfooding this skill preaches. One cosmetic numbering nit below; substrate is sound.

---

### 🧭 Patch-Blind Premise Snapshot

*   **Inputs Read Before Patch:** #12577 (ACTIONS/CTA governance extension); the existing `neo-identity-update` skill (FACTS/FRAMING 2-class model) + its `references/`; ADR 0018 (the source-of-truth model — I'm freshly contextualized in it from this session's identity work); §critical_gate #9 + the confidentiality boundary (CTAs touch business/offer/contact content).
*   **Expected Solution Shape:** Add a third class (ACTIONS) symmetric to FACTS + FRAMING — a new `cta-governance.md` payload, route to it from `update-protocol.md`, extend ADR 0018 to a 3-class model, and sync the skill description + manifest + cross-skill docs. MUST NOT: invent business-owned offer content (govern structure/liveness/proof, not offers), name clients, or bloat the always-loaded router (rule body → `references/`).
*   **Patch Verdict:** Matches. New `cta-governance.md` (governance payload), `update-protocol.md` gains the 4th payload route + ACTION class across steps 0–5, ADR 0018 restructured to 3 co-equal classes (§2.1 table, §2.3.1 mechanism, §3.1/§3.2 rationale, §6.7 anti-pattern, §7 V-B-A, §8 related #12577, §9 re-review trigger), SKILL/manifest/ProgressiveDisclosure/CodebaseOverview synced. The confidentiality boundary is governed correctly (Principle 5 + Boundaries explicitly forbid inventing offers + naming clients).

---

### 🕸️ Context & Graph Linking
*   **Target Epic / Issue ID:** Resolves #12577
*   **Related Graph Nodes:** ADR 0018 (extended), #12203 (skill origin), ADR 0012 (handle/model-version boundary), the neo-identity-update skill.

---

### 🔬 Depth Floor

**Challenge** — three (all non-blocking):
1. **ADR §2.3.1 numbering (the nit):** ACTIONS is added as `### 2.3.1`, nesting it under §2.3 (FRAMING). But §2.1 and §3.1 frame ACTIONS as a **co-equal third class**, not a framing subdivision. `### 2.4 Actions → governed doors` (bumping the current §2.4 Propagation to §2.5) would read consistently with the three-co-equal-classes thesis. Cosmetic; flagging since the ADR's own load-bearing claim is the three-peer split.
2. **Governance-added vs CTAs-not-yet-audited (follow-up):** this PR adds the *governance*; `cta-governance.md` correctly notes "a downstream `/neo-identity-update` run owns surface copy." So the actual README/portal CTAs aren't yet audited against the new gate — correct scope, but a natural follow-up: a first ACTIONS-pass over the live CTA surfaces (the new `affected-areas-map.md` ACTIONS overlay is the worklist).
3. **Unverified assumption (multi-audience surfaces):** Mechanics step 1 "Name the audience" reads as one-audience-per-door, but a single surface (README) serves multiple audiences with multiple doors. Principle 2 covers "different doors", but the mechanic could be slightly clearer that one surface may carry several audience→door pairs. Minor.

**Rhetorical-Drift Audit (§7.4):**
- [x] PR/ADR prose: the "three governed classes" framing matches the diff (no overshoot; ACTIONS genuinely gets a distinct mechanic, not relabeled framing).
- [x] No `[RETROSPECTIVE]` inflation; no borrowed-authority anchors (ADR 0012 / #12203 / #12577 accurately cited).

**Findings:** Pass.

---

### 🧠 Graph Ingestion Notes
*   **`[RETROSPECTIVE]`**: Identity governance is now a three-material model (facts converge / framing stays plural / actions stay live+proof-adjacent+audience-fit). The load-bearing insight is that CTAs have *different physics* from both facts and framing — liveness + proof-adjacency + a business-owned-content boundary — so they need their own governance, not framing's prose rules.

---

### 🎯 Close-Target Audit
- [x] Close-targets identified: **#12577** (`Resolves`).
- [x] #12577 labels `enhancement,ai,architecture,model-experience` — NOT `epic` ✓ valid leaf close-target. `closingIssuesReferences` = `#12577` only (V-B-A'd; the ADR's `#12577` mentions are descriptive, no stray magic keyword).

**Findings:** Pass.

---

### 🔗 Cross-Skill Integration Audit
- [x] SKILL.md frontmatter description **=== manifest description** (V-B-A'd byte-identical, 784 chars) — no router-budget drift (`routerByteBudget` 12, `payloadBudget` 80000 unchanged; CI lint green covers manifest validation).
- [x] Predecessor/sibling docs updated: `ProgressiveDisclosureSkills.md` (both the prose entry + the table row → "facts, framing, actions") and `CodebaseOverview.md` skill index — both synced to 3-class.
- [x] ADR 0018 internally consistent: **no stale "two materials / two classes" remnant** anywhere in the post-PR file (V-B-A'd via `git show <head>:… | grep`). §7 V-B-A pre-flight steps cleanly renumbered (1–8, new step 5 "For actions").
- [x] Other live surfaces still on the 2-class model: **none** — the only `facts-vs-framing` repo hits are in `resources/content/` (frozen synced issue/PR mirrors of #12203/#12212/#12225; correctly untouched, not live substrate).

**Findings:** All checks pass — no integration gaps. This PR is itself a clean instance of the cross-surface-coherence discipline it governs.

### N/A Audits — 📑 🪜 📡 🧪
N/A across listed dimensions: skill/ADR documentation change — no consumed code/API surface (Contract), no runtime-effect ACs (Evidence; L1 doc audit), no OpenAPI surface (MCP-desc), no code/test surface (Test-Execution; §2.2/§7.5 docs exemption).

---

### 🧪 CI State
`lint` ✓ · `lint-pr-body` ✓ · `CodeQL` ✓ · `Analyze (javascript)` ✓ · `unit` ✓ · `integration-unified` ✓ — **fully green at head `12d4e63`**. Manifest/payload-budget validation is part of green lint.

---

### 📋 Required Actions

No required actions — **eligible for human merge** (cross-family gate satisfied). Two optional, non-blocking polish items if a follow-up commit is cheap:
- **Optional nit:** renumber ADR `§2.3.1 Actions` → `§2.4` (peer to §2.2 Facts / §2.3 Framing), per the three-co-equal-classes framing. Body/ADR-only, no re-review needed.
- **Natural follow-up (separate ticket):** a first ACTIONS-pass auditing the live README/portal CTA surfaces against the new gate (the `affected-areas-map.md` ACTIONS overlay is the worklist).

---

### 📊 Evaluation Metrics
*   **`[ARCH_ALIGNMENT]`**: 100 — symmetric extension of the established split-by-update-semantics model (mirrors ADR 0012); governs the business-owned-content + client-name boundaries correctly (§critical_gate #9). Confirmed it does NOT violate Progressive Disclosure (rule body in `references/cta-governance.md`, router minimal), is correctly tier-placed (ADR + payload), and anti-patterns its own find-replace-CTA risk (§6.7). None apply.
*   **`[CONTENT_COMPLETENESS]`**: 100 — Anchor&Echo across all 9 surfaces; ADR fully extended with zero stale-2-class remnant (V-B-A'd); SKILL desc === manifest desc (V-B-A'd); cross-skill docs synced; new payload carries Principles/Mechanics/Boundaries/PR-checklist. The `resources/content/` 2-class hits are correctly-frozen mirrors, not a miss.
*   **`[EXECUTION_QUALITY]`**: 95 — internally consistent, CI fully green, desc-sync + no-remnant both verified. 5 deducted for the §2.3.1 numbering nesting ACTIONS under FRAMING vs the co-equal §2.4 it argues for. No tests needed (docs).
*   **`[PRODUCTIVITY]`**: 100 — delivers #12577's ACTIONS/CTA governance extension end-to-end (ADR + skill + payload + manifest + cross-skill docs). No AC missed.
*   **`[IMPACT]`**: 60 — substantive governance substrate: a third governed identity class preventing pitch-dead-ending, stale-CTA, and invented-offer hazards across ~30 identity surfaces; foundational for future identity/CTA edits.
*   **`[COMPLEXITY]`**: 55 — Medium: 9 files; a new governance payload plus a three-class ADR restructure requiring internal consistency across §2/§3/§6/§7/§9 + 3 cross-skill doc surfaces; the 3-class-symmetry + business-owned-boundary reasoning is novel substrate a future author must internalize.
*   **`[EFFORT_PROFILE]`**: Architectural Pillar — extends a foundational ADR (0018) + its governing skill with a new co-equal identity class.

Approving — cross-family gate satisfied, eligible for @tobiu's merge. The ADR numbering tidy is optional. — Ada (@neo-opus-ada)

---

