Handbooks
अवधारणाएँ

आर्किटेक्चर

चार layers में ग्यारह packages, सख़्ती से एकतरफ़ा dependency दिशा, और वे boundaries जो deterministic आधे हिस्से को अकेले भी reusable बनाती हैं।

Layering

Packages की layering: entry points, capabilities, engines, foundation
LayerPackagesकाम
Entry pointscli, studioजो इंसान या container चलाता है
Capabilitiespipeline, renderer, skill, planner, patcher, resyncहरेक का एक काम, स्वतंत्र रूप से इस्तेमाल के लायक़
Enginesanalyzer, llmवे दो चीज़ें जिन पर बाक़ी सब बना है
FoundationcoreData model, config registry, utilities

Dependencies हमेशा सिर्फ़ नीचे की ओर इशारा करती हैं:

cli → pipeline / renderer / skill / planner / patcher / resync → analyzer / llm → core

तीन नियम जो इसे स्वस्थ रखते हैं

1. एकतरफ़ा dependencies, enforced

core कोई internal चीज़ import नहीं करता। कोई भी cli को import नहीं करता। Cycle या ऊपर की ओर import pnpm check:workspace को fail कर देता है, जो यह भी verify करता है कि हर package के TypeScript project references उसकी package.json dependencies का हूबहू आईना हों — कोई reference छूट जाए तो tsc -b ग़लत क्रम में build करता है, और root से build करना उसे छिपा देता है।

2. LLM isolation package boundary है, convention नहीं

सिर्फ़ llm, pipeline, planner और resync किसी model से बात कर सकते हैं, और वह भी सिर्फ़ ChatClient interface के ज़रिये:

interface ChatClient {
  readonly model: string;
  complete(prompt: string, options?: ChatOptions): Promise<ChatResult>;
}

analyzer, renderer, skill और patcher @handbooks/llm पर बिल्कुल निर्भर नहीं करते। वे पूरी तरह deterministic हैं और कहीं भी किसी LLM के बिना reusable हैं। इसीलिए render, skill, validate, apply और rollback CI में बेझिझक चल सकते हैं।

इसीलिए पूरा test suite offline चलता है: एक seam, एक mock।

3. Renderer की boundary एक type है

HandbookModel (core में परिभाषित) ही एकमात्र चीज़ है जिसे renderer जानता है। वह pipeline के internals कभी नहीं पढ़ता।

interface HandbookModel {
  title: string;
  lang: NarrateLang;
  skeleton: Skeleton;
  cards: Record<string, FileCard>;
  assignment: Assignment;
  organization: Organization;
  narration: Narration;
  registers: RegisterEntry[];
  provenance?: { commit?: string; generatedAt: string };
}

जो भी producer एक HandbookModel भर सकता है, उसे rendering, skill packaging और planning मुफ़्त मिल जाती है। अगर आप handbook किसी और तरीक़े से generate करना चाहते हैं, तो आपको बस यही पूरा contract संतुष्ट करना है।

Data flow

source tree
   │  analyzer — tree-sitter WASM, one adapter per language

phase1/graph.json · functions.csv · graph.dot · dropped-calls.json · scan-coverage.json
   │  pipeline 2a — cards (batched LLM, three-tier degradation, resumable)

phase2/cards/<rel>.json + _coverage.json
   │  pipeline 2b — skeleton synthesis (+ doctor loop) + file assignment

phase2/skeleton.yaml + assignment.json
   │  pipeline 2c — call-graph topological order + LLM grouping (flat fallback)

phase2/organization.yaml
   │  pipeline 3 — bottom-up narration + register extraction (content-hash cached)

phase3/narration.json + registers.json
   │  loadHandbookModel()

HandbookModel ──▶ renderer ──▶ handbook/  (md · html/ · handbook.html · agent/ · llms.txt)

                     └──▶ skill ──▶ SKILL.md + references/ (+ coverage.json)

Work directory का contract: हर phase सिर्फ़ अपने upstream artifacts पढ़ता है और सिर्फ़ अपने artifacts लिखता है — सब पढ़ते समय version field के साथ schema-validated। कोई भी phase अकेला दोबारा चलाया जा सकता है। Crash के बाद resume होता है — cards हर batch पर लिखे जाते हैं, narration content-hash cached है।

मानवीय artifact समझाता है; agent artifact पता बताता है

एक ही HandbookModel, और दो ऐसे outputs जिनके काम सचमुच अलग हैं — और यह बँटवारा packaging का ब्योरा नहीं, ख़ुद design है।

Markdown और HTML handbooks पढ़े जाने के लिए लिखे गए हैं: गद्य, क्रम, एक कथात्मक रीढ़। agent/ grep किए जाने के लिए लिखा गया है: "sendPayment कहाँ परिभाषित है" का जवाब symbols.tsv एक पंक्ति में दे देता है, जो कितना भी गद्य नहीं कर पाता।

पहले ये दोनों एक ही गद्य के दो रूप थे, और उसकी क़ीमत ठोस थी: agent index मानवीय index से 2.1× बड़ा निकला, जबकि उसमें एक भी symbol का स्थान नहीं था, क्योंकि उसका 42% मानवीय पृष्ठों से byte-दर-byte नक़ल किया गया model का गद्य था। अब agent वाला पक्ष facts ढोता है और हर file के लिए गद्य की एक कटी हुई पंक्ति; और जहाँ व्याख्या चाहिए, हर stage पृष्ठ उसकी नक़ल करने के बजाय मानवीय पृष्ठ से जोड़ देता है।

Analyzer के भीतर

हर language एक LanguageAdapter लागू करती है: discover, analyze, और वैकल्पिक रूप से statementSpans। हर grammar WebAssembly है, इसलिए installation कभी native code compile नहीं करती।

Adapters हर module पर दो passes चलाते हैं:

  1. Scan — declarations, imports, classes और methods, और हर function के facts: signature, line range, async होना, decorators, self/this attribute के reads और writes, typed parameters, और constructor assignments से सीखे गए attribute types।
  2. Resolve — हर call site एक typed edge बनती है: self_method, self_attr_method, param_method, internal_func, internal_constructor, boundary, boundary_constructor — या unresolved, जिसे graph builder एक category के साथ dropped-calls.json में quarantine करता है।

रखे गए graph में हमेशा सिर्फ़ resolved, named callees होते हैं। इसी से उसका हर edge भरोसे लायक़ बनता है।

यही नियम एक स्तर ऊपर, पूरी files पर भी लागू होता है। जिस file को adapter पढ़ नहीं सका, या जिसकी grammar ने throw कर दिया, या जो syntax errors के साथ parse हुई — वह अपने reason के साथ scan-coverage.json में दर्ज होती है, और पहली दो scannedFiles से बाहर रखी जाती हैं, ताकि कोई बाद का phase ऐसी file का वर्णन न कर सके जिसे parser ने कभी देखा ही नहीं।

Nav-pack graph से निकाली गई एक deterministic orientation summary है — directory rollups, entry-point candidates, fan-out, external subsystems। Skeleton synthesizer codebase का सिर्फ़ यही रूप देखता है, जिससे वह prompt छोटा और grounded रहता है।

Pipeline की quality machinery

तीन-tier card degradation (2a)। पूरा batch → अकेली file → oversized files के लिए per-function chunks। जो files फिर भी fail हों, उन्हें एक ईमानदार खाली card मिलता है और वे _coverage.json में listed होती हैं। Coverage construction से ही complete है; चूकें दिखती हैं, चुप नहीं रहतीं।

Actor–critic skeleton doctor (2b)। Actor ground-truth stats के विरुद्ध अधिकतम तीन structural बदलाव प्रस्तावित करता है; तीन role-played critics (engineer, architect, reader) समानांतर में review करते हैं; हर बचा हुआ बदलाव apply होने से पहले यांत्रिक रूप से दोबारा validate होता है; प्रभावित files दोबारा assign होती हैं। Loop convergence पर, या बिना प्रगति के दो rounds पर रुक जाता है। टूटा हुआ critic REJECT गिना जाता है — fail होता reviewer किसी बदलाव को कभी पास न कर दे।

हर जगह deterministic fallbacks (2c, 3)। Organization call-graph क्रम पर गिरती है। Narration stage description पर। Register extraction की विफलता खाली सूची देती है। Generation run degrade होता है; block नहीं होता।

Content-hash caches (3)। Stage और system की prose phase3/cache/ में cache होती है, जिसकी key है prompt version, language और पूरे prompt का hash — इसलिए re-runs और resyncs सिर्फ़ उसी की क़ीमत चुकाते हैं जो सचमुच बदला।

Concurrency और safety

  • हर work directory में एक run। generateHandbook और resyncHandbook एक ही re-entrant directory lock लेते हैं, इसलिए एक CLI run और एक Studio job एक ही artifacts पर writes आपस में गूँथ नहीं सकते।
  • Atomic writes। हर artifact temp file में लिखा जाता है, फिर rename होता है। Crash कभी आधी-लिखी file नहीं छोड़ता जिस पर अगला run अटक जाए।
  • Cooperative cancellation। Phases के बीच और हर batch checkpoint पर AbortSignal जाँचा जाता है, और हर LLM call में पिरोया जाता है ताकि in-flight requests abort हों। Abort हुआ run जो बचा चुका है वह रखता है, और कोई run manifest नहीं लिखता।

जानने लायक़ फ़ैसले

#फ़ैसलाक्यों
1WASM-only tree-sitterशून्य native builds; हर language के लिए एक ही loading path; version-locked grammars
2हाथ से लिखा fetch LLM clientOpenAI-compatible endpoints अलग-अलग होते हैं; explicit retry वाला पतला client SDK dependency से बेहतर है। Transport से ज़्यादा मायने interface seam रखती है
3एक pipeline, दो strategiesअलग-अलग बड़ी/छोटी pipelines adapters, critics, clients और renderers की नक़ल करतीं; एक strategy flag उस surface का लगभग 40% हटा देता है
4version के साथ zod-validated artifactsCorrupt या hand-edited artifacts boundary पर ही ज़ोर से fail होते हैं, बाद के phases में ज़हर नहीं फैलाते
5Cards में facts/prose का अलगावModel graph से निकली पूरी inventory पर टिप्पणी करता है। Prose खाली हो सकती है; facts ग़लत नहीं हो सकते
6Single-turn planner protocolहर endpoint पर चलता है, आसानी से mock होता है, और transcript जाँची जा सकती है। लागत — tokens दोबारा भेजना — planner के पैमाने पर स्वीकार्य है
7ESM + tsc -b, कोई bundler नहींLibraries type-checked dist/ और .d.ts ship करती हैं; composite references बिना किसी अतिरिक्त tooling के incremental builds देते हैं
8एक configuration registryFlags, env names, YAML keys और तीन generated documents एक ही table से निकलते हैं, इसलिए वे drift नहीं कर सकते

आगे

इस पृष्ठ पर