AI-translated from English; not yet reviewed by a fluent editor.

# ایک منظور شدہ ماخذ سے تین جانچے جانے والے مسودوں کے لیے Claude Code skills ملائیں

> چار الگ Claude Code skills سے منظور شدہ تحریر کی فہرست بنائیں، LinkedIn، نیوزلیٹر اور اشتہار کے مسودے لکھیں، پھر انسانی جائزے سے پہلے فائل ہیش اور دعوے جانچیں۔

By BIG CHANGE Editorial

Published: 2026-10-19T19:41:34.834Z
Updated: 2026-10-19T19:41:34.834Z
Canonical: https://bigchange.ai/blog/claude-code-three-channel-skill-pipeline-guide

![Conceptual charcoal illustration of three unbranded layout proofs: a social post on a phone, an open newsletter on a wooden stand, and a small ad in a metal holder.](https://bigchange.ai/api/media/file/three-channel-proof-hero-v1.png)
AI-generated conceptual illustration by BIG CHANGE; no actual Claude Code interface, model output, reviewed draft or campaign is depicted.

منظور شدہ مضمون یا نقلِ گفتگو کئی مسودوں کی بنیاد بن سکتی ہے، مگر ہر مسودے کا ماخذ تک واضح راستہ ضروری ہے۔ یہ رہنما چار الگ [Claude Code پروجیکٹ skills](https://code.claude.com/docs/en/skills) ملاتا ہے: مالک کی منظور شدہ تحریر کی فہرست، الگ ڈائریکٹریوں میں LinkedIn پوسٹ، نیوزلیٹر کا حصہ اور اشتہار، پھر مقامی Python یکجائی سے فائل معاہدوں کی جانچ۔ نتیجہ نامزد انسانی مبصر کے لیے مسودے ہیں، شائع یا ارسال کرنے کی اجازت نہیں۔

BIG CHANGE نے ذیل کے skills، معاہدوں اور یکجائی کوڈ کا جائزہ لیا۔ آزاد مبصر نے 16 مقامی ساختی جانچیں چلائیں۔ ہم نے **نہیں** کیے: Claude Code میں skills نصب یا چلانا، ماڈل چلانے کی ادائیگی، حقیقی حقائق والا چینل مسودہ بنانا یا اصل مہم آزمانا۔ آگے قارئین کے اپنے منظور شدہ ماخذ پر آزمانے کا طریقہ ہے۔

## بڑی تبدیلی

- **کیا بدلا:** اپنی چار skills کے لیے ماخذ، فہرست اور چینل نتیجے کے واضح معاہدے ہیں، جن کے بعد متعین اصولوں والی یکجائی ہوتی ہے۔
- **یہ کیوں اہم ہے:** ہر مسودہ ماخذ کے حصوں سے ملایا جا سکتا ہے، جبکہ غیرموجود چینل رکا ہوا خانہ بن کر نمایاں رہتا ہے۔
- **کس بات کا خیال رکھیں:** فائل جانچ سچائی، موافقت کے حقوق یا ادارتی منظوری ثابت نہیں کرتی؛ ہر تیار مسودہ انسان جانچے۔

## 1. رسائی، حقوق اور ماخذ کی تصدیق

اپنے اختیار کا Claude Code ذخیرہ اور ماخذ پر کارروائی کی اجازت والا اکاؤنٹ استعمال کریں۔ خفیہ مواد سے پہلے دستیاب ماڈل، [استعمال اور اخراجات](https://code.claude.com/docs/en/costs), [ڈیٹا پالیسی](https://code.claude.com/docs/en/data-usage) اور [ٹول اجازتیں](https://code.claude.com/docs/en/permissions) جانچیں۔ Claude Code کی `/usage` کمانڈ موجودہ نشست کا استعمال دکھا سکتی ہے؛ بل اکاؤنٹ اور منصوبے پر منحصر ہے۔ اس عمل کی لاگت یا بچت نہیں ماپی گئی۔ ماخذ اصل منظور شدہ UTF-8 مضمون یا جانچی ہوئی نقل ہو، جس کے مالک، نسخے اور منظوری کا ریکارڈ ہو۔ نقل کی درستی، ہر چینل کے حقوق، رازداری اور کسی پیشکش کو پہلے مالک سے طے کریں۔ ویڈیو URL یا نیچے خالی سانچہ یہ شرط پوری نہیں کرتا۔

چار حوالہ جاتی skills، Claude Code کی دستاویزی `.claude/skills/<name>/SKILL.md` جگہ، سلیش کمانڈ سے بلانے اور `$ARGUMENTS` متبادل استعمال کرتی ہیں۔ ان کی `context: fork` ترتیب ہر کال کو گفتگو کی تاریخ کے بغیر الگ سیاق دیتی ہے۔ مسودوں کی skills میں `background: true` استعمال ہوتا ہے، جس کے لیے Claude Code **v2.1.218 یا بعد کا نسخہ** درکار ہے۔ یہ عمومی ایجنٹ استعمال کرتی ہیں؛ فائل لکھنے کی صلاحیت اور مؤثر اجازتیں صارف کی Claude Code ترتیب پر چلتی ہیں۔ skill کا متن اجازت نہیں دیتا اور صرف پڑھنے کی حد نافذ نہیں کرتا۔ میزبان اجازت دے تو پس منظر کے کام باہم چل سکتے ہیں، مگر متوازی اجرا کی ضمانت نہیں۔ نام اختیار کرنے سے پہلے ہم نام موجودہ skills اور کمانڈ تلاش کا نظام جانچیں، اور یقینی بنائیں کہ کمانڈ نصب کردہ پروجیکٹ فائل کی طرف جاتی ہے۔ دیکھیں [skills ہدایت نامہ](https://code.claude.com/docs/en/skills) اور [subagents ہدایت نامہ](https://code.claude.com/docs/en/sub-agents).

## 2. چار فائلیں اور معاہدے نصب کریں

پروجیکٹ میں نیچے فہرست کے عین راستے بنائیں۔ حوالہ جاتی فائلوں کا ہر مکمل بلاک بغیر تبدیلی نقل کریں۔ `.claude/repurpose-contracts/` کی پانچ فائلیں ان پٹ، فہرست، رکی حالت، چینل نتیجے کی شکل اور انحصار کا منصوبہ طے کرتی ہیں۔ `join.py` کو پروجیکٹ کی جڑ میں رکھیں۔ دو سانچے `repurpose-inputs/` میں جائیں گے؛ اجرا سے پہلے عارضی قدریں حقیقی منظور شدہ مواد سے بدلیں۔ آؤٹ پٹ درخت صرف ساخت دکھاتا ہے۔ نقل کے لیے مکمل فائلیں نیچے ہیں۔

| فائل | مقصد |
| --- | --- |
| `.claude/skills/source-inventory/SKILL.md` | منظوری جانچ کر لکھنے سے پہلے ماخذ کی ہر سطر درج کرتی ہے۔ |
| `.claude/skills/draft-linkedin/SKILL.md` | صرف LinkedIn مسودے اور نتیجے کی ذمہ دار۔ |
| `.claude/skills/draft-newsletter/SKILL.md` | صرف نیوزلیٹر مسودے اور نتیجے کی ذمہ دار۔ |
| `.claude/skills/draft-ad/SKILL.md` | صرف اشتہاری مسودے اور نتیجے یا پیشکش کی واضح رکاوٹ کی ذمہ دار۔ |
| `.claude/repurpose-contracts/*.json` | عین خانے، ہیش تعلق اور مراحل کا ترتیب طے کرتی ہے۔ |
| `join.py` | ساخت اور اصل جانچ کر دعووں کا نقشہ اور جائزے کی حوالگی بناتی ہے۔ |
| `repurpose-inputs/approved-source.template.md`, `repurpose-inputs/constraints.template.json` | خالی ابتدائی فارم، اجرا کا ثبوت ہرگز نہیں۔ |

پابندیاں LinkedIn، نیوزلیٹر اور اشتہار کے لیے واضح موافقت کے حقوق چاہتی ہیں، چاہے منظور شدہ پیشکش نہ ہونے سے اشتہار رکے۔ `forbiddenClaims` مالک کی مزید ممانعت نہ ہو تو خالی فہرست ہو سکتی ہے۔ اشتہار کے لیے منظور شدہ پیشکش، HTTP(S) منزل اور عمل کی دعوت ضروری ہے؛ کوئی قدر نہ ہو تو شاخ رکتی ہے۔ حقیقی مبصر کا نام درج کریں۔ اجرا میں منظور شدہ ماخذ اور پابندیاں نہ بدلیں۔

## 3. ہر انحصار آزمائیں، پھر فہرست چلائیں

پہلے ہر skill کو محدود، حقیقی، منظور شدہ ماخذ پر الگ آزمائیں۔ اصل فائلیں اور ہیش معاہدوں سے ملائیں۔ BIG CHANGE نے یہ Claude Code آزمائش نہیں کی۔ مکمل اجرا کے لیے ان پٹ، نتائج اور یکجائی کے نئے مطلق راستوں والے فولڈر بنائیں۔ ذخیرے میں تین مطلق راستوں سے فہرست کمانڈ بلائیں:

```prompt
/source-inventory <absolute-path-to-approved-source.md> <absolute-path-to-constraints.json> <absolute-path-to-new-run-directory>
```

زاویائی قوسین کا ہر جزو حقیقی راستے سے بدلیں۔ skill کو `inventory.json` لکھنا چاہیے جس میں ہر سطر کو ڈھانپتے لفظ بہ لفظ، ترتیب وار حصوں کے دائرے ہوں، یا `inventory-blocked.json` جس میں عین غیرموجود منظوری یا حق ہو۔ فہرست رکی ہو تو مسودہ شروع نہ کریں۔ درج SHA-256 دونوں ان پٹ فائلوں سے ملائیں اور خالی سطروں سمیت حصوں کی حد دیکھیں۔

## 4. تین الگ چینلوں میں مسودے

جب `inventory.json` موجود ہو اور دونوں ان پٹ ہیش ریکارڈ سے ملتے رہیں تو تین الگ skills بلائیں۔ پہلا جزو ایک ہی مطلق اجرا ڈائریکٹری اور دوسرا تین الگ آؤٹ پٹ ڈائریکٹریاں ہوں۔ ہر skill منظور شدہ ماخذ، پابندیاں اور فہرست پڑھتی ہے، ساتھی مسودہ نہیں۔ فہرست کے بعد انہیں الگ شروع کیا جا سکتا ہے، مگر Claude Code میزبان اور اجازتوں کے تحت۔

```prompt
/draft-linkedin <absolute-run-directory> <absolute-run-directory>/linkedin
/draft-newsletter <absolute-run-directory> <absolute-run-directory>/newsletter
/draft-ad <absolute-run-directory> <absolute-run-directory>/ad
```

ہر چینل `result.json` اور صرف تیار ہونے پر `draft.md` کا ذمہ دار ہے۔ نتیجہ چینل اور ماخذ، پابندیوں، فہرست کے SHA-256 بتاتا، پھر اہم دعوے یا اقتباس حصوں کے ID اور متعلقہ پابندی کلیدوں سے جوڑتا ہے۔ `ready` یعنی حقائق کے جائزے کے لیے تیار۔ منظور شدہ اشتہاری پیشکش، منزل یا دعوت نہ ہو تو اشتہاری مسودے کے بغیر رکا نتیجہ لازم ہے۔ پس منظر کال یا درست JSON اکیلا افادیت یا درستی ثابت نہیں کرتا۔

## 5. نتائج یکجا کریں اور انسانی فیصلہ لیں

تینوں مراحل لوٹ آئیں یا یقینی ناکام ہوں تو فراہم کردہ Python فائل اجرا ڈائریکٹری اور ایک **نئی** یکجائی ڈائریکٹری کے ساتھ چلائیں۔ یہ Python معیاری لائبریری استعمال کرتی ہے؛ پہلے مقامی تنصیب جانچیں۔

```bash
python join.py <absolute-run-directory> <absolute-new-join-directory>
```

یکجائی ماخذ اور پابندی ہیش، فہرست میں تمام سطریں، ہر چینل کا فہرست ہیش، مرحلے کی شناخت، دعوے کے اشارے، آؤٹ پٹ کی ذمہ داری اور اشتہار کی شرائط جانچتی ہے۔ یہ لکھتی ہے `claim-map.md`, `review.md` اور `summary.json`۔ غیرموجود یا غلط مراحل رکے دکھتے ہیں، مگر درست ساتھی مسودے نمایاں رہتے ہیں۔ مجموعی ماخذ کا فرق یکجائی روکتا ہے۔ یہ جملے کی سچائی، اقتباس کی انصاف پسندی، دعوے کی مناسب قانونی بنیاد یا مالک کی اشاعت منظوری نہیں جانچتی۔ `review.md` میں آغاز پر `PENDING` فیصلے ہوتے ہیں۔

نامزد مبصر ہر تیار `draft.md` کھول کر تمام اہم بیانات اور اقتباس مذکورہ حصے و پابندیوں سے ملائے، ابہام، چینل کے لہجے، موافقت کے حقوق، اشتہار کی منزل و پیشکش جانچے، پھر بنے انتظار والے خانوں سے باہر ہر چینل کے لیے درج کرے **قبول، ترمیم یا رد** ۔ اشاعت، ارسال، وقت بندی اور اشتہار لگانے کے لیے الگ انسانی اجازت اور پلیٹ فارم جانچ درکار ہے۔

## 6. درست کام بچا کر ناکام شاخ بحال کریں

ماخذ کی منظوری یا حقوق مبہم ہوں تو مالک سے عین کمی حل کر کے درست ان پٹ سے نیا اجرا کریں۔ ماخذ یا پابندی ہیش بدلے تو نئی مکمل فہرست چاہیے۔ ایک چینل ناکام اور باقی درست ہوں تو پہلے اس کی اصل فائلیں اور خطا دیکھیں۔ غیرمتبدل فہرست بائٹس اور جانچے ساتھی فولڈر نئی اجرا ڈائریکٹری میں نقل کریں، صرف ناکام مرحلہ وہاں چلائیں اور دوسری نئی ڈائریکٹری میں یکجا کریں۔ نقل نتائج اب بھی فہرست ہیش سے ملیں۔ عمل کا نتیجہ غیر یقینی ہو تو دہرانے سے پہلے نتیجہ اور ہیش پڑھیں۔ پچھلے اجرا اور مبصر کی ترامیم محفوظ رکھیں؛ دوبارہ چلا کر اوپر نہ لکھیں۔ گم یا غیرمعاون دعویٰ اشارہ ذمہ دار مرحلے میں درست کر کے نئی ڈائریکٹری میں متعین یکجائی دہرائیں۔

یہ دستی طور پر بلائی گئی، ماڈل کی رہنمائی والی ترکیب ہے۔ کوڈ سے نافذ تقسیم، خودکار دوبارہ کوشش، مقررہ ہم زمانی یا بیرونی اثرات کی بحالی نہیں دیتی۔ منظور شدہ تحریر سے شروع کریں؛ یہ نقل حاصل یا جانچ نہیں کرتی، اور مہم کے ویڈیو، آڈیو یا بصری مواد نہیں بناتی۔

## مکمل حوالہ جاتی فائلیں

راستے اور مواد ساتھ رکھیں۔ نیچے کوڈ بلاک آزادانہ جانچے پیکٹ کی مکمل نقول ہیں؛ یہ حوالہ ہیں، Claude اجرا کا ریکارڈ نہیں۔

### `.claude/skills/source-inventory/SKILL.md`

```markdown
---
name: source-inventory
description: Build a bounded passage inventory from one approved owned source for a three-format draft pipeline.
disable-model-invocation: true
context: fork
agent: general-purpose
background: false
---

Input arguments: `$ARGUMENTS[0]` is the approved UTF-8 source Markdown path, `$ARGUMENTS[1]` is the constraints JSON path, and `$ARGUMENTS[2]` is a new run directory. Read only those inputs and the run directory contract. Do not infer inputs from conversation history.

1. Verify both files exist. Read `constraints.json` against `.claude/repurpose-contracts/constraints.schema.json`; require `sourceOwner`, `sourceVersion`, `approvedAt`, `adaptationRights`, `audience`, `permittedChannels`, `reviewer`, `forbiddenClaims`, and `offer`. `adaptationRights` must explicitly mark all three channels `true`; a template or unapproved value does not count. `offer` may be null; that blocks ad copy requiring an offer or destination. If any approval or right is unclear, create only `inventory-blocked.json` under `.claude/repurpose-contracts/inventory-blocked.schema.json` explaining the gap and stop.
2. Record SHA-256 hashes of the exact two input files. Divide the entire approved source into numbered, contiguous, verbatim line ranges with no omitted lines. Give each range a stable `P001`-style ID, start/end line and exact text, including blank lines where they occur in a range. Do not add outside facts, inferred transcript words, or quotations not present in the source.
3. Write `inventory.json` in the run directory according to `.claude/repurpose-contracts/inventory.schema.json` in the reader project. Record the source and constraints paths/hashes, source owner/version/approval, passages and any ambiguous passages requiring human review. Do not edit either input.
4. Return the inventory path and blockers. This stage never drafts, publishes, fetches video, contacts services or approves source rights on behalf of the owner.
```

### `.claude/skills/draft-linkedin/SKILL.md`

```markdown
---
name: draft-linkedin
description: Draft one review-only LinkedIn post from an approved source inventory.
disable-model-invocation: true
context: fork
agent: general-purpose
background: true
---

Inputs: `$ARGUMENTS[0]` is the run directory containing `inventory.json`; `$ARGUMENTS[1]` is the dedicated `linkedin` output directory. Read the source and constraints paths recorded in the inventory and verify their hashes still match. Do not rely on prior conversation or sibling-stage results.

Write only under the dedicated output directory. If required source, permission, audience or voice information is missing or conflicting, write `result.json` with `status: blocked` and exact blockers; do not invent it. Otherwise create `draft.md`: one self-contained LinkedIn post with a factual opening and concrete takeaway, no invented statistics, quotes, benefits, offers or links. Write `result.json` according to `.claude/repurpose-contracts/stage-result.schema.json` in the reader project, with `channel: linkedin`, input hashes, inventory hash, draft path, and every material claim or quote paired with exact passage IDs and any applicable constraints keys. Mark uncertainty in the draft and result. `status: ready` means ready for human fact review, not approved or published. Return the two file paths. Never post or schedule.
```

### `.claude/skills/draft-newsletter/SKILL.md`

```markdown
---
name: draft-newsletter
description: Draft one review-only newsletter section from an approved source inventory.
disable-model-invocation: true
context: fork
agent: general-purpose
background: true
---

Inputs: `$ARGUMENTS[0]` is the run directory containing `inventory.json`; `$ARGUMENTS[1]` is the dedicated `newsletter` output directory. Read the source and constraints paths recorded in the inventory and verify their hashes still match. Do not rely on prior conversation or sibling-stage results.

Write only under the dedicated output directory. If required source, permission, audience or voice information is missing or conflicting, write `result.json` with `status: blocked` and exact blockers. Otherwise create `draft.md`: a descriptive heading, short summary and evidence-linked detail suited to the approved newsletter audience. Add no invented statistics, quotes, benefits or outside news. Write `result.json` according to `.claude/repurpose-contracts/stage-result.schema.json` in the reader project, with `channel: newsletter`, input hashes, inventory hash, draft path, and every material claim or quote paired with exact passage IDs and applicable constraints keys. Mark uncertainty in the draft and result. `status: ready` means ready for human fact review, not approved or sent. Return the two file paths. Never send or schedule.
```

### `.claude/skills/draft-ad/SKILL.md`

```markdown
---
name: draft-ad
description: Draft review-only ad copy from an approved source inventory and approved offer.
disable-model-invocation: true
context: fork
agent: general-purpose
background: true
---

Inputs: `$ARGUMENTS[0]` is the run directory containing `inventory.json`; `$ARGUMENTS[1]` is the dedicated `ad` output directory. Read the source and constraints paths recorded in the inventory and verify their hashes still match. Do not rely on prior conversation or sibling-stage results.

Write only under the dedicated output directory. Require an approved offer, destination URL and call to action in constraints, plus adaptation rights for advertising. If any are absent, write `result.json` with `status: blocked`, the missing exact fields and no draft. Otherwise create `draft.md` with one headline, one body and the approved call to action. No invented metrics, testimonials, urgency, claims, price or performance. Write `result.json` according to `.claude/repurpose-contracts/stage-result.schema.json` in the reader project, with `channel: ad`, input hashes, inventory hash, draft path, and every material claim or quote paired with exact passage IDs and applicable constraints keys. Mark uncertainty in the draft and result. `status: ready` means ready for human fact/legal review, not approved or placed. Return the two file paths. Never upload, buy or schedule ads.
```

### `.claude/repurpose-contracts/constraints.schema.json`

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Reader-approved source constraints",
  "type": "object",
  "required": ["sourceOwner", "sourceVersion", "approvedAt", "adaptationRights", "audience", "permittedChannels", "reviewer", "forbiddenClaims", "voiceRules", "offer"],
  "properties": {
    "sourceOwner": {"type": "string", "minLength": 1},
    "sourceVersion": {"type": "string", "minLength": 1},
    "approvedAt": {"type": "string", "minLength": 1},
    "adaptationRights": {"type": "object", "required": ["linkedin", "newsletter", "ad"], "properties": {"linkedin": {"type": "boolean"}, "newsletter": {"type": "boolean"}, "ad": {"type": "boolean"}}},
    "audience": {"type": "string", "minLength": 1},
    "permittedChannels": {"type": "array", "uniqueItems": true, "items": {"enum": ["linkedin", "newsletter", "ad"]}},
    "reviewer": {"type": "string", "minLength": 1},
    "forbiddenClaims": {"type": "array", "items": {"type": "string"}},
    "voiceRules": {"type": "string", "minLength": 1},
    "offer": {"type": ["object", "null"], "required": ["approvedOffer", "destinationUrl", "callToAction"], "properties": {"approvedOffer": {"type": "string", "minLength": 1}, "destinationUrl": {"type": "string", "format": "uri"}, "callToAction": {"type": "string", "minLength": 1}}}
  }
}
```

### `.claude/repurpose-contracts/inventory.schema.json`

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Approved source inventory",
  "type": "object",
  "additionalProperties": false,
  "required": ["sourcePath", "sourceSha256", "constraintsPath", "constraintsSha256", "sourceOwner", "sourceVersion", "approvedAt", "passages", "ambiguities"],
  "properties": {
    "sourcePath": {"type": "string", "minLength": 1},
    "sourceSha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
    "constraintsPath": {"type": "string", "minLength": 1},
    "constraintsSha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
    "sourceOwner": {"type": "string", "minLength": 1},
    "sourceVersion": {"type": "string", "minLength": 1},
    "approvedAt": {"type": "string", "minLength": 1},
    "passages": {"type": "array", "minItems": 1, "items": {"type": "object", "additionalProperties": false, "required": ["id", "startLine", "endLine", "text"], "properties": {"id": {"type": "string", "pattern": "^P[0-9]{3,}$"}, "startLine": {"type": "integer", "minimum": 1}, "endLine": {"type": "integer", "minimum": 1}, "text": {"type": "string"}}}},
    "ambiguities": {"type": "array", "items": {"type": "string"}}
  }
}
```

### `.claude/repurpose-contracts/inventory-blocked.schema.json`

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Preflight block before drafting",
  "type": "object",
  "additionalProperties": false,
  "required": ["status", "blockers"],
  "properties": {
    "status": {"const": "blocked"},
    "blockers": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}}
  }
}
```

### `.claude/repurpose-contracts/stage-result.schema.json`

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "One isolated channel result",
  "type": "object",
  "additionalProperties": false,
  "required": ["channel", "status", "sourceSha256", "constraintsSha256", "inventorySha256", "draftFile", "claims", "blockers"],
  "properties": {
    "channel": {"enum": ["linkedin", "newsletter", "ad"]},
    "status": {"enum": ["ready", "blocked"]},
    "sourceSha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
    "constraintsSha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
    "inventorySha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
    "draftFile": {"type": ["string", "null"]},
    "claims": {"type": "array", "items": {"type": "object", "additionalProperties": false, "required": ["text", "passageIds", "constraintKeys", "status"], "properties": {"text": {"type": "string", "minLength": 1}, "passageIds": {"type": "array", "items": {"type": "string", "pattern": "^P[0-9]{3,}$"}}, "constraintKeys": {"type": "array", "items": {"type": "string"}}, "status": {"enum": ["supported", "needs-human-check", "unsupported"]}}}},
    "blockers": {"type": "array", "items": {"type": "string"}}
  }
}
```

### `.claude/repurpose-contracts/pipeline-plan.json`

```json
{
  "purpose": "Compose reader-owned skills into one source-to-three-format review run",
  "execution": "manual Claude Code skill invocation plus deterministic local join",
  "sourceStage": {
    "skill": "source-inventory",
    "inputs": ["approved-source.md", "constraints.json"],
    "success": "inventory.json with source and constraints hashes and complete numbered line coverage",
    "failure": "inventory-blocked.json; stop all channel stages"
  },
  "parallelAfterInventory": [
    {"skill": "draft-linkedin", "reads": ["approved-source.md", "constraints.json", "inventory.json"], "writesOnly": "linkedin/", "contract": "stage-result.schema.json"},
    {"skill": "draft-newsletter", "reads": ["approved-source.md", "constraints.json", "inventory.json"], "writesOnly": "newsletter/", "contract": "stage-result.schema.json"},
    {"skill": "draft-ad", "reads": ["approved-source.md", "constraints.json", "inventory.json"], "writesOnly": "ad/", "contract": "stage-result.schema.json"}
  ],
  "join": {
    "file": "join.py",
    "dependsOn": ["all three stage results or recorded missing/failed slots"],
    "writesOnly": "new join directory",
    "outputs": ["claim-map.md", "review.md", "summary.json"],
    "humanApproval": "pending"
  }
}
```

### `join.py`

```python
"""Deterministic join/check for the proposed local, review-only pipeline.

This does not call a model, generate prose, or approve factual support. Run only
on reader-owned paths after separately invoked stage skills have returned.
"""

import hashlib
import json
import sys
from pathlib import Path
from urllib.parse import urlparse

CHANNELS = ("linkedin", "newsletter", "ad")
HEX = set("0123456789abcdef")


def digest(path):
    return hashlib.sha256(path.read_bytes()).hexdigest()


def valid_hash(value):
    return isinstance(value, str) and len(value) == 64 and set(value) <= HEX


def read_json(path):
    return json.loads(path.read_text(encoding="utf-8"))


def string_list(value):
    return isinstance(value, list) and all(isinstance(v, str) and v.strip() for v in value)


def approved_offer(value):
    return (isinstance(value, dict) and
            all(isinstance(value.get(k), str) and value[k].strip()
                for k in ("approvedOffer", "destinationUrl", "callToAction")) and
            urlparse(value["destinationUrl"]).scheme in ("http", "https") and
            bool(urlparse(value["destinationUrl"]).netloc))


def check_inventory(run):
    inv_path = run / "inventory.json"
    inv = read_json(inv_path)
    expected = {"sourcePath", "sourceSha256", "constraintsPath", "constraintsSha256",
                "sourceOwner", "sourceVersion", "approvedAt", "passages", "ambiguities"}
    if set(inv) != expected:
        raise ValueError("inventory keys differ from contract")
    source, constraints = Path(inv["sourcePath"]), Path(inv["constraintsPath"])
    if not source.is_file() or not constraints.is_file():
        raise ValueError("approved source or constraints missing")
    if digest(source) != inv["sourceSha256"] or digest(constraints) != inv["constraintsSha256"]:
        raise ValueError("approved input changed since inventory")
    rules = read_json(constraints)
    required = ("sourceOwner", "sourceVersion", "approvedAt", "adaptationRights",
                "audience", "permittedChannels", "reviewer", "forbiddenClaims", "voiceRules", "offer")
    if not isinstance(rules, dict) or any(k not in rules for k in required):
        raise ValueError("constraints approval/rights fields incomplete")
    text_fields = ("sourceOwner", "sourceVersion", "approvedAt", "audience", "reviewer", "voiceRules")
    if any(not isinstance(rules[k], str) or not rules[k].strip() for k in text_fields):
        raise ValueError("constraints text fields incomplete")
    if not string_list(rules["forbiddenClaims"]) or not string_list(rules["permittedChannels"]):
        raise ValueError("constraints lists malformed")
    if any("REPLACE_WITH" in rules[k] for k in text_fields):
        raise ValueError("constraints template was not completed")
    if not isinstance(rules["adaptationRights"], dict) or any(rules["adaptationRights"].get(c) is not True for c in CHANNELS):
        raise ValueError("all three adaptation rights must be explicit")
    if not set(CHANNELS).issubset(set(rules["permittedChannels"])):
        raise ValueError("three-channel rights not explicit")
    if any(rules[k] != inv[k] for k in ("sourceOwner", "sourceVersion", "approvedAt")):
        raise ValueError("source identity differs from constraints")
    lines = source.read_text(encoding="utf-8").splitlines()
    if not lines:
        raise ValueError("source is empty")
    next_line, ids = 1, set()
    if not isinstance(inv["passages"], list) or not string_list(inv["ambiguities"]):
        raise ValueError("inventory passages or ambiguities malformed")
    for n, p in enumerate(inv["passages"], 1):
        if set(p) != {"id", "startLine", "endLine", "text"}:
            raise ValueError("passage keys differ from contract")
        if p["id"] != f"P{n:03d}" or p["id"] in ids:
            raise ValueError("passage IDs must be ordered and unique")
        ids.add(p["id"])
        if (type(p["startLine"]) is not int or type(p["endLine"]) is not int or
                p["startLine"] != next_line or p["endLine"] < next_line or p["endLine"] > len(lines)):
            raise ValueError("source line range gap or overlap")
        actual = "\n".join(lines[p["startLine"] - 1:p["endLine"]])
        if p["text"] != actual:
            raise ValueError("passage text differs from approved source")
        next_line = p["endLine"] + 1
    if next_line != len(lines) + 1:
        raise ValueError("inventory omits source lines")
    return inv_path, inv, rules, ids


def check_stage(run, channel, inv, rules, ids, inv_hash):
    folder = run / channel
    path = folder / "result.json"
    if not path.is_file():
        return {"channel": channel, "status": "blocked", "blockers": ["result.json missing"], "claims": []}
    try:
        result = read_json(path)
        required = {"channel", "status", "sourceSha256", "constraintsSha256",
                    "inventorySha256", "draftFile", "claims", "blockers"}
        if set(result) != required or result["channel"] != channel:
            raise ValueError("result keys or channel differ from contract")
        if result["status"] not in ("ready", "blocked"):
            raise ValueError("invalid result status")
        if (result["sourceSha256"] != inv["sourceSha256"] or
                result["constraintsSha256"] != inv["constraintsSha256"] or
                result["inventorySha256"] != inv_hash):
            raise ValueError("result belongs to another source/inventory")
        if not isinstance(result["claims"], list) or not string_list(result["blockers"]):
            raise ValueError("claims/blockers must be arrays")
        if result["status"] == "ready":
            if channel == "ad" and not approved_offer(rules.get("offer")):
                raise ValueError("ad requires approved offer, HTTP(S) destination and call to action")
            if result["draftFile"] != "draft.md" or not result["claims"] or result["blockers"]:
                raise ValueError("ready result lacks draft or claim list")
            draft = folder / "draft.md"
            if not draft.is_file() or not draft.read_text(encoding="utf-8").strip():
                raise ValueError("ready draft missing or empty")
        elif not result["blockers"] or result["draftFile"] is not None:
            raise ValueError("blocked result needs reasons and no draft")
        for claim in result["claims"]:
            if set(claim) != {"text", "passageIds", "constraintKeys", "status"}:
                raise ValueError("claim keys differ from contract")
            if not isinstance(claim["text"], str) or not claim["text"].strip():
                raise ValueError("empty claim")
            if claim["status"] not in ("supported", "needs-human-check", "unsupported"):
                raise ValueError("invalid claim status")
            if not string_list(claim["passageIds"]) or not string_list(claim["constraintKeys"]):
                raise ValueError("claim evidence pointers must be string arrays")
            if not set(claim["passageIds"]).issubset(ids):
                raise ValueError("unknown source passage ID")
            if not claim["passageIds"] and not claim["constraintKeys"]:
                raise ValueError("claim has no evidence pointer")
            if not set(claim["constraintKeys"]).issubset(rules):
                raise ValueError("unknown constraints key")
            if claim["status"] == "unsupported" and result["status"] == "ready":
                raise ValueError("unsupported claim in ready draft")
        return result
    except (OSError, ValueError, KeyError, TypeError, json.JSONDecodeError) as exc:
        return {"channel": channel, "status": "blocked", "blockers": [f"invalid result: {exc}"], "claims": []}


def main():
    if len(sys.argv) != 3:
        raise SystemExit("Usage: python join.py RUN_DIRECTORY NEW_JOIN_DIRECTORY")
    run, target = Path(sys.argv[1]).resolve(), Path(sys.argv[2]).resolve()
    if target.exists():
        raise SystemExit("join output already exists; use a new directory to preserve prior review")
    inv_path, inv, rules, ids = check_inventory(run)
    inv_hash = digest(inv_path)
    results = [check_stage(run, channel, inv, rules, ids, inv_hash) for channel in CHANNELS]
    target.mkdir(parents=True, exist_ok=False)
    map_lines = ["# Claim map for human review", "", f"Source SHA-256: {inv['sourceSha256']}",
                 f"Constraints SHA-256: {inv['constraintsSha256']}", f"Inventory SHA-256: {inv_hash}", ""]
    review_lines = ["# Review handoff", "", f"Reviewer: {rules['reviewer']}",
                    "No draft is approved, sent or published by this join.", ""]
    for result in results:
        channel = result["channel"]
        map_lines += [f"## {channel}", ""]
        review_lines += [f"## {channel}: {result['status']}", ""]
        for blocker in result["blockers"]:
            review_lines.append(f"- Blocker: {blocker}")
        for claim in result["claims"]:
            refs = ", ".join(claim["passageIds"] + claim["constraintKeys"])
            map_lines.append(f"- {claim['status']}: {claim['text']} — {refs}")
        map_lines.append("")
        review_lines += ["- Human must compare every claim and quote with the approved inputs.",
                         "- Human must check rights, voice, audience and any offer/destination.",
                         "- Human approval decision: PENDING", ""]
    (target / "claim-map.md").write_text("\n".join(map_lines), encoding="utf-8")
    (target / "review.md").write_text("\n".join(review_lines), encoding="utf-8")
    summary = {"sourceSha256": inv["sourceSha256"], "constraintsSha256": inv["constraintsSha256"],
               "inventorySha256": inv_hash, "channels": {r["channel"]: r["status"] for r in results},
               "humanApproval": "pending"}
    (target / "summary.json").write_text(json.dumps(summary, indent=2) + "\n", encoding="utf-8")
    print(json.dumps(summary))


if __name__ == "__main__":
    main()
```

### `repurpose-inputs/approved-source.template.md`

```markdown
# Replace with the owner's approved written source

This template is deliberately empty of publishable facts. Supply the exact approved transcript or article, retain line breaks for passage IDs, and record its owner and version in constraints.json. Do not invoke the pipeline on this template.
```

### `repurpose-inputs/constraints.template.json`

```json
{
  "sourceOwner": "REPLACE_WITH_ACTUAL_OWNER",
  "sourceVersion": "REPLACE_WITH_ACTUAL_VERSION",
  "approvedAt": "REPLACE_WITH_ACTUAL_APPROVAL_DATE",
  "adaptationRights": {"linkedin": false, "newsletter": false, "ad": false},
  "audience": "REPLACE_WITH_APPROVED_AUDIENCE",
  "permittedChannels": ["linkedin", "newsletter", "ad"],
  "reviewer": "REPLACE_WITH_ACTUAL_HUMAN_REVIEWER",
  "forbiddenClaims": [],
  "voiceRules": "REPLACE_WITH_APPROVED_VOICE_RULES",
  "offer": null
}
```

### `expected-output-tree.txt`

```markdown
STRUCTURE ONLY. No Claude run or model output exists in this packet.

<new-run>/
  inventory.json                 from source-inventory skill, or inventory-blocked.json
  linkedin/result.json           ready or blocked; draft.md only when ready
  newsletter/result.json         ready or blocked; draft.md only when ready
  ad/result.json                 ready or blocked; draft.md only when ready
<new-join>/
  claim-map.md                   generated by join.py from stage claims
  review.md                      all three statuses and PENDING human decisions
  summary.json                   hashes, statuses and humanApproval=pending
```

## ماخذ اور مزید مطالعہ

- [Claude Code skills ہدایت نامہ](https://code.claude.com/docs/en/skills) — پروجیکٹ skill راستے، frontmatter، کال، اجزا کا متبادل، الگ سیاق اور پس منظر رویہ۔ 19 اکتوبر 2026 کو جانچا؛ پوری ترکیب کا واحد نسخہ نہیں دیا۔
- [Claude Code subagents ہدایت نامہ](https://code.claude.com/docs/en/sub-agents) — عمومی ایجنٹ، سیاق اور اجازتیں۔ 19 اکتوبر 2026 کو جانچا۔
- [Claude Code اخراجات](https://code.claude.com/docs/en/costs), [ڈیٹا استعمال](https://code.claude.com/docs/en/data-usage) اور [اجازتیں](https://code.claude.com/docs/en/permissions) — اکاؤنٹ کے مطابق استعمال، ڈیٹا برتاؤ اور ٹول کنٹرول۔ 19 اکتوبر 2026 کو جانچا؛ اس عمل کا خرچ نہیں ماپا۔
- آزادانہ جانچا مقامی پیکٹ چار skills، پانچ JSON معاہدے، دو ان پٹ سانچے، آؤٹ پٹ درخت اور یکجائی اسکرپٹ دیتا ہے۔ 16 جانچوں میں صرف عارضی ساختی نمونے تھے؛ نہ تحریر پیدا ہوئی نہ حقائق کی منظوری۔
- [متعلقہ BIG CHANGE رہنما: متوازی دستاویز جائزہ](https://bigchange.ai/blog/claude-managed-agents-parallel-document-review-guide) دستاویز کے جائزے پر مبنی مختلف Claude طریقہ ہے، Code میں مقامی طور پر ماخذ کو تین چینلوں میں ڈھالنا نہیں۔

## Sources

- [Claude Code skills ہدایت نامہ](https://code.claude.com/docs/en/skills) — پروجیکٹ راستہ، کال، الگ سیاق اور پس منظر؛ HTTP 200 اور ہیش 2026-10-19 کو جانچے۔
- [Claude Code subagents ہدایت نامہ](https://code.claude.com/docs/en/sub-agents) — عمومی ایجنٹ اور اجازتیں؛ HTTP 200 اور ہیش 2026-10-19 کو جانچے۔
- [Claude Code اخراجات](https://code.claude.com/docs/en/costs) — اکاؤنٹ کے مطابق استعمال اور نشست رپورٹ؛ 2026-10-19 کو جانچا۔
- [Claude Code ڈیٹا استعمال](https://code.claude.com/docs/en/data-usage) — اکاؤنٹ اور ادارے کی ڈیٹا پالیسی؛ 2026-10-19 کو جانچی۔
- [Claude Code اجازتیں](https://code.claude.com/docs/en/permissions) — اصل ٹول رسائی skill کی تحریر سے باہر طے ہوتی ہے؛ 2026-10-19 کو جانچی۔
