From newsroom-os
Config-driven, channel-agnostic drafting engine for an installed newsroom. It turns approved research briefs + the commission + the user-approved editorial plan into a structurally-conformant edition DRAFT — in the channel's configured output_language, following the channel's configured format profile (section structure + toolkit), in the channel's configured voice lens. EVERY installation-specific value — the output language, the section names/order, the voice, the brand, the audience, the link/image rules — is a CONFIG READ from the active channel module's drafting.{format_profile_ref, voice_lens_ref, output_language} + config/company-context.md, NEVER a hardcoded literal. The drafting MECHANISM is pure: a reviewer grepping this skill finds no language/format/voice/brand/ section-name literals. Per D2 the voice lens is a PROPOSE→CONFIRM artifact the operator REFINES over several editions, and voice QUALITY is human-judged and explicitly NON-GATED; the skill gates STRUCTURE (format-profile conformance) + LANGUAGE (output_language) + PROVENANCE (every drafted claim grounds to a research brief's source anchor), not voice quality (AC4). The draft is written at status: draft and stops at the human refinement checkpoint — the skill NEVER self-greenlights and NEVER publishes (P8/R25). Use when the user asks to "draft the edition", "write the issue", "write the article", "assemble the draft", "rewrite a story", "produce the draft", or any request to produce or refine an edition draft after research briefs exist. Manifest-first: reads MANIFEST.md before globbing zones.
How this skill is triggered — by the user, by Claude, or both
Slash command
/newsroom-os:newsroom-draftingThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
The drafting layer of the newsroom: it turns the approved, researched material for
README.mdchecklists/provenance-grounding.mdchecklists/structural-conformance.mdreferences/ai-authenticity/00-method.mdreferences/ai-authenticity/de.mdreferences/ai-authenticity/en.mdreferences/ai-authenticity/nl.mdreferences/canonical-source-refinement-rule.mdreferences/drafting-runbook.mdreferences/format-profile-schema.mdreferences/voice-lens-schema.mdThe drafting layer of the newsroom: it turns the approved, researched material for one commission into a structurally-conformant edition draft that goes to the editor-in-chief at the human refinement checkpoint. It is a skill, not a CLI and not a writer-of-record: the agent reads the inputs and writes the draft by applying editorial craft — but every installation-specific shape it follows (the language, the section structure, the voice, the brand, the link and image rules) is read from config at runtime, never assumed.
The make-or-break invariant — config-as-truth. This is the spec's flagged hardest genericization: "language/format/voice as config." A reviewer grepping this skill (and its references/checklists) for a language literal, a section name, a voice descriptor, a brand name, an audience description, or a banned-word list will find none. They are all resolved from the active channel module's
drafting.{format_profile_ref, voice_lens_ref, output_language}andconfig/company-context.md. This is the literal generalization of the reference installation's output-lens port (one installation's lens replaced by another's, the lens system proven portable in the reference vault) — done once, generically, so the third installation costs an onboarding interview, not a fork.
This skill operates on an installed newsroom (a project-workspace-contract@2
workspace). It ships zero source-installation content.
Generic by construction. All worked examples below use the synthetic Meridian Robotics (a fictional industrial-automation vendor, German output) carried by the bundled contracts — mechanics only, never a real installation's language, sections, voice, or brand. The synthetic German fragments are illustrations of a config value resolving, not literals this skill embeds.
Per the substrate I/O spine
([[newsroom-os/skills/newsroom-install/templates/substrate/contracts/io-contracts.md]];
drafting conforms to it and operates on the installed copy at runtime), drafting is
the research + commission → edition-draft step that feeds the publish routine.
It produces the content the distribution routine later freezes into an edition
(per [[newsroom-os/skills/newsroom-install/templates/substrate/schemas/edition.md]],
status: draft). Drafting does not itself publish, freeze the edition snapshot,
or write delivery_state — that is the publish routine's job (R64).
approved research briefs ─┐
greenlit/draft commission ─┼─▶ DRAFTING ─▶ story drafts + assembled edition draft ─▶ human checkpoint (P8)
approved editorial plan ───┤ (processing/stories/, processing/drafts/) │
active channel drafting.* ─┘ ▼
config/company-context.md ─┘ publish routine → edition (frozen, R64)
| Direction | Artifact | Path | Role |
|---|---|---|---|
| Reads | registry | config/registry.yaml | resolve the active channel + its module_ref |
| Reads | channel module config | config/channels/<channel_id>.yaml | the drafting.{format_profile_ref, voice_lens_ref, output_language} triad |
| Reads | format profile | resolved from drafting.format_profile_ref | section structure + toolkit (config, never hardcoded) |
| Reads | voice lens | resolved from drafting.voice_lens_ref | voice/persona/vocabulary (iterated PROPOSE→CONFIRM artifact — D2) |
| Reads | company-context | config/company-context.md | §7 language & culture, §8 vocabulary, §4 audience, §1 identity |
| Reads | commission | newsroom/commissions/<id>.md | the production unit: brief_shape, derived_from_story_arcs, quick_note_items, slot plan |
| Reads | editorial plan | newsroom/editorial-plans/<plan-id>.md | format decision + slot directives the draft respects |
| Reads | research briefs | processing/research/<slot>/research-<slug>.md | the evidence boundary — source anchors per approved story |
| Reads (optional) | story-arcs / dossiers | newsroom/story-arcs/, newsroom/dossiers/ | arc angle + dossier evidence backing |
| Writes | story drafts | processing/stories/<slot>/story-<slug>.md | one self-contained story block per full-story slot |
| Writes | assembled edition draft | processing/drafts/<edition-draft-id>.md | the issue/article draft → human checkpoint (status: draft) |
processing/**is operational, not a primitive zone. Drafting's output lands inprocessing/stories/andprocessing/drafts/— in-flight editorial workspace, not one of the sevennewsroom/primitive kinds (directory-structure.md). It carries nonewsroom-indexmanifest entry and there is nometa/drafts-index; the draft zone is never manifest-indexed or graph-audit-regenerated. The publish routine reads the assembled draft directly to produce thenewsroom/editions/<id>edition.
Operational runbook. Read
references/drafting-runbook.mdbefore executing — it carries the step-by-step procedure (config resolution, per-story drafting from briefs, the format-profile-driven structure, the voice-lens application as iterated-non-gated, assembly, and refinement). The format-profile schema lives atreferences/format-profile-schema.md; the voice-lens schema atreferences/voice-lens-schema.md; the AI-authenticity rubric (per-language machine-tell catalog + humanization moves, applied on top of the voice lens) atreferences/ai-authenticity/. The end-of-run gates live inchecklists/structural-conformance.mdandchecklists/provenance-grounding.md.
Read MANIFEST.md at the newsroom root FIRST, before any files-first globbing
of zone folders. The manifest is the routing surface: it lists which commissions,
editorial-plans, story-arcs, and dossiers exist with their kind / status / path
/ edges. Discover the commission to draft and its upstream artifacts via the
manifest; use the zone folders only to resolve the concrete files the manifest
points at. The meta/*-index caches are derived caches subordinate to the manifest
(manifest wins on conflict). Files-first discovery — globbing a zone to learn what
exists — is the refused anti-pattern.
Before writing one word, resolve the active channel module and its drafting config. This is the step that makes everything downstream config-driven:
target_channel. Read
config/registry.yaml; find the channels[] entry whose channel_id maps to
that target_channel and is enabled: true; load its module_ref
(config/channels/<channel_id>.yaml).drafting.output_language — the ISO 639-1 language the draft is written in.drafting.format_profile_ref — resolve to the format profile (section
structure + toolkit). See references/format-profile-schema.md.drafting.voice_lens_ref — resolve to the voice lens (voice, persona,
vocabulary, framing discipline). See references/voice-lens-schema.md.*_ref paths resolve INSIDE the install (config/... or a
bundled child under the active module skill's references/). A *_ref that
escapes the install dir (../, an absolute system path, a sibling-plugin path)
is invalid — STOP and report (R63 / §3.5 isolation at the config layer).config/company-context.md — §7 (language & culture), §8 (vocabulary:
brand spelling rule + words-to-use + words-to-avoid), §4 (audience), §1
(identity). These supply the installation values the format profile and voice
lens reference; the skill reads them, it does not assume them.What this means in practice (the literal generalization). The reference installation hardcoded "write in German", a fixed
Was passiert ist / Einfach erklärt / Quellensection set, a named output lens, and a brand-spelling scan. Here, all four of those are reads: language ←drafting.output_language; sections ← the format profile; voice ← the voice lens; brand spelling ← company context §8. A different installation supplies a different language, a different section set, a different voice, a different brand — and this skill produces a correct draft for it with no skill edit. That is the portability claim, and the structural-conformance checklist is what enforces it.
If the active channel cannot be resolved, or any required drafting.* field is
missing, or a *_ref does not resolve inside the install — STOP and surface
the gap. Drafting never falls back to a hardcoded default language/format/voice
(there is none to fall back to — that is the point).
| Condition | Action |
|---|---|
| No greenlit/draft commission for this slot | STOP. Tell the user to run the managing-editor/commission step first. |
Commission exists but a derived_from_story_arcs story has no research brief | STOP. Send that story back to story-research. Drafting never invents evidence. |
Research brief quality_status: warnings and lacks support for a central claim | STOP. Return to research rather than drafting from weak evidence. |
| An approved editorial plan exists for the slot | Read it; respect its format_decision + slot directives. Record the linkage in the draft frontmatter. |
A plan_state: proposed plan exists | STOP. Surface it — a proposed plan awaits the user's approval; do not silently bypass the gate. |
Drafting is the executor of an already-approved commission; it does not make the
selection or planning decisions (those belong to curation + the managing-editor +
the human checkpoint). It reads status:/plan_state: to know it is clear to run;
it never writes a status:/arc_state:/plan_state:/dossier_state:
transition (R32 / R68 / P9) and never writes status: greenlit on anything
(P8 / R25).
Four moves, in order. Moves 1–3 produce the story drafts and the assembly; move 4
hands the assembled draft to the human checkpoint. None of them transitions a
status:/*_state: autonomously, and none of them embeds an installation literal.
For each full-story slot the commission carries (derived_from_story_arcs +
the plan's slot breakdown), write one self-contained story draft to
processing/stories/<slot>/story-<slug>.md:
primary_source_anchors / secondary_source_anchors and body. Drafting is
closed to broad web research for prose claims — if the brief is insufficient,
STOP and return the story to story-research (the format profile's IMAGE rule, if
it declares one, is the only narrowly-scoped carve-out — see Move 1 image step).drafting.output_language. Write the prose in that language.
Apply the company-context §7 orthography/register notes for that language. (The
reference installation's German-vs-ASCII-umlaut discipline generalizes to
"respect the configured language's orthography," resolved from §7 — not a
hardcoded German rule.)references/ai-authenticity/ (00-method.md
en/de/nl catalog matching drafting.output_language) and apply its
humanization moves: vary sentence length, cut formulaic signposting, prefer concrete
specifics over generic abstractions, break the "not just X, it's Y" and rule-of-three
reflexes, drop hedge-openers, and for DE/NL restore the modal particles a machine drops.
Like voice quality, this is human-judged and non-gated (D2 / R32) — a craft aid,
never a score or a blocker.source_anchors: frontmatter mirrors the
brief's anchors so the post-publish provenance integrity audit can walk the
full-story path (Edition → Commission → Story-Arc → Dossier → Wire → anchor). A
claim with no brief anchor is a provenance dead-end — cut it or send the story back
to research. See checklists/provenance-grounding.md.has_image + an image_scan_log when none found).
If the format profile declares no image affordance, skip this step entirely.
The image rule is read from the format profile, never hardcoded here.Some channels' format profiles declare a channel-level synthesis/lead slot (a
cross-story editorial piece). Draft it only when the format profile defines it
and the editorial plan approved it. It follows the format profile's spec for
that slot (length, image budget, free-form vs toolkit), written in
drafting.output_language and the voice lens. Save to
processing/stories/<slot>/<synthesis-slug>.md. If the format profile declares no
such slot, this move is a no-op.
Combine the story drafts + the commission's quick_note_items into one assembled
edition draft at processing/drafts/<edition-draft-id>.md:
quick_note_items[]
entry has ≥1 wire_refs (the provenance hard rule). Render each per the format
profile's quick-note format, carrying a resolvable link to the destination anchor.
Their provenance resolves along the quick-note path (Edition →
included_quick_note → Wire → anchor) — keep each note's wire_refs intact so the
publish routine can freeze them into the edition's included_quick_notes[].status: draft. Record output_language,
target_channel, commission_ref, story_drafts, story_order, and the plan
linkage in frontmatter (schema below). It is not an edition yet — the publish
routine produces newsroom/editions/<id> from it.Present the assembled draft to the editor-in-chief with a brief summary (story count, length against the format profile's target, any trade-offs made). Refinement is human-in-the-loop:
processing/stories/<slot>/story-<slug>.md story draft first, then reassemble.
The assembled draft must never become the only place where the final story text
lives. Issue-level-only changes (story order, header wording, quick-note phrasing,
transitions around the synthesis slot) may be edited directly in the assembled
draft.status: greenlit, never sets delivery_state, and
never publishes. The greenlight is the user's explicit signal at the checkpoint
(P8 / R25); publishing is the orchestrator's publish routine + the project-local
adapter (R64). Drafting stops at a clean status: draft the human can approve.The hardest part of "voice as config" is honest about its own limit:
drafting.output_language, not the source-installation's language), and
PROVENANCE (every claim grounds to a research-brief anchor — AC10 chain). AC4
passes on structural conformance + correct target language + zero
source-installation residue, not on a voice score.This split is load-bearing: it is why "voice as config" is an honest claim and not
an over-promise. See references/voice-lens-schema.md § "Voice quality is
non-gated."
These are the mechanisms, all driven by config — none embeds an installation literal:
checklists/structural-conformance.md.drafting.output_language
(proper nouns and unavoidable technical terms excepted), and respects that
language's orthography per company-context §7. Brand spelling matches §8.wire_refs; the
story drafts' source_anchors: mirror the briefs so the post-publish provenance
integrity audit can walk the typed paths. Checklist:
checklists/provenance-grounding.md.has_image
image_scan_log when none); the assembled draft preserves every story image +
caption verbatim. If the format profile declares no image affordance, this gate
is N/A.Voice quality is NOT on this list — by design (D2). None of these gates scores or blocks on voice quality. Voice is applied (the lens) and human-judged (non-gated).
processing/stories/<slot>/)---
type: story-draft # processing artifact — `type:` only; `kind:` is reserved for the seven manifest primitives
story_kind: full-story
story_slug: <arc-slug> # mirrors the story-arc arc_id + research brief story_slug
title: "<plain working title>"
target_channel: <from commission> # closed enum value — read, not hardcoded
output_language: <from drafting.output_language> # ISO 639-1 — read from channel config
issue_slot: "<period>" # mirrors the editorial-plan / commission slot
commission_ref: "[[newsroom/commissions/<commission-id>]]"
research_brief_ref: "[[processing/research/<slot>/research-<slug>]]"
story_arc_ref: "[[newsroom/story-arcs/<arc-slug>]]"
format_profile_ref: "<resolved drafting.format_profile_ref>" # provenance of the shape applied
voice_lens_ref: "<resolved drafting.voice_lens_ref>" # provenance of the voice applied
selected_sections: [] # the beats chosen FROM the format profile's toolkit
source_anchors: # MIRRORS the research brief — the AC10 grounding
- anchor_type: url | corpus_note | raw
ref: "<https://… | [[knowledge/<subzone>/<note>]] | [[research/sources/<id>/raw…]]>"
backing_wire: "[[newsroom/wire/<wire-id>]]"
has_image: false | true # only when the format profile declares an image slot
image_scan_log: null # required when has_image is false AND an image slot exists
status: draft # RESERVED enum (R61); starts + stays draft (never greenlit here)
created: 2026-06-18
related:
- "[[processing/research/<slot>/research-<slug>]]"
- "[[newsroom/story-arcs/<arc-slug>]]"
---
processing/drafts/)---
type: edition-draft # processing artifact — `type:` only; `kind:` is reserved for the seven manifest primitives
edition_draft_id: "<channel slot id>" # mirrors the channel's canonical slot/filename pattern
title: "<published-style title>" # in output_language; brand spelling per company-context §8
target_channel: <from commission> # closed enum — read, not hardcoded
output_language: <from drafting.output_language> # ISO 639-1 — read from channel config
issue_slot: null | "<period>" # for issue-based channels
commission_ref: "[[newsroom/commissions/<commission-id>]]"
editorial_plan_ref: null | "[[newsroom/editorial-plans/<plan-id>]]"
format_profile_ref: "<resolved drafting.format_profile_ref>"
voice_lens_ref: "<resolved drafting.voice_lens_ref>"
format_decision: null | regular | special | series | digest # mirrors the plan
story_drafts:
- "[[processing/stories/<slot>/story-<slug>]]"
story_order:
- <arc-slug>
synthesis_slot_included: false | true # iff the format profile + plan declare one
quick_note_slugs: [] # mirrors commission.quick_note_items[].slug
status: draft # RESERVED enum (R61); the human greenlights, not this skill
created: 2026-06-18
last_updated: 2026-06-18
related:
- "[[newsroom/commissions/<commission-id>]]"
- "<resolved drafting.format_profile_ref>"
- "<resolved drafting.voice_lens_ref>"
---
The edition-draft is a workspace artifact, not the published edition. The publish routine consumes it to write
newsroom/editions/<id>(frozen snapshot +delivery_state) per the edition schema (R64). Drafting writes neither the frozenincluded_story_arcs[]/included_quick_notes[]snapshot nordelivery_state— those are the publish routine's (the truth-gate is publish's, not drafting's).
| Condition | Action |
|---|---|
Active channel not resolvable from registry.yaml / commission target_channel | STOP. Report the registry/commission mismatch; never guess a channel. |
A required drafting.* field missing, or a *_ref escapes the install | STOP. Report the invalid module config (R63). No hardcoded fallback exists. |
| Format profile or voice lens does not resolve | STOP. Report the broken *_ref. Drafting cannot run without a format profile + lens. |
| Commission missing or not greenlit/draft | STOP. Route back to the commission step. |
Research brief missing for a derived_from_story_arcs story | STOP. Send that story back to story-research. |
Research brief stale or quality_status: warnings without central-claim support | STOP. Regenerate/strengthen research before drafting. |
| A drafted claim has no research-brief anchor | Cut the claim or return the story to research — never publish an ungrounded claim. |
quick_note_items[i].wire_refs empty | STOP. A quick note with no wire is a provenance dead-end (Commandment V). |
No quick_note_items | Proceed without the quick-note section (if the format profile makes it optional). |
| Format profile declares no synthesis slot, but the plan asked for one | Surface the mismatch; the format profile is authoritative for what slots exist. |
status: draft.drafting.output_language AND free of source-installation residue — gated on
structure + language + provenance, not on a voice-quality score.wire_refs.status: greenlit and never publishes; it
stops at the human refinement checkpoint.The draft's human-facing output language is config-driven, never hardcoded — it
is drafting.output_language (the active channel module's triad), which the install
derives from config/company-context.md §7 output_language. Respond to the
operator in the conversation's language unless asked otherwise. Beyond that:
drafting.output_language (Phase 0.5) —
honor that config declaration over the inferred conversation language for the
artifact prose; it is the gated LANGUAGE invariant (AC4), not a default.ue, oe, ae, ss).type, status, target_channel, output_language value as ISO 639-1).Standard runs end normally: the assembled status: draft edition draft + a brief
summary + the suggested next step (the human refinement checkpoint). Do not fire
a generic "anything to add?" prompt.
When a deviation specific to drafting occurred during the run, surface it and ask
whether to fold the change back into SKILL.md / the bundled references (the format-
profile schema, the voice-lens schema, the structural-conformance / provenance-
grounding checklists) before going idle. Drafting-specific triggers:
*_ref resolved awkwardly, or an image/link rule
needed improvisation the profile did not anticipate;Name the concrete artifact (the specific story draft, the format profile, the voice lens) in the prompt. If nothing deviated, end without the prompt.
npx claudepluginhub cmgramse/skill-development --plugin newsroom-osGenerates brand assets: logos (55+ styles, Gemini AI), CIP mockups, HTML slides (Chart.js), banners (22 styles), SVG icons (15 styles), and social media photos. Routes to sub-skills for design tokens and UI styling.