@toon-protocol/rig 4.5.0 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/factory-job-gate.ts","../src/factory-job-events.ts","../src/factory-job-plan.ts","../src/factory-job-execute.ts","../src/ci/runner.ts"],"sourcesContent":["/**\n * Factory job protocol — reproducible gate result per increment (#53).\n *\n * Decision 5 of toon-meta#262 splits trust into three layers: delivery (the\n * hashlock), the objective floor (this — \"did this increment pass the\n * repo's own gate?\"), and quality (reputation, out of scope here). Per\n * `FACTORY.md`, the pinned pipeline's gate is lint/typecheck/test/build run\n * before a PR is opened; this module is the pure, network-free shape for\n * recording exactly what ran, not a summary verdict.\n *\n * This proves conformance, not fitness for purpose — a gate-passing\n * increment can still completely miss the brief. Reputation is what\n * distinguishes those, never this.\n */\n\n/** One command the gate ran, and whether it passed. */\nexport interface GateCheck {\n /** e.g. \"lint\", \"typecheck\", \"test\", \"build\" — whatever actually ran,\n * never a canonical list (gates differ per repo and drift over time). */\n name: string;\n /** The literal command, e.g. \"eslint .\", so a buyer can reproduce it verbatim. */\n command: string;\n pass: boolean;\n}\n\n/**\n * A reproducible gate result: enough for a buyer to re-run the gate\n * themselves and get the same answer. Omitting the commit or the command\n * set turns this from a reproducible result into a provider's unverifiable\n * claim of having done well — both are required, not advisory.\n */\nexport interface GateResult {\n /** The commit this gate ran against. */\n commit: string;\n /** Toolchain versions relevant to reproducing the result, e.g. `{ node: \"20.11.0\", pnpm: \"9.1.0\" }`. */\n toolchain: Record<string, string>;\n checks: GateCheck[];\n}\n\n/** Overall pass/fail — every recorded check passed. A failing gate is not a protocol violation (it is still published, just visibly failing) — this is only the aggregate the `gate` tag and reputation's gate-pass rate read. */\nexport function gatePassed(gate: GateResult): boolean {\n return gate.checks.every((check) => check.pass);\n}\n","/**\n * Factory job protocol — pure NIP-90 event builders/parser (#52).\n *\n * Wire format per toon-meta's `docs/factory-job-protocol.md`\n * (toon-meta#263), fixed 2026-08-03: kind:5097 job request, kind:6097 job\n * result, kind:7000 job feedback (quote / increment offer / narration,\n * disambiguated by the `status` tag). Mirrors nip34-events.ts: every builder\n * returns an UnsignedEvent — the caller signs and publishes (e.g. through a\n * Publisher, publisher.ts) elsewhere. No crypto, no network, no payment.\n */\n\nimport type { UnsignedEvent } from './nip34-events.js';\nimport type { IncrementSpec } from './factory-job-plan.js';\nimport { gatePassed, type GateResult } from './factory-job-gate.js';\n\n// ---------------------------------------------------------------------------\n// Kinds\n// ---------------------------------------------------------------------------\n\n/** Factory job request (the brief). */\nexport const FACTORY_JOB_REQUEST_KIND = 5097;\n/** Factory job result — terminal state (completed/abandoned-*). */\nexport const FACTORY_JOB_RESULT_KIND = 6097;\n/** Factory job feedback — quote, increment offer, or free narration. */\nexport const FACTORY_JOB_FEEDBACK_KIND = 7000;\n\n/** The three §5.2 terminal outcomes — decision 8 has no fourth. */\nexport type FactoryJobOutcome =\n | 'completed'\n | 'abandoned-provider'\n | 'abandoned-buyer';\n\n// ---------------------------------------------------------------------------\n// kind:5097 — job request\n// ---------------------------------------------------------------------------\n\n/** A parsed kind:5097 job request (§2). */\nexport interface FactoryJobRequest {\n /** The requesting kind:5097 event's id. */\n requestEventId: string;\n /** The requesting kind:5097 event's pubkey (who to reply/bill). */\n buyerPubkey: string;\n /** The brief — free text or a chained milestone reference (§2.3). */\n brief: string;\n /** Maximum the buyer will pay across the whole job (micro-USDC, §2.1). */\n bidMicroUsdc: string;\n /** `param repo` — target repo, e.g. `toon-protocol/buzz`. */\n repo?: string;\n /** `param target` — the ticket/issue this job resolves. */\n target?: string;\n /** `param constraints` — gate requirements / scope limits. */\n constraints?: string;\n /** `output` — expected deliverable MIME type. */\n outputMime?: string;\n}\n\n/** Minimal shape of a relay event this module reads/builds — no signing. */\nexport interface RelayEvent {\n id: string;\n pubkey: string;\n tags: string[][];\n content: string;\n}\n\n/**\n * Parse a kind:5097 job request event's tags into a {@link FactoryJobRequest}.\n * Throws if the required `i` (brief) or `bid` tag is missing — both are\n * REQUIRED per §2.1.\n */\nexport function parseFactoryJobRequest(event: RelayEvent): FactoryJobRequest {\n const briefTag = event.tags.find((t) => t[0] === 'i' && t[2] === 'text');\n if (!briefTag?.[1]) {\n throw new Error(\n `kind:${FACTORY_JOB_REQUEST_KIND} event ${event.id} is missing its required brief (\"i\", \"<brief>\", \"text\") tag`\n );\n }\n const bidTag = event.tags.find((t) => t[0] === 'bid');\n if (!bidTag?.[1]) {\n throw new Error(\n `kind:${FACTORY_JOB_REQUEST_KIND} event ${event.id} is missing its required \"bid\" tag`\n );\n }\n\n const params = event.tags.filter((t) => t[0] === 'param');\n const paramValue = (name: string): string | undefined =>\n params.find((t) => t[1] === name)?.[2];\n\n const repo = paramValue('repo');\n const target = paramValue('target');\n const constraints = paramValue('constraints');\n const outputMime = event.tags.find((t) => t[0] === 'output')?.[1];\n\n return {\n requestEventId: event.id,\n buyerPubkey: event.pubkey,\n brief: briefTag[1],\n bidMicroUsdc: bidTag[1],\n ...(repo !== undefined ? { repo } : {}),\n ...(target !== undefined ? { target } : {}),\n ...(constraints !== undefined ? { constraints } : {}),\n ...(outputMime !== undefined ? { outputMime } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"quote\" — the RFQ reply (§3)\n// ---------------------------------------------------------------------------\n\n/**\n * Build the `kind:7000 status:\"quote\"` reply: the proposed increment\n * schedule, committing nothing (§3.3 — there is no accept message; payment\n * of increment 1 is acceptance).\n */\nexport function buildQuoteEvent(\n job: FactoryJobRequest,\n schedule: IncrementSpec[]\n): UnsignedEvent {\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: JSON.stringify({\n increments: schedule.map((s) => ({\n n: s.n,\n of: s.of,\n milestone: s.milestone,\n priceUsdc: s.priceUsdc,\n })),\n }),\n tags: [\n ['e', job.requestEventId, '', 'root'],\n ['p', job.buyerPubkey],\n ['status', 'quote'],\n ],\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"partial\" — the increment offer (§4)\n// ---------------------------------------------------------------------------\n\n/** Where the encrypted increment artifact lives, and its hashlock condition. */\nexport interface EncryptedArtifactRef {\n /** Arweave tx id the ciphertext was uploaded to. */\n arweaveTxId: string;\n /** sha256 of the ciphertext, hex — lets the buyer detect a bad fetch. */\n ciphertextSha256: string;\n /** `sha256(key)` hex, where `key` decrypts the artifact (§4.2 — the join). */\n conditionHex: string;\n}\n\nexport interface BuildIncrementOfferOptions {\n job: FactoryJobRequest;\n /** The quote event's id for increment 1, else the previous offer's id. */\n parentEventId: string;\n increment: IncrementSpec;\n artifact: EncryptedArtifactRef;\n /**\n * The reproducible gate result for this increment's work (#53 — the\n * objective floor), when one ran. Omitted for milestones with nothing to\n * gate (e.g. `plan` — there is no code to lint against a brief).\n */\n gate?: GateResult;\n}\n\n/** A gate result missing its commit or check list cannot be reproduced by the buyer — reject it here rather than publish an unverifiable claim. */\nfunction assertReproducible(gate: GateResult): void {\n if (gate.checks.length === 0) {\n throw new Error(\n 'gate result must record at least one check for the offer to be reproducible'\n );\n }\n if (!gate.commit) {\n throw new Error(\n 'gate result must record the commit it ran against for the offer to be reproducible'\n );\n }\n}\n\n/**\n * Build the `kind:7000 status:\"partial\"` increment offer — published once\n * the provider has done that increment's work and uploaded the encrypted\n * artifact. This is the join between the relay plane and the connector\n * plane (§4.2): `condition` here MUST equal the paying PREPARE's\n * `executionCondition`, byte for byte.\n *\n * When `gate` is supplied, the offer also carries a `[\"gate\", \"pass\"|\"fail\"]`\n * tag (cheap to scan for a gate-pass rate, decision 8) and the full\n * reproducible result in `content` — visible to the buyer before they pay,\n * per the issue: a gate-failing increment is still offered, never hidden.\n */\nexport function buildIncrementOfferEvent(\n options: BuildIncrementOfferOptions\n): UnsignedEvent {\n const { job, parentEventId, increment, artifact, gate } = options;\n if (gate) assertReproducible(gate);\n\n const tags: string[][] = [\n ['e', job.requestEventId, '', 'root'],\n ['e', parentEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['status', 'partial'],\n ['increment', String(increment.n), String(increment.of)],\n ['i', artifact.arweaveTxId, 'url'],\n ['i', artifact.ciphertextSha256, 'text', '', 'hash'],\n ['amount', increment.priceUsdc, 'usdc'],\n ['condition', artifact.conditionHex],\n ];\n if (gate) tags.push(['gate', gatePassed(gate) ? 'pass' : 'fail']);\n\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: gate ? JSON.stringify({ gate }) : '',\n tags,\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"processing\" — free narration (§6)\n// ---------------------------------------------------------------------------\n\nexport interface BuildNarrationOptions {\n job: FactoryJobRequest;\n /** The prior event in the thread (quote, offer, or narration). */\n parentEventId: string;\n message: string;\n}\n\n/**\n * Build a free narration event — no artifact, no `amount`/`condition`, MUST\n * NOT be paid against (§6). This is what makes a provider look alive between\n * increments.\n */\nexport function buildNarrationEvent(\n options: BuildNarrationOptions\n): UnsignedEvent {\n const { job, parentEventId, message } = options;\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: message,\n tags: [\n ['e', job.requestEventId, '', 'root'],\n ['e', parentEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['status', 'processing'],\n ],\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:6097 — job result (§5)\n// ---------------------------------------------------------------------------\n\nexport interface BuildResultEventOptions {\n job: FactoryJobRequest;\n /** The original kind:5097 request event, verbatim (the `request` tag). */\n requestEvent: RelayEvent;\n /** The last kind:7000 event in the thread (offer or narration). */\n lastEventId: string;\n outcome: FactoryJobOutcome;\n /** How far the job got — `n-reached == of` only when outcome is completed. */\n reachedIncrement: number;\n totalIncrements: number;\n /** Required (and only meaningful) when outcome is `\"completed\"` (§5.2). */\n finalArtifact?: { arweaveTxId: string };\n}\n\n/**\n * Build the terminal `kind:6097` job result. Reputation (decision 8) is\n * computed from exactly this event and nothing else.\n */\nexport function buildResultEvent(\n options: BuildResultEventOptions\n): UnsignedEvent {\n const {\n job,\n requestEvent,\n lastEventId,\n outcome,\n reachedIncrement,\n totalIncrements,\n finalArtifact,\n } = options;\n\n const tags: string[][] = [\n ['e', job.requestEventId, '', 'root'],\n ['e', lastEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['request', JSON.stringify(requestEvent)],\n ['outcome', outcome],\n ['increment', String(reachedIncrement), String(totalIncrements)],\n ];\n if (outcome === 'completed' && finalArtifact) {\n tags.push(['i', finalArtifact.arweaveTxId, 'url']);\n }\n\n return {\n kind: FACTORY_JOB_RESULT_KIND,\n content: '',\n tags,\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n","/**\n * Factory job protocol — increment schedule planner (#52).\n *\n * `planFactoryJob` is the network-free half of the adapter (mirrors\n * `planPush`'s split from `executePush` in push.ts): given a job's `bid` and\n * the ticket list the factory's plan phase produced, it prices the\n * milestone boundaries decision 6 of toon-meta#262 fixed as increments —\n * plan → implement ×N tickets → review — and returns the schedule that\n * becomes the `kind:7000 status:\"quote\"` content (§3.2 of\n * `docs/factory-job-protocol.md`).\n *\n * Per the issue gotcha, `implement` must be many increments, not one: this\n * always emits one increment per ticket rather than a single lump-sum\n * implement increment, using the factory's existing parallel per-issue\n * fan-out (`.sandcastle/main.ts`'s Plan phase output — `{issues: [{id,\n * title, branch}]}`) as the boundary.\n */\n\n/** One ticket the factory's plan phase fanned the implement milestone into. */\nexport interface FactoryTicket {\n id: string;\n title: string;\n branch: string;\n}\n\nexport type FactoryMilestone = 'plan' | 'implement' | 'review';\n\n/** One priced increment in the schedule (§3.2's `increments[]` shape). */\nexport interface IncrementSpec {\n n: number;\n of: number;\n milestone: FactoryMilestone;\n /** Set only for `milestone: \"implement\"` increments. */\n ticket?: FactoryTicket;\n priceUsdc: string;\n}\n\n/** Plan milestone's share of the bid, in basis points. */\nconst PLAN_SHARE_BPS = 1000n; // 10%\n/** Review milestone's share of the bid, in basis points. */\nconst REVIEW_SHARE_BPS = 1000n; // 10%\nconst BPS_DENOM = 10000n;\n\n/**\n * Build the increment schedule for a job, given the tickets its plan phase\n * produced. Plan and review each get a fixed 10% of the bid; the remaining\n * 80% (\"implement\") splits evenly across tickets, with the integer-division\n * remainder distributed one micro-USDC at a time to the earliest tickets —\n * so `sum(priceUsdc) === bid` exactly (§3.2 only requires `<=`, but an exact\n * match avoids \"where did the leftover micro-USDC go\" questions).\n *\n * Per decision 7, this schedule is agreed before any paid work starts and is\n * never repriced — a job that runs larger than `tickets` implied is a\n * lesson, not a renegotiation.\n *\n * Throws if `tickets` is empty (a job with no implementation work has no\n * increments to schedule) or `bidMicroUsdc` is not a positive integer.\n */\nexport function planFactoryJob(\n bidMicroUsdc: string,\n tickets: FactoryTicket[]\n): IncrementSpec[] {\n if (tickets.length === 0) {\n throw new Error(\n 'planFactoryJob requires at least one implementation ticket from the plan phase'\n );\n }\n\n let bid: bigint;\n try {\n bid = BigInt(bidMicroUsdc);\n } catch {\n throw new Error(`bidMicroUsdc must be an integer string, got ${bidMicroUsdc}`);\n }\n if (bid <= 0n) {\n throw new Error(`bidMicroUsdc must be positive, got ${bidMicroUsdc}`);\n }\n\n const planPrice = (bid * PLAN_SHARE_BPS) / BPS_DENOM;\n const reviewPrice = (bid * REVIEW_SHARE_BPS) / BPS_DENOM;\n const implementTotal = bid - planPrice - reviewPrice;\n\n const ticketCount = BigInt(tickets.length);\n const base = implementTotal / ticketCount;\n const remainder = implementTotal % ticketCount;\n\n const total = tickets.length + 2;\n const schedule: IncrementSpec[] = [\n { n: 1, of: total, milestone: 'plan', priceUsdc: planPrice.toString() },\n ];\n\n tickets.forEach((ticket, i) => {\n const price = base + (BigInt(i) < remainder ? 1n : 0n);\n schedule.push({\n n: i + 2,\n of: total,\n milestone: 'implement',\n ticket,\n priceUsdc: price.toString(),\n });\n });\n\n schedule.push({\n n: total,\n of: total,\n milestone: 'review',\n priceUsdc: reviewPrice.toString(),\n });\n\n return schedule;\n}\n","/**\n * executeFactoryJob — the adapter's imperative shell (#52): runs the\n * factory's own milestones (`.sandcastle/main.ts`'s plan → per-ticket\n * implement+review → merge phases) through `hooks`, and turns each\n * milestone boundary into a paid increment via `publisher` (existing,\n * publisher.ts — upload + event publish) and `delivery` (new,\n * factory-job-delivery.ts — encrypt + payment wait).\n *\n * Mirrors `executePush`'s split from `planPush`: `planFactoryJob` is the\n * network-free planning half; this is the half that spends money.\n *\n * Decision 6 (toon-meta#262): work happens BEFORE the packet, so every\n * increment's artifact is produced and uploaded before that increment is\n * offered for payment. Decision 7: the schedule is fixed once the quote is\n * published — this never reprices mid-job. Per the issue: \"Stop on\n * non-payment. One unpaid increment and work halts\" — the symmetric\n * protection that neither side is ever exposed for more than one increment.\n */\n\nimport type { Publisher } from './publisher.js';\nimport type { JobDeliveryPort } from './factory-job-delivery.js';\nimport {\n buildIncrementOfferEvent,\n buildQuoteEvent,\n buildResultEvent,\n type FactoryJobOutcome,\n type FactoryJobRequest,\n type RelayEvent,\n} from './factory-job-events.js';\nimport type { GateResult } from './factory-job-gate.js';\nimport {\n planFactoryJob,\n type FactoryTicket,\n type IncrementSpec,\n} from './factory-job-plan.js';\n\n/** One milestone's deliverable, plus the gate result that ran against it, when one did. */\nexport interface FactoryJobWork {\n artifact: Uint8Array;\n /**\n * The reproducible gate result (#53) for this milestone's work, per\n * `FACTORY.md`'s pinned pipeline (lint/typecheck/test/build). Omitted for\n * `plan` — there is no code to gate against a brief.\n */\n gate?: GateResult;\n}\n\n/** Runs one factory milestone and returns its deliverable, plus that milestone's gate result. */\nexport interface FactoryJobHooks {\n /**\n * Runs the factory's plan phase for `job` (mirrors `.sandcastle/main.ts`'s\n * Phase 1): returns the per-ticket fan-out the implement milestone splits\n * into, plus the plan document itself as increment 1's artifact. No gate\n * result — planning produces no code to lint/typecheck/test/build.\n */\n plan(\n job: FactoryJobRequest\n ): Promise<{ tickets: FactoryTicket[]; artifact: Uint8Array }>;\n /**\n * Runs one ticket's implement sandbox pipeline (mirrors Phase 2's\n * per-issue `sandbox.run`, which ends in the gate per `FACTORY.md`) and\n * returns its deliverable (e.g. the ticket's diff) plus that run's gate\n * result.\n */\n implement(ticket: FactoryTicket): Promise<FactoryJobWork>;\n /**\n * Runs the factory's merge/review phase over every completed ticket\n * (mirrors Phase 3, which re-runs the gate per `merge-prompt.md`) and\n * returns the final deliverable plus that run's gate result.\n */\n review(tickets: FactoryTicket[]): Promise<FactoryJobWork>;\n}\n\nexport interface ExecuteFactoryJobOptions {\n job: FactoryJobRequest;\n /** The original kind:5097 request event, verbatim — carried in the result's `request` tag. */\n requestEvent: RelayEvent;\n hooks: FactoryJobHooks;\n publisher: Publisher;\n delivery: JobDeliveryPort;\n relayUrls: string[];\n}\n\nexport interface FactoryJobExecution {\n outcome: FactoryJobOutcome;\n /** The last increment successfully paid for (0 if the buyer never paid increment 1). */\n reachedIncrement: number;\n totalIncrements: number;\n /** The published kind:6097 result event's id. */\n resultEventId: string;\n}\n\nasync function workFor(\n spec: IncrementSpec,\n tickets: FactoryTicket[],\n planArtifact: Uint8Array,\n hooks: FactoryJobHooks\n): Promise<FactoryJobWork> {\n if (spec.milestone === 'plan') return { artifact: planArtifact };\n if (spec.milestone === 'implement') {\n if (!spec.ticket) {\n throw new Error(\n `internal: implement increment ${spec.n} is missing its ticket`\n );\n }\n return hooks.implement(spec.ticket);\n }\n return hooks.review(tickets);\n}\n\n/**\n * Run a job end to end: plan → quote → one increment per milestone boundary\n * (encrypt, upload, offer, wait for payment) → terminal kind:6097 result.\n * Halts at the first unpaid increment (`abandoned-buyer`) or the first\n * hook failure once work is underway (`abandoned-provider`) — see §5.2 of\n * `docs/factory-job-protocol.md` for the three terminal outcomes.\n *\n * Throws if `hooks.plan` itself fails — with no quote ever published, there\n * is no prior kind:7000 event for a result's `reply` e-tag to reference, so\n * no protocol event can be honestly emitted for that failure.\n */\nexport async function executeFactoryJob(\n options: ExecuteFactoryJobOptions\n): Promise<FactoryJobExecution> {\n const { job, requestEvent, hooks, publisher, delivery, relayUrls } =\n options;\n\n if (!publisher.uploadBlob) {\n throw new Error(\n 'the active publisher cannot upload an encrypted increment artifact ' +\n '(uploadBlob unavailable) — executeFactoryJob requires the standalone publisher'\n );\n }\n const uploadBlob = publisher.uploadBlob.bind(publisher);\n\n const { tickets, artifact: planArtifact } = await hooks.plan(job);\n const schedule = planFactoryJob(job.bidMicroUsdc, tickets);\n const totalIncrements = schedule.length;\n\n const quoteEvent = buildQuoteEvent(job, schedule);\n const quoteReceipt = await publisher.publishEvent(quoteEvent, relayUrls);\n\n let parentEventId = quoteReceipt.eventId;\n let reachedIncrement = 0;\n let finalArtifactTxId: string | undefined;\n\n const haltWith = async (\n outcome: FactoryJobOutcome\n ): Promise<FactoryJobExecution> => {\n const resultEvent = buildResultEvent({\n job,\n requestEvent,\n lastEventId: parentEventId,\n outcome,\n reachedIncrement,\n totalIncrements,\n ...(outcome === 'completed' && finalArtifactTxId\n ? { finalArtifact: { arweaveTxId: finalArtifactTxId } }\n : {}),\n });\n const resultReceipt = await publisher.publishEvent(\n resultEvent,\n relayUrls\n );\n return {\n outcome,\n reachedIncrement,\n totalIncrements,\n resultEventId: resultReceipt.eventId,\n };\n };\n\n for (const spec of schedule) {\n let work: FactoryJobWork;\n try {\n work = await workFor(spec, tickets, planArtifact, hooks);\n } catch {\n // Provider-side failure mid-job: stop where we are, no renegotiation.\n return haltWith('abandoned-provider');\n }\n\n const encrypted = await delivery.encryptArtifact(work.artifact);\n const uploadReceipt = await uploadBlob({\n body: Buffer.from(encrypted.ciphertext),\n contentType: 'application/octet-stream',\n ...(job.repo ? { repoId: job.repo } : {}),\n });\n\n const offerEvent = buildIncrementOfferEvent({\n job,\n parentEventId,\n increment: spec,\n artifact: {\n arweaveTxId: uploadReceipt.txId,\n ciphertextSha256: encrypted.ciphertextSha256,\n conditionHex: encrypted.conditionHex,\n },\n ...(work.gate ? { gate: work.gate } : {}),\n });\n const offerReceipt = await publisher.publishEvent(offerEvent, relayUrls);\n\n const paid = await delivery.waitForPayment({\n offerEventId: offerReceipt.eventId,\n conditionHex: encrypted.conditionHex,\n priceUsdc: spec.priceUsdc,\n });\n if (!paid) {\n parentEventId = offerReceipt.eventId;\n return haltWith('abandoned-buyer');\n }\n\n reachedIncrement = spec.n;\n parentEventId = offerReceipt.eventId;\n if (spec.milestone === 'review') finalArtifactTxId = uploadReceipt.txId;\n }\n\n return haltWith('completed');\n}\n","/**\n * The Runner seam (rig#125) — the ONE new interface the relay-native CI\n * layer introduces. The coordinator (./coordinator.ts) materializes a commit,\n * picks a workflow, decides the secret set, and hands all of that to a\n * Runner; the Runner executes the jobs and reports per-job conclusions, exit\n * codes, timings, logs and artifact files. Nothing about relays, payments,\n * or NIP-C1 events crosses this boundary in either direction.\n *\n * Two implementations exist today:\n *\n * - {@link FakeRunner} (here): scripted outcomes for coordinator tests.\n * - `ActRunner` (./act-runner.ts): shells out to `act` against the host\n * Docker daemon — the first-slice \"coordinator is its own runner\" shape\n * ngit's reference coordinator also uses.\n *\n * A later TOON-lease runner (rent a workload from a TOON Network provider,\n * blocked on TOON_Network Milestone 1) is a third class behind the SAME\n * interface; that is the whole point of the seam.\n */\n\nimport type { CiConclusion, CiTriggerContext } from './nip-c1-events.js';\n\n/** One artifact file a job produced (one entry per FILE, never an archive). */\nexport interface RunnerArtifact {\n /** Absolute path of the file on the coordinator host. */\n path: string;\n /** The file's path within the artifact (mirrors upload-artifact). */\n filename: string;\n /** The artifact name grouping related files. */\n name: string;\n}\n\n/** The outcome of one job within a run. */\nexport interface RunnerJobResult {\n /** Job id as declared in the workflow file. */\n jobId: string;\n /** Human-readable job name, when the workflow declares one. */\n name?: string;\n conclusion: CiConclusion;\n exitCode?: number;\n /** Unix seconds. */\n startedAt: number;\n /** Unix seconds. */\n finishedAt: number;\n /** The job's complete log output. */\n log: string;\n artifacts: RunnerArtifact[];\n}\n\n/** The outcome of one workflow run. */\nexport interface RunnerRunResult {\n /** Combined conclusion — the execution backend's own verdict, passed through. */\n conclusion: CiConclusion;\n jobs: RunnerJobResult[];\n startedAt: number;\n finishedAt: number;\n}\n\n/** Everything a Runner needs to execute one workflow once. */\nexport interface RunnerRequest {\n /** A real git repository, checked out at `trigger.commit`. */\n checkoutDir: string;\n /** Workflow file path relative to `checkoutDir`, plus its content SHA-256. */\n workflow: { path: string; sha256: string };\n trigger: CiTriggerContext;\n /** Secrets to inject; `{}` for runs the coordinator did not authorize secrets for. */\n secrets: Record<string, string>;\n /** Extra environment for the jobs. */\n env?: Record<string, string>;\n /** Wall-clock budget for the whole run; exceeding it concludes `timed_out`. */\n timeoutMs: number;\n /** Streaming log sink (per job). */\n onLog?: (jobId: string, chunk: string) => void;\n /** Cancels the run (concludes `cancelled`). */\n signal?: AbortSignal;\n}\n\n/** Executes one workflow run. See the module header. */\nexport interface Runner {\n /** Runner family, as advertised in the NIP-C1 `W` tag. */\n readonly family: 'act';\n /** Accepted selectors for the family (`R` = `act:<selector>`). */\n readonly selectors: string[];\n run(request: RunnerRequest): Promise<RunnerRunResult>;\n}\n\n// ---------------------------------------------------------------------------\n// FakeRunner — scripted outcomes for tests\n// ---------------------------------------------------------------------------\n\nexport type FakeRunnerScript = (\n request: RunnerRequest\n) => RunnerRunResult | Promise<RunnerRunResult>;\n\n/** The default script: one successful `build` job with a short log. */\nexport function defaultFakeRunResult(request: RunnerRequest): RunnerRunResult {\n const now = Math.floor(Date.now() / 1000);\n return {\n conclusion: 'success',\n startedAt: now,\n finishedAt: now,\n jobs: [\n {\n jobId: 'build',\n name: 'build',\n conclusion: 'success',\n exitCode: 0,\n startedAt: now,\n finishedAt: now,\n log: `fake runner: ran ${request.workflow.path} at ${request.trigger.commit}\\n`,\n artifacts: [],\n },\n ],\n };\n}\n\n/**\n * A Runner that returns scripted outcomes and records every request, so a\n * coordinator test can assert on what was asked (secrets, timeout, commit)\n * and drive the publish sequence from a chosen result. Never touches Docker.\n */\nexport class FakeRunner implements Runner {\n readonly family = 'act' as const;\n readonly selectors = ['ubuntu-latest'];\n /** Every request, in call order. */\n readonly requests: RunnerRequest[] = [];\n\n constructor(\n private readonly script: FakeRunnerScript = defaultFakeRunResult\n ) {}\n\n async run(request: RunnerRequest): Promise<RunnerRunResult> {\n this.requests.push(request);\n const result = await this.script(request);\n if (request.onLog) {\n for (const job of result.jobs) request.onLog(job.jobId, job.log);\n }\n return result;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/factory-job-gate.ts","../src/factory-job-events.ts","../src/factory-job-plan.ts","../src/factory-job-execute.ts","../src/ci/runner.ts","../src/nip34-fixtures/ngit-announcement-wyrd.ts","../src/nip34-fixtures/ngit-state-ngit.ts","../src/nip34-fixtures/ngit-comment-thread-hydrusui.ts","../src/nip34-fixtures/ngit-status-ngit.ts","../src/nip34-fixtures/index.ts"],"sourcesContent":["/**\n * Factory job protocol — reproducible gate result per increment (#53).\n *\n * Decision 5 of toon-meta#262 splits trust into three layers: delivery (the\n * hashlock), the objective floor (this — \"did this increment pass the\n * repo's own gate?\"), and quality (reputation, out of scope here). Per\n * `FACTORY.md`, the pinned pipeline's gate is lint/typecheck/test/build run\n * before a PR is opened; this module is the pure, network-free shape for\n * recording exactly what ran, not a summary verdict.\n *\n * This proves conformance, not fitness for purpose — a gate-passing\n * increment can still completely miss the brief. Reputation is what\n * distinguishes those, never this.\n */\n\n/** One command the gate ran, and whether it passed. */\nexport interface GateCheck {\n /** e.g. \"lint\", \"typecheck\", \"test\", \"build\" — whatever actually ran,\n * never a canonical list (gates differ per repo and drift over time). */\n name: string;\n /** The literal command, e.g. \"eslint .\", so a buyer can reproduce it verbatim. */\n command: string;\n pass: boolean;\n}\n\n/**\n * A reproducible gate result: enough for a buyer to re-run the gate\n * themselves and get the same answer. Omitting the commit or the command\n * set turns this from a reproducible result into a provider's unverifiable\n * claim of having done well — both are required, not advisory.\n */\nexport interface GateResult {\n /** The commit this gate ran against. */\n commit: string;\n /** Toolchain versions relevant to reproducing the result, e.g. `{ node: \"20.11.0\", pnpm: \"9.1.0\" }`. */\n toolchain: Record<string, string>;\n checks: GateCheck[];\n}\n\n/** Overall pass/fail — every recorded check passed. A failing gate is not a protocol violation (it is still published, just visibly failing) — this is only the aggregate the `gate` tag and reputation's gate-pass rate read. */\nexport function gatePassed(gate: GateResult): boolean {\n return gate.checks.every((check) => check.pass);\n}\n","/**\n * Factory job protocol — pure NIP-90 event builders/parser (#52).\n *\n * Wire format per toon-meta's `docs/factory-job-protocol.md`\n * (toon-meta#263), fixed 2026-08-03: kind:5097 job request, kind:6097 job\n * result, kind:7000 job feedback (quote / increment offer / narration,\n * disambiguated by the `status` tag). Mirrors nip34-events.ts: every builder\n * returns an UnsignedEvent — the caller signs and publishes (e.g. through a\n * Publisher, publisher.ts) elsewhere. No crypto, no network, no payment.\n */\n\nimport type { UnsignedEvent } from './nip34-events.js';\nimport type { IncrementSpec } from './factory-job-plan.js';\nimport { gatePassed, type GateResult } from './factory-job-gate.js';\n\n// ---------------------------------------------------------------------------\n// Kinds\n// ---------------------------------------------------------------------------\n\n/** Factory job request (the brief). */\nexport const FACTORY_JOB_REQUEST_KIND = 5097;\n/** Factory job result — terminal state (completed/abandoned-*). */\nexport const FACTORY_JOB_RESULT_KIND = 6097;\n/** Factory job feedback — quote, increment offer, or free narration. */\nexport const FACTORY_JOB_FEEDBACK_KIND = 7000;\n\n/** The three §5.2 terminal outcomes — decision 8 has no fourth. */\nexport type FactoryJobOutcome =\n | 'completed'\n | 'abandoned-provider'\n | 'abandoned-buyer';\n\n// ---------------------------------------------------------------------------\n// kind:5097 — job request\n// ---------------------------------------------------------------------------\n\n/** A parsed kind:5097 job request (§2). */\nexport interface FactoryJobRequest {\n /** The requesting kind:5097 event's id. */\n requestEventId: string;\n /** The requesting kind:5097 event's pubkey (who to reply/bill). */\n buyerPubkey: string;\n /** The brief — free text or a chained milestone reference (§2.3). */\n brief: string;\n /** Maximum the buyer will pay across the whole job (micro-USDC, §2.1). */\n bidMicroUsdc: string;\n /** `param repo` — target repo, e.g. `toon-protocol/buzz`. */\n repo?: string;\n /** `param target` — the ticket/issue this job resolves. */\n target?: string;\n /** `param constraints` — gate requirements / scope limits. */\n constraints?: string;\n /** `output` — expected deliverable MIME type. */\n outputMime?: string;\n}\n\n/** Minimal shape of a relay event this module reads/builds — no signing. */\nexport interface RelayEvent {\n id: string;\n pubkey: string;\n tags: string[][];\n content: string;\n}\n\n/**\n * Parse a kind:5097 job request event's tags into a {@link FactoryJobRequest}.\n * Throws if the required `i` (brief) or `bid` tag is missing — both are\n * REQUIRED per §2.1.\n */\nexport function parseFactoryJobRequest(event: RelayEvent): FactoryJobRequest {\n const briefTag = event.tags.find((t) => t[0] === 'i' && t[2] === 'text');\n if (!briefTag?.[1]) {\n throw new Error(\n `kind:${FACTORY_JOB_REQUEST_KIND} event ${event.id} is missing its required brief (\"i\", \"<brief>\", \"text\") tag`\n );\n }\n const bidTag = event.tags.find((t) => t[0] === 'bid');\n if (!bidTag?.[1]) {\n throw new Error(\n `kind:${FACTORY_JOB_REQUEST_KIND} event ${event.id} is missing its required \"bid\" tag`\n );\n }\n\n const params = event.tags.filter((t) => t[0] === 'param');\n const paramValue = (name: string): string | undefined =>\n params.find((t) => t[1] === name)?.[2];\n\n const repo = paramValue('repo');\n const target = paramValue('target');\n const constraints = paramValue('constraints');\n const outputMime = event.tags.find((t) => t[0] === 'output')?.[1];\n\n return {\n requestEventId: event.id,\n buyerPubkey: event.pubkey,\n brief: briefTag[1],\n bidMicroUsdc: bidTag[1],\n ...(repo !== undefined ? { repo } : {}),\n ...(target !== undefined ? { target } : {}),\n ...(constraints !== undefined ? { constraints } : {}),\n ...(outputMime !== undefined ? { outputMime } : {}),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"quote\" — the RFQ reply (§3)\n// ---------------------------------------------------------------------------\n\n/**\n * Build the `kind:7000 status:\"quote\"` reply: the proposed increment\n * schedule, committing nothing (§3.3 — there is no accept message; payment\n * of increment 1 is acceptance).\n */\nexport function buildQuoteEvent(\n job: FactoryJobRequest,\n schedule: IncrementSpec[]\n): UnsignedEvent {\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: JSON.stringify({\n increments: schedule.map((s) => ({\n n: s.n,\n of: s.of,\n milestone: s.milestone,\n priceUsdc: s.priceUsdc,\n })),\n }),\n tags: [\n ['e', job.requestEventId, '', 'root'],\n ['p', job.buyerPubkey],\n ['status', 'quote'],\n ],\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"partial\" — the increment offer (§4)\n// ---------------------------------------------------------------------------\n\n/** Where the encrypted increment artifact lives, and its hashlock condition. */\nexport interface EncryptedArtifactRef {\n /** Arweave tx id the ciphertext was uploaded to. */\n arweaveTxId: string;\n /** sha256 of the ciphertext, hex — lets the buyer detect a bad fetch. */\n ciphertextSha256: string;\n /** `sha256(key)` hex, where `key` decrypts the artifact (§4.2 — the join). */\n conditionHex: string;\n}\n\nexport interface BuildIncrementOfferOptions {\n job: FactoryJobRequest;\n /** The quote event's id for increment 1, else the previous offer's id. */\n parentEventId: string;\n increment: IncrementSpec;\n artifact: EncryptedArtifactRef;\n /**\n * The reproducible gate result for this increment's work (#53 — the\n * objective floor), when one ran. Omitted for milestones with nothing to\n * gate (e.g. `plan` — there is no code to lint against a brief).\n */\n gate?: GateResult;\n}\n\n/** A gate result missing its commit or check list cannot be reproduced by the buyer — reject it here rather than publish an unverifiable claim. */\nfunction assertReproducible(gate: GateResult): void {\n if (gate.checks.length === 0) {\n throw new Error(\n 'gate result must record at least one check for the offer to be reproducible'\n );\n }\n if (!gate.commit) {\n throw new Error(\n 'gate result must record the commit it ran against for the offer to be reproducible'\n );\n }\n}\n\n/**\n * Build the `kind:7000 status:\"partial\"` increment offer — published once\n * the provider has done that increment's work and uploaded the encrypted\n * artifact. This is the join between the relay plane and the connector\n * plane (§4.2): `condition` here MUST equal the paying PREPARE's\n * `executionCondition`, byte for byte.\n *\n * When `gate` is supplied, the offer also carries a `[\"gate\", \"pass\"|\"fail\"]`\n * tag (cheap to scan for a gate-pass rate, decision 8) and the full\n * reproducible result in `content` — visible to the buyer before they pay,\n * per the issue: a gate-failing increment is still offered, never hidden.\n */\nexport function buildIncrementOfferEvent(\n options: BuildIncrementOfferOptions\n): UnsignedEvent {\n const { job, parentEventId, increment, artifact, gate } = options;\n if (gate) assertReproducible(gate);\n\n const tags: string[][] = [\n ['e', job.requestEventId, '', 'root'],\n ['e', parentEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['status', 'partial'],\n ['increment', String(increment.n), String(increment.of)],\n ['i', artifact.arweaveTxId, 'url'],\n ['i', artifact.ciphertextSha256, 'text', '', 'hash'],\n ['amount', increment.priceUsdc, 'usdc'],\n ['condition', artifact.conditionHex],\n ];\n if (gate) tags.push(['gate', gatePassed(gate) ? 'pass' : 'fail']);\n\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: gate ? JSON.stringify({ gate }) : '',\n tags,\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:7000 status:\"processing\" — free narration (§6)\n// ---------------------------------------------------------------------------\n\nexport interface BuildNarrationOptions {\n job: FactoryJobRequest;\n /** The prior event in the thread (quote, offer, or narration). */\n parentEventId: string;\n message: string;\n}\n\n/**\n * Build a free narration event — no artifact, no `amount`/`condition`, MUST\n * NOT be paid against (§6). This is what makes a provider look alive between\n * increments.\n */\nexport function buildNarrationEvent(\n options: BuildNarrationOptions\n): UnsignedEvent {\n const { job, parentEventId, message } = options;\n return {\n kind: FACTORY_JOB_FEEDBACK_KIND,\n content: message,\n tags: [\n ['e', job.requestEventId, '', 'root'],\n ['e', parentEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['status', 'processing'],\n ],\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n\n// ---------------------------------------------------------------------------\n// kind:6097 — job result (§5)\n// ---------------------------------------------------------------------------\n\nexport interface BuildResultEventOptions {\n job: FactoryJobRequest;\n /** The original kind:5097 request event, verbatim (the `request` tag). */\n requestEvent: RelayEvent;\n /** The last kind:7000 event in the thread (offer or narration). */\n lastEventId: string;\n outcome: FactoryJobOutcome;\n /** How far the job got — `n-reached == of` only when outcome is completed. */\n reachedIncrement: number;\n totalIncrements: number;\n /** Required (and only meaningful) when outcome is `\"completed\"` (§5.2). */\n finalArtifact?: { arweaveTxId: string };\n}\n\n/**\n * Build the terminal `kind:6097` job result. Reputation (decision 8) is\n * computed from exactly this event and nothing else.\n */\nexport function buildResultEvent(\n options: BuildResultEventOptions\n): UnsignedEvent {\n const {\n job,\n requestEvent,\n lastEventId,\n outcome,\n reachedIncrement,\n totalIncrements,\n finalArtifact,\n } = options;\n\n const tags: string[][] = [\n ['e', job.requestEventId, '', 'root'],\n ['e', lastEventId, '', 'reply'],\n ['p', job.buyerPubkey],\n ['request', JSON.stringify(requestEvent)],\n ['outcome', outcome],\n ['increment', String(reachedIncrement), String(totalIncrements)],\n ];\n if (outcome === 'completed' && finalArtifact) {\n tags.push(['i', finalArtifact.arweaveTxId, 'url']);\n }\n\n return {\n kind: FACTORY_JOB_RESULT_KIND,\n content: '',\n tags,\n created_at: Math.floor(Date.now() / 1000),\n };\n}\n","/**\n * Factory job protocol — increment schedule planner (#52).\n *\n * `planFactoryJob` is the network-free half of the adapter (mirrors\n * `planPush`'s split from `executePush` in push.ts): given a job's `bid` and\n * the ticket list the factory's plan phase produced, it prices the\n * milestone boundaries decision 6 of toon-meta#262 fixed as increments —\n * plan → implement ×N tickets → review — and returns the schedule that\n * becomes the `kind:7000 status:\"quote\"` content (§3.2 of\n * `docs/factory-job-protocol.md`).\n *\n * Per the issue gotcha, `implement` must be many increments, not one: this\n * always emits one increment per ticket rather than a single lump-sum\n * implement increment, using the factory's existing parallel per-issue\n * fan-out (`.sandcastle/main.ts`'s Plan phase output — `{issues: [{id,\n * title, branch}]}`) as the boundary.\n */\n\n/** One ticket the factory's plan phase fanned the implement milestone into. */\nexport interface FactoryTicket {\n id: string;\n title: string;\n branch: string;\n}\n\nexport type FactoryMilestone = 'plan' | 'implement' | 'review';\n\n/** One priced increment in the schedule (§3.2's `increments[]` shape). */\nexport interface IncrementSpec {\n n: number;\n of: number;\n milestone: FactoryMilestone;\n /** Set only for `milestone: \"implement\"` increments. */\n ticket?: FactoryTicket;\n priceUsdc: string;\n}\n\n/** Plan milestone's share of the bid, in basis points. */\nconst PLAN_SHARE_BPS = 1000n; // 10%\n/** Review milestone's share of the bid, in basis points. */\nconst REVIEW_SHARE_BPS = 1000n; // 10%\nconst BPS_DENOM = 10000n;\n\n/**\n * Build the increment schedule for a job, given the tickets its plan phase\n * produced. Plan and review each get a fixed 10% of the bid; the remaining\n * 80% (\"implement\") splits evenly across tickets, with the integer-division\n * remainder distributed one micro-USDC at a time to the earliest tickets —\n * so `sum(priceUsdc) === bid` exactly (§3.2 only requires `<=`, but an exact\n * match avoids \"where did the leftover micro-USDC go\" questions).\n *\n * Per decision 7, this schedule is agreed before any paid work starts and is\n * never repriced — a job that runs larger than `tickets` implied is a\n * lesson, not a renegotiation.\n *\n * Throws if `tickets` is empty (a job with no implementation work has no\n * increments to schedule) or `bidMicroUsdc` is not a positive integer.\n */\nexport function planFactoryJob(\n bidMicroUsdc: string,\n tickets: FactoryTicket[]\n): IncrementSpec[] {\n if (tickets.length === 0) {\n throw new Error(\n 'planFactoryJob requires at least one implementation ticket from the plan phase'\n );\n }\n\n let bid: bigint;\n try {\n bid = BigInt(bidMicroUsdc);\n } catch {\n throw new Error(`bidMicroUsdc must be an integer string, got ${bidMicroUsdc}`);\n }\n if (bid <= 0n) {\n throw new Error(`bidMicroUsdc must be positive, got ${bidMicroUsdc}`);\n }\n\n const planPrice = (bid * PLAN_SHARE_BPS) / BPS_DENOM;\n const reviewPrice = (bid * REVIEW_SHARE_BPS) / BPS_DENOM;\n const implementTotal = bid - planPrice - reviewPrice;\n\n const ticketCount = BigInt(tickets.length);\n const base = implementTotal / ticketCount;\n const remainder = implementTotal % ticketCount;\n\n const total = tickets.length + 2;\n const schedule: IncrementSpec[] = [\n { n: 1, of: total, milestone: 'plan', priceUsdc: planPrice.toString() },\n ];\n\n tickets.forEach((ticket, i) => {\n const price = base + (BigInt(i) < remainder ? 1n : 0n);\n schedule.push({\n n: i + 2,\n of: total,\n milestone: 'implement',\n ticket,\n priceUsdc: price.toString(),\n });\n });\n\n schedule.push({\n n: total,\n of: total,\n milestone: 'review',\n priceUsdc: reviewPrice.toString(),\n });\n\n return schedule;\n}\n","/**\n * executeFactoryJob — the adapter's imperative shell (#52): runs the\n * factory's own milestones (`.sandcastle/main.ts`'s plan → per-ticket\n * implement+review → merge phases) through `hooks`, and turns each\n * milestone boundary into a paid increment via `publisher` (existing,\n * publisher.ts — upload + event publish) and `delivery` (new,\n * factory-job-delivery.ts — encrypt + payment wait).\n *\n * Mirrors `executePush`'s split from `planPush`: `planFactoryJob` is the\n * network-free planning half; this is the half that spends money.\n *\n * Decision 6 (toon-meta#262): work happens BEFORE the packet, so every\n * increment's artifact is produced and uploaded before that increment is\n * offered for payment. Decision 7: the schedule is fixed once the quote is\n * published — this never reprices mid-job. Per the issue: \"Stop on\n * non-payment. One unpaid increment and work halts\" — the symmetric\n * protection that neither side is ever exposed for more than one increment.\n */\n\nimport type { Publisher } from './publisher.js';\nimport type { JobDeliveryPort } from './factory-job-delivery.js';\nimport {\n buildIncrementOfferEvent,\n buildQuoteEvent,\n buildResultEvent,\n type FactoryJobOutcome,\n type FactoryJobRequest,\n type RelayEvent,\n} from './factory-job-events.js';\nimport type { GateResult } from './factory-job-gate.js';\nimport {\n planFactoryJob,\n type FactoryTicket,\n type IncrementSpec,\n} from './factory-job-plan.js';\n\n/** One milestone's deliverable, plus the gate result that ran against it, when one did. */\nexport interface FactoryJobWork {\n artifact: Uint8Array;\n /**\n * The reproducible gate result (#53) for this milestone's work, per\n * `FACTORY.md`'s pinned pipeline (lint/typecheck/test/build). Omitted for\n * `plan` — there is no code to gate against a brief.\n */\n gate?: GateResult;\n}\n\n/** Runs one factory milestone and returns its deliverable, plus that milestone's gate result. */\nexport interface FactoryJobHooks {\n /**\n * Runs the factory's plan phase for `job` (mirrors `.sandcastle/main.ts`'s\n * Phase 1): returns the per-ticket fan-out the implement milestone splits\n * into, plus the plan document itself as increment 1's artifact. No gate\n * result — planning produces no code to lint/typecheck/test/build.\n */\n plan(\n job: FactoryJobRequest\n ): Promise<{ tickets: FactoryTicket[]; artifact: Uint8Array }>;\n /**\n * Runs one ticket's implement sandbox pipeline (mirrors Phase 2's\n * per-issue `sandbox.run`, which ends in the gate per `FACTORY.md`) and\n * returns its deliverable (e.g. the ticket's diff) plus that run's gate\n * result.\n */\n implement(ticket: FactoryTicket): Promise<FactoryJobWork>;\n /**\n * Runs the factory's merge/review phase over every completed ticket\n * (mirrors Phase 3, which re-runs the gate per `merge-prompt.md`) and\n * returns the final deliverable plus that run's gate result.\n */\n review(tickets: FactoryTicket[]): Promise<FactoryJobWork>;\n}\n\nexport interface ExecuteFactoryJobOptions {\n job: FactoryJobRequest;\n /** The original kind:5097 request event, verbatim — carried in the result's `request` tag. */\n requestEvent: RelayEvent;\n hooks: FactoryJobHooks;\n publisher: Publisher;\n delivery: JobDeliveryPort;\n relayUrls: string[];\n}\n\nexport interface FactoryJobExecution {\n outcome: FactoryJobOutcome;\n /** The last increment successfully paid for (0 if the buyer never paid increment 1). */\n reachedIncrement: number;\n totalIncrements: number;\n /** The published kind:6097 result event's id. */\n resultEventId: string;\n}\n\nasync function workFor(\n spec: IncrementSpec,\n tickets: FactoryTicket[],\n planArtifact: Uint8Array,\n hooks: FactoryJobHooks\n): Promise<FactoryJobWork> {\n if (spec.milestone === 'plan') return { artifact: planArtifact };\n if (spec.milestone === 'implement') {\n if (!spec.ticket) {\n throw new Error(\n `internal: implement increment ${spec.n} is missing its ticket`\n );\n }\n return hooks.implement(spec.ticket);\n }\n return hooks.review(tickets);\n}\n\n/**\n * Run a job end to end: plan → quote → one increment per milestone boundary\n * (encrypt, upload, offer, wait for payment) → terminal kind:6097 result.\n * Halts at the first unpaid increment (`abandoned-buyer`) or the first\n * hook failure once work is underway (`abandoned-provider`) — see §5.2 of\n * `docs/factory-job-protocol.md` for the three terminal outcomes.\n *\n * Throws if `hooks.plan` itself fails — with no quote ever published, there\n * is no prior kind:7000 event for a result's `reply` e-tag to reference, so\n * no protocol event can be honestly emitted for that failure.\n */\nexport async function executeFactoryJob(\n options: ExecuteFactoryJobOptions\n): Promise<FactoryJobExecution> {\n const { job, requestEvent, hooks, publisher, delivery, relayUrls } =\n options;\n\n if (!publisher.uploadBlob) {\n throw new Error(\n 'the active publisher cannot upload an encrypted increment artifact ' +\n '(uploadBlob unavailable) — executeFactoryJob requires the standalone publisher'\n );\n }\n const uploadBlob = publisher.uploadBlob.bind(publisher);\n\n const { tickets, artifact: planArtifact } = await hooks.plan(job);\n const schedule = planFactoryJob(job.bidMicroUsdc, tickets);\n const totalIncrements = schedule.length;\n\n const quoteEvent = buildQuoteEvent(job, schedule);\n const quoteReceipt = await publisher.publishEvent(quoteEvent, relayUrls);\n\n let parentEventId = quoteReceipt.eventId;\n let reachedIncrement = 0;\n let finalArtifactTxId: string | undefined;\n\n const haltWith = async (\n outcome: FactoryJobOutcome\n ): Promise<FactoryJobExecution> => {\n const resultEvent = buildResultEvent({\n job,\n requestEvent,\n lastEventId: parentEventId,\n outcome,\n reachedIncrement,\n totalIncrements,\n ...(outcome === 'completed' && finalArtifactTxId\n ? { finalArtifact: { arweaveTxId: finalArtifactTxId } }\n : {}),\n });\n const resultReceipt = await publisher.publishEvent(\n resultEvent,\n relayUrls\n );\n return {\n outcome,\n reachedIncrement,\n totalIncrements,\n resultEventId: resultReceipt.eventId,\n };\n };\n\n for (const spec of schedule) {\n let work: FactoryJobWork;\n try {\n work = await workFor(spec, tickets, planArtifact, hooks);\n } catch {\n // Provider-side failure mid-job: stop where we are, no renegotiation.\n return haltWith('abandoned-provider');\n }\n\n const encrypted = await delivery.encryptArtifact(work.artifact);\n const uploadReceipt = await uploadBlob({\n body: Buffer.from(encrypted.ciphertext),\n contentType: 'application/octet-stream',\n ...(job.repo ? { repoId: job.repo } : {}),\n });\n\n const offerEvent = buildIncrementOfferEvent({\n job,\n parentEventId,\n increment: spec,\n artifact: {\n arweaveTxId: uploadReceipt.txId,\n ciphertextSha256: encrypted.ciphertextSha256,\n conditionHex: encrypted.conditionHex,\n },\n ...(work.gate ? { gate: work.gate } : {}),\n });\n const offerReceipt = await publisher.publishEvent(offerEvent, relayUrls);\n\n const paid = await delivery.waitForPayment({\n offerEventId: offerReceipt.eventId,\n conditionHex: encrypted.conditionHex,\n priceUsdc: spec.priceUsdc,\n });\n if (!paid) {\n parentEventId = offerReceipt.eventId;\n return haltWith('abandoned-buyer');\n }\n\n reachedIncrement = spec.n;\n parentEventId = offerReceipt.eventId;\n if (spec.milestone === 'review') finalArtifactTxId = uploadReceipt.txId;\n }\n\n return haltWith('completed');\n}\n","/**\n * The Runner seam (rig#125) — the ONE new interface the relay-native CI\n * layer introduces. The coordinator (./coordinator.ts) materializes a commit,\n * picks a workflow, decides the secret set, and hands all of that to a\n * Runner; the Runner executes the jobs and reports per-job conclusions, exit\n * codes, timings, logs and artifact files. Nothing about relays, payments,\n * or NIP-C1 events crosses this boundary in either direction.\n *\n * Two implementations exist today:\n *\n * - {@link FakeRunner} (here): scripted outcomes for coordinator tests.\n * - `ActRunner` (./act-runner.ts): shells out to `act` against the host\n * Docker daemon — the first-slice \"coordinator is its own runner\" shape\n * ngit's reference coordinator also uses.\n *\n * A later TOON-lease runner (rent a workload from a TOON Network provider,\n * blocked on TOON_Network Milestone 1) is a third class behind the SAME\n * interface; that is the whole point of the seam.\n */\n\nimport type { CiConclusion, CiTriggerContext } from './nip-c1-events.js';\n\n/** One artifact file a job produced (one entry per FILE, never an archive). */\nexport interface RunnerArtifact {\n /** Absolute path of the file on the coordinator host. */\n path: string;\n /** The file's path within the artifact (mirrors upload-artifact). */\n filename: string;\n /** The artifact name grouping related files. */\n name: string;\n}\n\n/** The outcome of one job within a run. */\nexport interface RunnerJobResult {\n /** Job id as declared in the workflow file. */\n jobId: string;\n /** Human-readable job name, when the workflow declares one. */\n name?: string;\n conclusion: CiConclusion;\n exitCode?: number;\n /** Unix seconds. */\n startedAt: number;\n /** Unix seconds. */\n finishedAt: number;\n /** The job's complete log output. */\n log: string;\n artifacts: RunnerArtifact[];\n}\n\n/** The outcome of one workflow run. */\nexport interface RunnerRunResult {\n /** Combined conclusion — the execution backend's own verdict, passed through. */\n conclusion: CiConclusion;\n jobs: RunnerJobResult[];\n startedAt: number;\n finishedAt: number;\n}\n\n/** Everything a Runner needs to execute one workflow once. */\nexport interface RunnerRequest {\n /** A real git repository, checked out at `trigger.commit`. */\n checkoutDir: string;\n /** Workflow file path relative to `checkoutDir`, plus its content SHA-256. */\n workflow: { path: string; sha256: string };\n trigger: CiTriggerContext;\n /** Secrets to inject; `{}` for runs the coordinator did not authorize secrets for. */\n secrets: Record<string, string>;\n /** Extra environment for the jobs. */\n env?: Record<string, string>;\n /** Wall-clock budget for the whole run; exceeding it concludes `timed_out`. */\n timeoutMs: number;\n /** Streaming log sink (per job). */\n onLog?: (jobId: string, chunk: string) => void;\n /** Cancels the run (concludes `cancelled`). */\n signal?: AbortSignal;\n}\n\n/** Executes one workflow run. See the module header. */\nexport interface Runner {\n /** Runner family, as advertised in the NIP-C1 `W` tag. */\n readonly family: 'act';\n /** Accepted selectors for the family (`R` = `act:<selector>`). */\n readonly selectors: string[];\n run(request: RunnerRequest): Promise<RunnerRunResult>;\n}\n\n// ---------------------------------------------------------------------------\n// FakeRunner — scripted outcomes for tests\n// ---------------------------------------------------------------------------\n\nexport type FakeRunnerScript = (\n request: RunnerRequest\n) => RunnerRunResult | Promise<RunnerRunResult>;\n\n/** The default script: one successful `build` job with a short log. */\nexport function defaultFakeRunResult(request: RunnerRequest): RunnerRunResult {\n const now = Math.floor(Date.now() / 1000);\n return {\n conclusion: 'success',\n startedAt: now,\n finishedAt: now,\n jobs: [\n {\n jobId: 'build',\n name: 'build',\n conclusion: 'success',\n exitCode: 0,\n startedAt: now,\n finishedAt: now,\n log: `fake runner: ran ${request.workflow.path} at ${request.trigger.commit}\\n`,\n artifacts: [],\n },\n ],\n };\n}\n\n/**\n * A Runner that returns scripted outcomes and records every request, so a\n * coordinator test can assert on what was asked (secrets, timeout, commit)\n * and drive the publish sequence from a chosen result. Never touches Docker.\n */\nexport class FakeRunner implements Runner {\n readonly family = 'act' as const;\n readonly selectors = ['ubuntu-latest'];\n /** Every request, in call order. */\n readonly requests: RunnerRequest[] = [];\n\n constructor(\n private readonly script: FakeRunnerScript = defaultFakeRunResult\n ) {}\n\n async run(request: RunnerRequest): Promise<RunnerRunResult> {\n this.requests.push(request);\n const result = await this.script(request);\n if (request.onLog) {\n for (const job of result.jobs) request.onLog(job.jobId, job.log);\n }\n return result;\n }\n}\n","/**\n * Real wire capture from wss://relay.ngit.dev (2026-09-17T16:00:01Z).\n *\n * A kind:30617 repository announcement for ngit's `wyrd` repo. It carries a\n * `clone` tag and the unmerged role tags from nips PR #2324 (`M` — lead\n * maintainer, `m` — maintainer), alongside the conformant `relays`, `web`\n * and `[\"r\", <sha>, \"euc\"]` earliest-unique-commit tags described in\n * rig#153's Implementation Decisions. Committed byte-for-byte as received —\n * no edited tags, no re-serialization — so its id and signature verify (see\n * `ngit-fixtures.test.ts`).\n *\n * No live event combining `M`, `m` AND `o` together with `clone` was found\n * on relay.ngit.dev at capture time (a relay-wide scan of 597 kind:30617\n * events found exactly one `o`-tagged announcement, and it carried no `M`).\n * This fixture is the richest real combination available: `M` + `m` + `clone`.\n *\n * Captured with `scripts/capture-ngit-fixtures.mjs` (repo root); see that\n * file for the exact filter and how to re-run the capture.\n */\n\nimport type { NostrEvent } from '../remote-state.js';\n\nexport const NGIT_ANNOUNCEMENT_WYRD_RELAY = 'wss://relay.ngit.dev';\nexport const NGIT_ANNOUNCEMENT_WYRD_CAPTURED_AT = '2026-09-17T16:00:01Z';\nexport const NGIT_ANNOUNCEMENT_WYRD_EVENT_ID =\n '5df470edc9067955e707def26b57dbf3c61920ede9c6792ab592d435729192b0';\n\n/** kind:30617 announcement for `wyrd`, as served by relay.ngit.dev. */\nexport const NGIT_ANNOUNCEMENT_WYRD: NostrEvent = {\n id: '5df470edc9067955e707def26b57dbf3c61920ede9c6792ab592d435729192b0',\n pubkey: 'b3c95ce33dfa84326611e8b7a9c10b78df28754c38b106a4bc0196b9be5f4e4a',\n created_at: 1789554867,\n kind: 30617,\n tags: [\n ['d', 'wyrd'],\n ['r', '2e12ddef8cb4f0adf28518ba7322021379c65a02', 'euc'],\n ['name', 'wyrd'],\n [\n 'description',\n '**Wyrd** is a decentralized, append-only, content-addressed drive system.',\n ],\n [\n 'clone',\n 'https://grasp.t5.st/npub1k0y4eceal2zryes3azm6nsgt0r0jsa2v8zcsdf9uqxttn0jlfe9q04c9h8/wyrd.git',\n ],\n [\n 'web',\n 'https://gitworkshop.dev/npub1k0y4eceal2zryes3azm6nsgt0r0jsa2v8zcsdf9uqxttn0jlfe9q04c9h8/grasp.t5.st/wyrd',\n ],\n ['relays', 'wss://grasp.t5.st'],\n ['alt', 'git repository: wyrd'],\n [\n 'maintainers',\n 'b3c95ce33dfa84326611e8b7a9c10b78df28754c38b106a4bc0196b9be5f4e4a',\n '86a314a7ef4aa4fb4e00d738a7bec5fc96ac1312246c7c25888ae609046e6965',\n ],\n ['M', 'b3c95ce33dfa84326611e8b7a9c10b78df28754c38b106a4bc0196b9be5f4e4a'],\n [\n 'm',\n '86a314a7ef4aa4fb4e00d738a7bec5fc96ac1312246c7c25888ae609046e6965',\n '1789554803',\n ],\n ],\n content: '',\n sig: '546036757fc3d3857d6d3b80b478ad9c726ce6964d7f7e76dbb5551ce04c8e79e2d2efb90a99f402d50724a2b24749a3a5b952d6406cfb25b57d500f3dac2f11',\n};\n","/**\n * Real wire capture from wss://relay.ngit.dev (2026-09-17T16:00:01Z).\n *\n * The kind:30618 repository state event for ngit's own `ngit` repo\n * (author a008def15796fba9a0d6fab04e8fd57089285d9fd505da5a83fe8aad57a3564d,\n * npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr — the\n * canonical repo ngit's cli/git plugin ships from). It carries `HEAD` and\n * NIP-34 ref-shape tags for `refs/heads/*` and `refs/tags/*`, INCLUDING\n * peeled `^{}` entries for annotated tags (e.g. `refs/tags/v3.0.0^{}`) —\n * exactly the shape rig#153's dual-read work must parse without treating\n * peeled entries as ordinary refs. Committed byte-for-byte as received — no\n * edited tags, no re-serialization, tag order preserved — so its id and\n * signature verify (see `ngit-fixtures.test.ts`).\n *\n * Captured with `scripts/capture-ngit-fixtures.mjs` (repo root); see that\n * file for the exact filter and how to re-run the capture.\n */\n\nimport type { NostrEvent } from '../remote-state.js';\n\nexport const NGIT_STATE_NGIT_RELAY = 'wss://relay.ngit.dev';\nexport const NGIT_STATE_NGIT_CAPTURED_AT = '2026-09-17T16:00:01Z';\nexport const NGIT_STATE_NGIT_EVENT_ID =\n '17fdde63cbc0289e933e6ed232e354cce6713d89e10a793f6d6f9d8ad274ee14';\n\n/** kind:30618 repository state for ngit's own `ngit` repo. */\nexport const NGIT_STATE_NGIT: NostrEvent = {\n id: '17fdde63cbc0289e933e6ed232e354cce6713d89e10a793f6d6f9d8ad274ee14',\n pubkey: 'a008def15796fba9a0d6fab04e8fd57089285d9fd505da5a83fe8aad57a3564d',\n created_at: 1789656880,\n kind: 30618,\n tags: [\n ['d', 'ngit'],\n ['refs/tags/v1.1.2', 'ada8f00836ec3330047d6a39108b84b7bd234448'],\n ['refs/tags/v3.0.0-rc.7', 'fa86c50cba00596c0de1f1681b6223c71914f78c'],\n ['refs/tags/v3.0.0-rc.6', '23d0d79e63cbfd681d8f95668732ddf727fcf4ab'],\n ['refs/tags/v2.2.0', 'e0213ed04aa5bbec24ed7c8b7de9bdf8057346be'],\n ['refs/tags/v1.0.0^{}', '535be9c1c0a40fdeef9aa3ca84f90b01bc371a22'],\n ['refs/tags/v1.6.1^{}', '3476464013dc2d54030bdec5f9590a3c61dc16c0'],\n ['refs/tags/v2.4.1^{}', '875f4e0a99688c7363cba69c1485af1cd4b34999'],\n ['refs/tags/v3.0.0-rc.6^{}', '3361488d34ad6b80aac68e93207ff8e7d2176b9f'],\n ['refs/tags/v1.6.0^{}', '632af9a091205a0453f0d96a165e1535fd24b2a7'],\n ['refs/tags/v0.1.0', 'ba171a6b14b9d7237a60c4b2089f1fb0b83173cf'],\n ['refs/tags/v2.1.0', '6bc45e30804da0ba1fc2a2727f432ccfc751e1e6'],\n ['refs/tags/v1.0.0', 'ce44cc759858b8dfa9552b01efc8ec00cb7a946a'],\n ['refs/tags/v1.5.1^{}', '51358320c50afece31fc25945a09e3d7aac8f39c'],\n ['refs/tags/v2.0.1', '317fc12b99ee8f5e1df9010b586b31a3f2546372'],\n ['refs/tags/v3.0.0-rc.3^{}', 'd70b3863e89e24a112ebc782fb63c1e05bb3691b'],\n ['refs/tags/v1.3-beta1', 'fc3258fa9dbad5d1fa1c20a0236c1514ea36f97b'],\n ['refs/tags/v1.6.2^{}', '23d0f1232c0b2e7be147f328a55e23aed6641440'],\n ['refs/tags/v1.4.5', 'ea6765741d1aa837318bd1fc6e724383ebef9889'],\n ['refs/tags/v0.0.2', 'e1cf767fde29d696a1ddb3943a5defa68213b6cf'],\n ['refs/tags/v1.5.3', '1b8ff53ed826d1290058e531a7f43c9e3493a9c5'],\n ['refs/tags/v2.6.1', '694642891cbcc23a8173f0fc4a4fcf13eff8acc9'],\n ['refs/tags/v1.4.3^{}', '190b2f622f44c49aa77f8089d9ca9218c0aade81'],\n ['refs/tags/v2.6.0', 'e71a5472652ed8869ec9069ccd388639f8d1234a'],\n ['refs/tags/v1.4.6^{}', 'fce27a10b2161508d35a07ce926e994ae9bb909f'],\n ['refs/tags/v0.1.2', 'adbb992190c29f93e2aa649dac57f1757154af4f'],\n ['refs/tags/v1.1.2^{}', '87ee59ebd91ec4b73b5528446e6d7d87449a9e5d'],\n ['refs/tags/v3.0.0-rc.5', 'f449b229d4ae6a74213e647ab2925e02a28e582f'],\n ['refs/tags/v1.5.0^{}', 'b9ea26bc928c361b092eefde69820c51ebc3a3df'],\n ['refs/tags/v1.7.3^{}', 'bddbcbb97f238760fd346f88bfdd8470f3eddf2a'],\n ['refs/tags/v2.3.0^{}', '45b0f8c6b1dab2d51338d88b2a7caf4b1b571f4d'],\n ['refs/tags/v1.5.2^{}', '12dc5bc06f701308c47589018e932a413ad543ba'],\n ['refs/tags/v1.7.4^{}', '4447aad6378403bba73d738214aa81c50b6d5567'],\n ['refs/tags/v1.4.1', 'bd64e8e8109eb43180c98a25f2d0333f5c7b6af2'],\n ['refs/tags/v1.6.1', 'b3ce4f7a34ec968e8f3eb37ff874cc84b2232861'],\n ['refs/tags/v1.5.1', '1c995b00d0d21597a82627b9157a0f8626c3d858'],\n ['refs/tags/v3.0.0-rc.8', 'cc57418dfbc6d2673f24328ac927a36fdc4a2163'],\n ['refs/tags/v2.5.0^{}', 'd88bce1ab357c74a6cfd153b0cf4a49f25b8dfaa'],\n ['refs/tags/v2.6.0^{}', '7b37d89713e42ea91679d1138d43af1fe9e9341d'],\n ['refs/tags/v2.3.0', '7a7ae1862b1edeaa931e53c0395d8ae392ca557b'],\n ['refs/tags/v1.7.1', 'e70809933fa73016feb45da906e79e511a019a18'],\n ['refs/tags/v3.0.1', '56ddf48e5543ee7d107edbb47253cffb00c90cf8'],\n ['refs/tags/v2.2.3^{}', 'f91a0d00bafe2af1f6b6828c3de3d3e5d65153c3'],\n ['refs/tags/v1.7.2', 'd800a8c43a412f1bc1751f9f86f63ff869bd93b2'],\n ['refs/tags/v0.1.1', '9c1ffe1d084c427c91b581fdb677106b31faeab0'],\n ['refs/heads/tmp', '99b761499e81e79259df521aab9c33a1f11a4c59'],\n ['refs/tags/v1.7.3', 'faf822c49cb0c597595901938dc5ad07c8735d69'],\n ['refs/tags/v2.4.4^{}', '05b0edbff1659689ecc3efbfe36420d64486e2f7'],\n ['refs/tags/v1.4.2', '5635ccf7a664de52f017dcd85f10548fd6ed18f4'],\n ['refs/tags/v1.4.3', '1c6b3ea18ca930067407c9733997af810ccc7188'],\n ['refs/tags/v0.0.2^{}', 'ae6e1f72d9d668783cf27c5f3a88f3cb445f6402'],\n ['refs/tags/v1.5.0', '4a0c01adfb7345ba9feb9a26eb434ea0489c7a81'],\n ['refs/tags/v1.4.0^{}', 'a3073105ae20747fbf76b70cf0ac4ed5c0a9b1b3'],\n ['refs/tags/v2.2.0^{}', '3a17877bcdabc9d6721e3054c2bb07a892f32147'],\n ['refs/tags/v1.4.6', '7335fccd5947bc7f469c9762b45422b5f87985dc'],\n ['refs/tags/v2.4.2^{}', '59e467b423e0ba66c30c7f1b7d41d47d938615de'],\n ['refs/tags/v1.2.0^{}', 'f5c82ee70dc2e881c2b1013f6d352f5da7b08291'],\n ['refs/tags/v2.2.3', 'c6944db92396dcfb7a0e4b8c5ebed475c9435df4'],\n ['refs/tags/v2.4.4', 'b04b58cd1baf4a8db03f72f1162d4950b54489d8'],\n ['refs/tags/v1.5.2', '6cc7ac1aa32a3d54bb091bcf73371d0f6c51e6a3'],\n ['refs/tags/v2.5.0', '02d14a38bb27a279a69ebf3dd34615c95ea49d8b'],\n ['refs/tags/v1.2.1^{}', '1083153ea340b57620870a47ce7653aeae3d1196'],\n ['refs/tags/v1.6.2', 'b76fac50b49aac116b2f1c7965d00b343bedac8f'],\n ['refs/tags/v1.6.3', '423534e17a5d6a2efb412d2b0b5944a270af5f86'],\n ['refs/tags/v3.0.0-rc.5^{}', 'e1d20770b354a39eb8c6ba965ea5d5e822e81610'],\n ['refs/tags/v1.2.0', 'c8728180f959b5e5dca04a926f0e3d0e16e374b5'],\n ['refs/tags/v1.4.4^{}', '76a88f81af435a5c11fa7baf1689c82880fdcd5c'],\n ['refs/tags/v3.0.1^{}', '682b22a34baca24ddd3edde65f7490eb8f91e50c'],\n ['refs/tags/v2.2.1', '6509946b8942f7970d1404310cef8e2f7952b929'],\n ['refs/tags/v1.7.4', '8e54a753cdd2ccba302188d4a398e38cf0d42271'],\n ['refs/tags/v2.1.0^{}', 'd2412565334f48bd31e57d29d7959c24258ccd98'],\n ['refs/tags/v2.0.1^{}', '4a6f51a8245e335cdb276029c4e82c6d6e2798af'],\n ['refs/tags/v3.0.0-rc.8^{}', '7899027ec1715ea5ab0ab2178774c10f39e0a7da'],\n ['refs/tags/v3.0.0-rc.3', '6aeca06ea2933a5a38f73cc29ceda55652cffa16'],\n ['refs/tags/v1.1.1^{}', 'bba5a092b7f07b9a096e2801d51030b186161f80'],\n ['refs/tags/v1.1.0^{}', 'aa36f4e569c9eac7cec101917f87995d6809005e'],\n ['refs/tags/v3.0.0^{}', 'c2cc591dcfae5d46dc178c0ea87e8bc1802f09b3'],\n ['refs/tags/v1.7.1^{}', '6fbbc8ccb8ec330e7963a4333944db69f2e4dc9f'],\n ['refs/tags/v2.4.3', 'c5b0d370b8c980964a77379e33459c2eab642fc2'],\n ['refs/tags/v3.0.0-rc.4^{}', '015dfb79f80dcd07bdf7de6010c00c0754d5d8b4'],\n ['refs/tags/v2.4.3^{}', 'e8c3c49b5bbac43cd1497c2593198d19fe506073'],\n ['refs/tags/v2.2.1^{}', '3017faf6d346fa9328c5979c6e9c6bc471bd3942'],\n ['refs/tags/v0.0.1', '6f33ddda6f4d2a1deb4af72f1ce0fdab365c1cb9'],\n ['refs/tags/v1.3.0', '2c778140d892fd3c8757b078971cb935058b600c'],\n ['refs/tags/v2.0.0', 'f5ad8c320b413b23df80d776217e750765186892'],\n ['refs/tags/v3.0.0-rc.2^{}', '71a8a71313a3a0b029e07bc8a1ed3aa5789255e9'],\n ['refs/tags/v0.1.1^{}', 'fc3f22eac2bb81823f170f61ba9d39baff76b933'],\n ['refs/tags/v1.4.0', 'c280794fa6c70b076e9f121f1f30f9aa0edb0855'],\n ['refs/tags/v0.1.0^{}', '15879248571e2753395ecbeb52e67bd2a9ad9db0'],\n ['refs/tags/v1.7.2^{}', '20f2680394e0b62864b1b192338b3cd7ecdad4ec'],\n ['refs/tags/v1.4.5^{}', '36763dc0d821425e088adefd16dc7c4bf3b30f3b'],\n ['refs/tags/v3.0.0', '695aca4f159938582bc978b257ab78fbf1676612'],\n ['refs/tags/v1.6.0', '10d699134f72a7d4bdad44fb86889a406f9e9acd'],\n ['refs/tags/v1.4.2^{}', 'daaaa3a21277351638eda04739ac3d7b3716f955'],\n ['refs/tags/v1.3.1^{}', '52f9efa50f81142da013c4da5f3cd3091e07916b'],\n ['refs/tags/v2.4.0', 'dc4734acbddb7eba7d55bcbe88a4436b03d89904'],\n ['refs/tags/v2.4.1', 'c0141e567d61c98f9ec92aa004261e821a753104'],\n ['refs/tags/v1.7.0', 'f3609a709a41248337666b7da4db89e7050a4abd'],\n ['refs/heads/stable', '682b22a34baca24ddd3edde65f7490eb8f91e50c'],\n ['refs/tags/v2.4.2', '81d3dd6f90871dbcdf149180dbe43f18d7163f5f'],\n ['refs/tags/v1.3.1', '4172a64be45d1cb038d42011eb9958fab6a30ecd'],\n ['refs/tags/v2.6.2^{}', '4316af88a7ffa2a9d9fe72afbf9b8fd6b1c8568f'],\n ['refs/tags/v1.7.0^{}', '8b182325ad221e06f20b5778fd4963a4b90622c5'],\n ['refs/tags/v0.1.2^{}', '4b66c76a5a3ba878b4603d992353327807b8d8e6'],\n ['refs/tags/v3.0.0-rc.2', '457f018d0d01c6c8607a1b341c573b0668ab4fc5'],\n ['refs/tags/v0.0.1^{}', '7d50d66d21bcaac39ad1d422d1da3167e98f8a40'],\n ['refs/tags/v1.4.1^{}', '09ead86c82f773778b984b5bbbd9798155265cd3'],\n ['refs/tags/v3.0.0-rc.4', '0168bb5fb768e21346e5abbfc04934ed6e824efd'],\n ['refs/tags/v2.4.0^{}', '205ca05897cbc727d9b75e7ab68375b5c93ead39'],\n ['refs/tags/v1.1.1', '16cbf069aea4c0c4ce75bbf6bcea773b8386cd9e'],\n ['refs/tags/v2.6.3^{}', 'a923f6417d236b5472d502cb447c2da5d6b17666'],\n ['refs/tags/v1.6.3^{}', 'd4bc948bfd06a8d61b3deb072db8a70b5ef8a775'],\n ['refs/tags/v2.6.1^{}', '55f3e3a838dffd03f0fdd996103b8dc8c2cddcd6'],\n ['refs/tags/v1.3-beta1^{}', 'ba82a894fad645757c49242c11573b6c5dd8d1e6'],\n ['refs/tags/v2.6.2', 'c49c6410af12b9b53ced477b956a08280fed78bc'],\n ['refs/tags/v1.2.1', '21cabdbd03fa4d2068975db47416c9d7c819c47e'],\n ['refs/tags/v3.0.0-rc.7^{}', '8eb500aded769ad047b07e25c0abe33413f4b500'],\n ['refs/tags/v1.1.0', 'f47d46864b9691e258a83f4e40570e61bd8df9aa'],\n ['refs/tags/v1.4.4', 'bbee90c53e455474d13ba7b0b18670dc0b0ba66f'],\n ['refs/tags/v2.0.0^{}', '9b97fd944f7635cb71b7d5be53369a65a0538ebd'],\n ['refs/tags/v1.3.0^{}', 'd1dab56ad1319b61b5e3f70949780ba98d312ea8'],\n ['refs/tags/v1.5.3^{}', '0089b8fce131c0675539490b67f5a55aab377853'],\n ['refs/tags/v2.6.3', 'ecb8474a1c0d60c4452876920912fc0772c9505d'],\n ['refs/heads/timeout-testing', 'bbdd4387da920ba56065320fdd5a9997bd209e5e'],\n ['refs/heads/main', '715f0fdeca1c7dd68ad144f36112e02cab58a264'],\n ['HEAD', 'ref: refs/heads/main'],\n ],\n content: '',\n sig: '8512128f0ee420a754d1da87771d8fcbb967744e88af6a11f273adfdaa62f517a69ac61774d3841b4db96446700a423c9efda9aa4752cf418220a1dac2e8ba0f',\n};\n","/**\n * Real wire capture from wss://relay.ngit.dev (2026-09-17T16:00:01Z).\n *\n * A kind:1621 issue on the `HydrusUI` repo (author\n * 43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1) and its\n * full kind:1111 NIP-22 comment thread: five comments, including one\n * NESTED reply. Four of the five comments (`e`/`k`/`p`) point straight at\n * the root issue; `NGIT_COMMENT_THREAD_NESTED_REPLY` instead points its\n * lowercase `e`/`k`/`p` at another comment\n * (`NGIT_COMMENT_THREAD_NESTED_REPLY_PARENT_ID`), while its uppercase\n * `E`/`K`/`P` still name the root issue — the shape rig#153's comment\n * builder and readers must produce and accept. Committed byte-for-byte as\n * received — no edited tags, no re-serialization, tag order preserved — so\n * every event's id and signature verify (see `ngit-fixtures.test.ts`).\n *\n * Captured with `scripts/capture-ngit-fixtures.mjs` (repo root); see that\n * file for the exact filter and how to re-run the capture.\n */\n\nimport type { NostrEvent } from '../remote-state.js';\n\nexport const NGIT_COMMENT_THREAD_RELAY = 'wss://relay.ngit.dev';\nexport const NGIT_COMMENT_THREAD_CAPTURED_AT = '2026-09-17T16:00:01Z';\nexport const NGIT_COMMENT_THREAD_ROOT_EVENT_ID =\n '3bb49002757fe697f328a2825fdd00f48a8c376238db65b18906d52864692e2a';\nexport const NGIT_COMMENT_THREAD_COMMENT_EVENT_IDS = [\n 'c7a156e8ced575251d7c6b3b20504d0d8b24b6e88da94a2072680aff9b12fbba',\n '1301e2e3bd9426e1d6919b544f700f43a508bd585acae38ef706cc510c336102',\n '6f815f058c6f627eab6f5324f06cda29163e0232f410abf8eff22de250e188c1',\n '952c1fe0a2e88c3c716b56aa2f695349a77220a211779de30e97b115ba94e084',\n 'fdf52ad9ccd8038ca8d0a1ee73e508d3351edd3b80e703953646ee2c83366a1a',\n];\n/** The one comment in the thread whose parent is another comment, not the root issue. */\nexport const NGIT_COMMENT_THREAD_NESTED_REPLY_ID =\n '952c1fe0a2e88c3c716b56aa2f695349a77220a211779de30e97b115ba94e084';\nexport const NGIT_COMMENT_THREAD_NESTED_REPLY_PARENT_ID =\n '6f815f058c6f627eab6f5324f06cda29163e0232f410abf8eff22de250e188c1';\n\n/** kind:1621 issue \"Bug: Thumbnail Healing / Transfer\" on HydrusUI. */\nexport const NGIT_COMMENT_THREAD_ROOT: NostrEvent = {\n id: '3bb49002757fe697f328a2825fdd00f48a8c376238db65b18906d52864692e2a',\n pubkey: '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n created_at: 1789629752,\n kind: 1621,\n tags: [\n [\n 'a',\n '30617:43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1:HydrusUI',\n 'wss://relay.yiff.tech/',\n ],\n [\n 'p',\n '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n 'wss://relay.yiff.tech/',\n ],\n ['subject', 'Bug: Thumbnail Healing / Transfer'],\n ['alt', 'Git issue: Bug: Thumbnail Healing / Transfer'],\n [\n 'imeta',\n 'url https://blossom.ditto.pub/56d1b8449bb4691af1373df9ce9d13c00cb31827ed7c47775e448e7df96ae981.png',\n 'x 56d1b8449bb4691af1373df9ce9d13c00cb31827ed7c47775e448e7df96ae981',\n 'ox 56d1b8449bb4691af1373df9ce9d13c00cb31827ed7c47775e448e7df96ae981',\n 'size 93750',\n 'm image/png',\n 'dim 978x596',\n 'blurhash L35#^r%jI8x^pMoNW9kDt9R%oLj]',\n ],\n ],\n content:\n \"In the case where you have plaintext SHAs on a private blossom server already, there are many times where you couldn't 'heal' or get thumbnails to get moved onto it from within the UI.\\n\\nWorking on an automated tool that can do it if it ever occurs. For now, the most effective method is to use the multi selector and right click menu, and run 'Regen Previews' \\n\\nhttps://blossom.ditto.pub/56d1b8449bb4691af1373df9ce9d13c00cb31827ed7c47775e448e7df96ae981.png\",\n sig: 'efff5623455271d9866bd2384b1e59476c4e277cbccff4c59b9d73c5e927410379e25a600da9f82447e2a5b78440cdc5d314b64ebdacc3fb5ae80a4c86782e2b',\n};\n\n/** kind:1111 top-level comment #1 on the issue. */\nconst COMMENT_1: NostrEvent = {\n id: 'c7a156e8ced575251d7c6b3b20504d0d8b24b6e88da94a2072680aff9b12fbba',\n pubkey: '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n created_at: 1789629841,\n kind: 1111,\n tags: [\n [\n 'E',\n '3bb49002757fe697f328a2825fdd00f48a8c376238db65b18906d52864692e2a',\n '',\n '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n ],\n ['K', '1621'],\n ['P', '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1'],\n [\n 'e',\n '3bb49002757fe697f328a2825fdd00f48a8c376238db65b18906d52864692e2a',\n '',\n '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n ],\n ['k', '1621'],\n ['p', '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1'],\n ],\n content:\n 'Deploying 2eb6450ace79018d4d3f20f7c9952fa113659029 , trying to build in automatic healing.',\n sig: '6004bdb25dd1437851a8ed325743f335f4f298c5faf3db406706cc419c84182dc107ce4d288f73f05f96000de32be2a19835c9bd21d525330b95876d25276c9d',\n};\n\n/** kind:1111 top-level comment #2 (an image share, carries `imeta`). */\nconst COMMENT_2: NostrEvent = {\n id: '1301e2e3bd9426e1d6919b544f700f43a508bd585acae38ef706cc510c336102',\n pubkey: '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n created_at: 1789630511,\n kind: 1111,\n tags: [\n [\n 'E',\n '3bb49002757fe697f328a2825fdd00f48a8c376238db65b18906d52864692e2a',\n '',\n '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n ],\n ['K', '1621'],\n ['P', '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1'],\n [\n 'e',\n '3bb49002757fe697f328a2825fdd00f48a8c376238db65b18906d52864692e2a',\n '',\n '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n ],\n ['k', '1621'],\n ['p', '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1'],\n [\n 'imeta',\n 'url https://blossom.ditto.pub/bb1b398f4e1627336b481ee5825d95317fd48458c786ea855eab803e48d0ea95.png',\n 'x bb1b398f4e1627336b481ee5825d95317fd48458c786ea855eab803e48d0ea95',\n 'ox bb1b398f4e1627336b481ee5825d95317fd48458c786ea855eab803e48d0ea95',\n 'size 8963',\n 'm image/png',\n 'dim 367x106',\n 'blurhash L79O[V}Z1bI:BUS#w{ae0x1H}Fxa',\n ],\n ],\n content:\n 'https://blossom.ditto.pub/bb1b398f4e1627336b481ee5825d95317fd48458c786ea855eab803e48d0ea95.png',\n sig: '48442690b282b643d50752c0c22102f191c5a2402e075d92550ac1d1644c390841e3f3aad540974b38f9fd853a46f57712b8a88488c2b06ac12e5308864c2cb6',\n};\n\n/** kind:1111 top-level comment #3 (parent of the nested reply below). */\nconst COMMENT_3: NostrEvent = {\n id: '6f815f058c6f627eab6f5324f06cda29163e0232f410abf8eff22de250e188c1',\n pubkey: '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n created_at: 1789631422,\n kind: 1111,\n tags: [\n [\n 'E',\n '3bb49002757fe697f328a2825fdd00f48a8c376238db65b18906d52864692e2a',\n '',\n '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n ],\n ['K', '1621'],\n ['P', '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1'],\n [\n 'e',\n '3bb49002757fe697f328a2825fdd00f48a8c376238db65b18906d52864692e2a',\n '',\n '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n ],\n ['k', '1621'],\n ['p', '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1'],\n ],\n content:\n 'Currently, it does work to heal a private clear-text blossom server now that has existing submission blobs.\\n\\nThe UI will attempt to load the thumbnails when you press \"Show More\" if you have more than the initial set of submissions in your query, attempt to load them, then drop them out of the list. \\n\\nThis notification will pop-up and grow as the full submission files get pulled from the related endpoint to regenerate the thumbnail, then upload it. Deltas get broadcasted onto the network to notify of the update to the submission.',\n sig: '44763a88f67a5c0020d3b503e73b8229854a9445591fb7f57237ea3f92e8dcdf7ab645f631fd841801b77d6ebce0e2b78ce869ccadac604c5282a45518f69476',\n};\n\n/**\n * kind:1111 NESTED reply — lowercase `e`/`k`/`p` point at {@link COMMENT_3},\n * not the root issue, while uppercase `E`/`K`/`P` still name the root.\n */\nconst NESTED_REPLY: NostrEvent = {\n id: '952c1fe0a2e88c3c716b56aa2f695349a77220a211779de30e97b115ba94e084',\n pubkey: '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n created_at: 1789633147,\n kind: 1111,\n tags: [\n [\n 'E',\n '3bb49002757fe697f328a2825fdd00f48a8c376238db65b18906d52864692e2a',\n '',\n '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n ],\n ['K', '1621'],\n ['P', '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1'],\n [\n 'e',\n '6f815f058c6f627eab6f5324f06cda29163e0232f410abf8eff22de250e188c1',\n '',\n '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n ],\n ['k', '1111'],\n ['p', '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1'],\n ],\n content:\n \"I do need to handle this exception, so that we don't just reset or drop entire pools of submissions you might be looking at while things are healing\",\n sig: '315005f83203dcf83cb6cfe507a270e0400d659e3b8af8d8f42fecddc4fc05f1632d9df429e19550453e6961cffb4707e5093e9ece33521fe2ce8beb358d8332',\n};\n\n/** kind:1111 top-level comment #5 (thread closing note). */\nconst COMMENT_5: NostrEvent = {\n id: 'fdf52ad9ccd8038ca8d0a1ee73e508d3351edd3b80e703953646ee2c83366a1a',\n pubkey: '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n created_at: 1789644897,\n kind: 1111,\n tags: [\n [\n 'E',\n '3bb49002757fe697f328a2825fdd00f48a8c376238db65b18906d52864692e2a',\n '',\n '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n ],\n ['K', '1621'],\n ['P', '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1'],\n [\n 'e',\n '3bb49002757fe697f328a2825fdd00f48a8c376238db65b18906d52864692e2a',\n '',\n '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1',\n ],\n ['k', '1621'],\n ['p', '43d7f07d10b9e662745e10b2fc201a74dc178a308c31409350d06b04477816e1'],\n ],\n content:\n \"Closing for now. got the base in by f55d8a42c9fdff59c1982e1835a81ec9cc805043 , added some additional features & polish by 93a4e2f2c259676c75d8170faa0aea384b8719cb\\n\\nAll the DOM Reloads as thumbnails regenerate & take over do make the client feel laggy if you've got devtools open, but even with 300 thumbnails being processed with tagging events from the bulk uploader, it's so far been stable in it's tab. \\n\\nIt's much snappier without devtools.\\n\\nFairly happy with the memory footprint in this state, even if it's chrome. Granted, initial uploads are much heavier in the browser than this.\",\n sig: 'f97b7e8638383903e0245fae076f760d4c942949f782804aac437c06e55a9c05aad7de391b4a0a349bf7bcdc1c58d74a3f4348b0ae827af0625b359cc1fef7b2',\n};\n\n/** All five kind:1111 comments on the thread, oldest first. */\nexport const NGIT_COMMENT_THREAD_COMMENTS: NostrEvent[] = [\n COMMENT_1,\n COMMENT_2,\n COMMENT_3,\n NESTED_REPLY,\n COMMENT_5,\n];\n","/**\n * Real wire capture from wss://relay.ngit.dev (2026-09-17T16:00:01Z).\n *\n * A kind:1631 (\"applied\"/merged) status event on ngit's own `ngit` repo, in\n * NIP-10 marker form: `[\"e\", <target-id>, \"\", \"root\"]` plus the repo's `a`\n * coordinate tag — exactly the shape rig#153's status builder must produce\n * and its readers must resolve, instead of today's bare `[\"e\", <id>]`. Its\n * target, {@link NGIT_STATUS_NGIT_TARGET}, is the kind:1618 pull request the\n * status applies to (a `branch-name` tag rides along on it, relevant to a\n * neighbouring rig#153 ticket). Committed byte-for-byte as received — no\n * edited tags, no re-serialization, tag order preserved — so both events'\n * ids and signatures verify (see `ngit-fixtures.test.ts`).\n *\n * Captured with `scripts/capture-ngit-fixtures.mjs` (repo root); see that\n * file for the exact filter and how to re-run the capture.\n */\n\nimport type { NostrEvent } from '../remote-state.js';\n\nexport const NGIT_STATUS_NGIT_RELAY = 'wss://relay.ngit.dev';\nexport const NGIT_STATUS_NGIT_CAPTURED_AT = '2026-09-17T16:00:01Z';\nexport const NGIT_STATUS_NGIT_EVENT_ID =\n '7fa92c2970493b77974848db0c225e0c37fb6694eabed67387224a4711093b2f';\nexport const NGIT_STATUS_NGIT_TARGET_EVENT_ID =\n 'fabe42e7c41d4e43e3dcc78db20c3859a7696eb6bc655aed852741e074951c48';\n\n/** kind:1631 status (\"applied\") in NIP-10 marker form, targeting the PR below. */\nexport const NGIT_STATUS_NGIT: NostrEvent = {\n id: '7fa92c2970493b77974848db0c225e0c37fb6694eabed67387224a4711093b2f',\n pubkey: 'a008def15796fba9a0d6fab04e8fd57089285d9fd505da5a83fe8aad57a3564d',\n created_at: 1789656887,\n kind: 1631,\n tags: [\n [\n 'e',\n 'fabe42e7c41d4e43e3dcc78db20c3859a7696eb6bc655aed852741e074951c48',\n '',\n 'root',\n ],\n [\n 'a',\n '30617:a008def15796fba9a0d6fab04e8fd57089285d9fd505da5a83fe8aad57a3564d:ngit',\n 'wss://nos.lol/',\n ],\n ['alt', 'Status change'],\n [\n 'p',\n '657c9f566a2627ad76196695361e1d814bdb49e87b90f4db818fb91c59dc142a',\n 'wss://relay.damus.io/',\n ],\n ['merge-commit', '715f0fdeca1c7dd68ad144f36112e02cab58a264'],\n ['r', '715f0fdeca1c7dd68ad144f36112e02cab58a264'],\n ],\n content: '',\n sig: 'aea241f5f67ec7851b25a1c3309e922ea858b5f0f4055b5b95e7eddcdd2e46f909fb67d2dc8e94a32a960ffc6e0fa238c80aad7b9b5a915111960a7dd780c57b',\n};\n\n/** kind:1618 pull request that {@link NGIT_STATUS_NGIT} targets. */\nexport const NGIT_STATUS_NGIT_TARGET: NostrEvent = {\n id: 'fabe42e7c41d4e43e3dcc78db20c3859a7696eb6bc655aed852741e074951c48',\n pubkey: '657c9f566a2627ad76196695361e1d814bdb49e87b90f4db818fb91c59dc142a',\n created_at: 1789630888,\n kind: 1618,\n tags: [\n [\n 'a',\n '30617:a008def15796fba9a0d6fab04e8fd57089285d9fd505da5a83fe8aad57a3564d:ngit',\n 'wss://relay.ngit.dev',\n ],\n [\n 'subject',\n 'Stabilize release-edit metadata tests without changing release dates',\n ],\n [\n 'alt',\n 'git Pull Request: Stabilize release-edit metadata tests without changing release dates',\n ],\n ['branch-name', 'release-edit-ordering-fixture'],\n ['r', '26689f97810fc656c7134c76e2a37d33b2e40ce7'],\n ['c', '21e185c74370b43015705e4ff3dd0155e6ba5732'],\n [\n 'clone',\n 'https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit.git',\n ],\n ['merge-base', '24b90e585a184d14840489ccda008bf0389c6eea'],\n ['p', 'a008def15796fba9a0d6fab04e8fd57089285d9fd505da5a83fe8aad57a3564d'],\n ],\n content:\n 'The legacy-release metadata test could fail with replacement_ordering_exhausted after its first edit randomly produced a very low event ID. This caused the unrelated CI failure on the whoami PR.\\n\\nSplit omission and explicit commit override into independent scenarios. Each starts with a signed predecessor in the upper half of the event-ID space, constructed with a bounded search. Both assert the relay-visible metadata, preserved release date, and lower replacement ID. A separate unit test uses the exact CI predecessor ID to verify the intentional exhaustion error.\\n\\nThis is a test-only fix. Production keeps its bounded search and date-preservation contract; this does not make arbitrary repeated fixed-date edits always succeed.\\n\\nValidation: all 22 release integration tests and 17 ordering unit tests pass; all-targets Clippy with warnings denied, formatting, and diff checks pass.\\n\\nCI failure being addressed: https://blossom.ditto.pub/6ec38c668665ed7201b6aa5eaf0606f226e391ff1aa4c8f614611d7925cb15e6.txt',\n sig: 'cec1e1d7a7e04adec03e5d21078587ea086ea47cd31111721112e58e68b78222a13e2b24edb81a0243b9f069a0c01cfd45f7bdbb12116108eee8880a566a5663',\n};\n","/**\n * Real NIP-34 wire fixtures captured from `wss://relay.ngit.dev` (rig#155,\n * spec rig#153). Conformance is tested against what the incumbent (ngit)\n * actually publishes, not against our reading of the NIP.\n *\n * Every event here is committed exactly as the relay sent it — no edited\n * tags, no re-serialization — so `ngit-fixtures.test.ts` can verify each\n * one's id and signature. `@toon-protocol/rig` imports these fixtures from\n * this module and re-exports them from its package index.\n * `@toon-protocol/rig-web` tests use a self-contained COPY of this same\n * captured data instead of importing this package directly\n * (`packages/rig-web/src/web/__fixtures__/ngit-wire.ts`) — rig-web's\n * typecheck gate runs before `@toon-protocol/rig` is built, so an import\n * resolved through its built `dist/` would fail there even though it can\n * look fine locally with a stale `dist/`. See that file's header for the\n * full rationale.\n *\n * Re-capture with `scripts/capture-ngit-fixtures.mjs` (repo root); update\n * BOTH this package's fixtures and rig-web's copy from the same capture.\n */\n\nexport {\n NGIT_ANNOUNCEMENT_WYRD,\n NGIT_ANNOUNCEMENT_WYRD_CAPTURED_AT,\n NGIT_ANNOUNCEMENT_WYRD_EVENT_ID,\n NGIT_ANNOUNCEMENT_WYRD_RELAY,\n} from './ngit-announcement-wyrd.js';\n\nexport {\n NGIT_STATE_NGIT,\n NGIT_STATE_NGIT_CAPTURED_AT,\n NGIT_STATE_NGIT_EVENT_ID,\n NGIT_STATE_NGIT_RELAY,\n} from './ngit-state-ngit.js';\n\nexport {\n NGIT_COMMENT_THREAD_COMMENTS,\n NGIT_COMMENT_THREAD_CAPTURED_AT,\n NGIT_COMMENT_THREAD_COMMENT_EVENT_IDS,\n NGIT_COMMENT_THREAD_NESTED_REPLY_ID,\n NGIT_COMMENT_THREAD_NESTED_REPLY_PARENT_ID,\n NGIT_COMMENT_THREAD_RELAY,\n NGIT_COMMENT_THREAD_ROOT,\n NGIT_COMMENT_THREAD_ROOT_EVENT_ID,\n} from './ngit-comment-thread-hydrusui.js';\n\nexport {\n NGIT_STATUS_NGIT,\n NGIT_STATUS_NGIT_CAPTURED_AT,\n NGIT_STATUS_NGIT_EVENT_ID,\n NGIT_STATUS_NGIT_RELAY,\n NGIT_STATUS_NGIT_TARGET,\n NGIT_STATUS_NGIT_TARGET_EVENT_ID,\n} from './ngit-status-ngit.js';\n\nimport type { NostrEvent } from '../remote-state.js';\nimport { NGIT_ANNOUNCEMENT_WYRD } from './ngit-announcement-wyrd.js';\nimport {\n NGIT_COMMENT_THREAD_COMMENTS,\n NGIT_COMMENT_THREAD_ROOT,\n} from './ngit-comment-thread-hydrusui.js';\nimport { NGIT_STATE_NGIT } from './ngit-state-ngit.js';\nimport {\n NGIT_STATUS_NGIT,\n NGIT_STATUS_NGIT_TARGET,\n} from './ngit-status-ngit.js';\n\n/** Every captured event, flattened — for the id/signature verification test. */\nexport const ALL_NGIT_FIXTURE_EVENTS: NostrEvent[] = [\n NGIT_ANNOUNCEMENT_WYRD,\n NGIT_STATE_NGIT,\n NGIT_COMMENT_THREAD_ROOT,\n ...NGIT_COMMENT_THREAD_COMMENTS,\n NGIT_STATUS_NGIT,\n NGIT_STATUS_NGIT_TARGET,\n];\n"],"mappings":"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","names":[]}
@@ -93,12 +93,28 @@ declare function createGitTag(opts: {
93
93
  * All builders return UnsignedEvent — the caller signs with their keypair
94
94
  * via finalizeEvent() and publishes through a Publisher (#226). Tag
95
95
  * structures follow the NIP-34 spec and `@toon-protocol/core/nip34`.
96
+ *
97
+ * The wire contract — every tag each builder writes, the NIP-34 clause it
98
+ * follows, and any legacy shape still read (with its end condition) — is
99
+ * `docs/nip34-wire-shapes.md`. Read that before changing a tag shape here;
100
+ * update it after. Where the two disagree, this file's actual builders are
101
+ * the ground truth and the document is the bug.
96
102
  */
97
103
 
98
104
  /** Repository State (refs) — replaceable, pairs with kind:30617 via `d` tag. */
99
105
  declare const REPOSITORY_STATE_KIND = 30618;
100
- /** Comment on an issue or patch (NIP-22 style threading within NIP-34). */
101
- declare const COMMENT_KIND = 1622;
106
+ /**
107
+ * Comment on an issue or patch — NIP-22 `kind:1111`, which NIP-34's "Replies"
108
+ * clause mandates ("Replies … should follow NIP-22 comment"). This is the kind
109
+ * rig WRITES (rig#159).
110
+ */
111
+ declare const COMMENT_KIND = 1111;
112
+ /**
113
+ * rig's pre-#159 private comment dialect. READ-ONLY: never written again, kept
114
+ * so threads published before that release keep rendering. Every reader merges
115
+ * it with {@link COMMENT_KIND} by `created_at`.
116
+ */
117
+ declare const LEGACY_COMMENT_KIND = 1622;
102
118
  interface UnsignedEvent {
103
119
  kind: number;
104
120
  content: string;
@@ -164,24 +180,67 @@ declare function isValidEvmPayoutAddress(address: string): boolean;
164
180
  */
165
181
  declare function parsePayout(tags: string[][]): PayoutPointer | null;
166
182
  /**
167
- * Build a kind:30617 repository announcement event.
168
- *
169
- * @param repoId - Repository identifier (d tag)
170
- * @param name - Human-readable repository name
171
- * @param description - Repository description
172
- * @param maintainers - Optional declared maintainer pubkeys (hex). Emitted as
173
- * a single `["maintainers", …]` tag when non-empty; duplicate and non-64-hex
174
- * values are dropped. The owner (the signer) is an implicit maintainer and
175
- * need not be listed — if passed it is emitted, which is harmless since the
176
- * owner is authorized regardless. See {@link MAINTAINERS_TAG}.
177
- * @param payout - Optional declared payout pointer (rig#92). Emitted as a
178
- * single `["payout", "evm", <address>]` tag when given; omit (or pass
179
- * `null`) to leave the repo with no payout pointer. See {@link PAYOUT_TAG}.
183
+ * Maximum number of `arweave` (git SHA → Arweave txId) tags on ONE kind:30618
184
+ * event, enforced on write (here and in `executePush`) and on read
185
+ * (`parseRefsEvent`, rig-web's `parseRepoRefs`). Before this cap the map was
186
+ * merged cumulatively on every push and bounded by nothing, so a large repo
187
+ * eventually produced an event relays reject (#162).
188
+ *
189
+ * WHY 2000 — the arithmetic, in the event's JSON serialization:
190
+ *
191
+ * one arweave tag `["arweave","<40-hex sha>","<43-char txId>"]`
192
+ * = 1 + 9 + 1 + 42 + 1 + 45 + 1 = 100 bytes, +1 comma = 101
193
+ * 2000 of them = 202,000 bytes ≈ 197 KiB ← the map's whole budget
194
+ *
195
+ * Which limit is that 197 KiB measured against? rig's own relay
196
+ * (`relay-ws.devnet.toonprotocol.dev`) advertises no `limitation` in its
197
+ * NIP-11 document — probed 2026-09-17, the HTTPS origin answers `Upgrade
198
+ * Required` rather than a relay-information document — so there is no
199
+ * self-declared number to size against. The smallest limit any relay surveyed
200
+ * for #153 declares is `relay.ngit.dev`'s 5 MiB message cap (probed the same
201
+ * day), and that is the figure used here: the map is capped at under 4% of
202
+ * it. That leaves the rest of the budget to the refs, which are the other
203
+ * term in the same event:
204
+ *
205
+ * `["r","<refname>","<sha>"]`, and `isSafeRefname` admits refnames up to
206
+ * 1024 bytes → ≤ 1076 bytes each. Every rig reader ingests at most
207
+ * MAX_REFS_PER_EVENT = 1000 of them, and 1000 at that adversarial width is
208
+ * ≈ 1.03 MiB, so a readable event tops out near 1.23 MiB — a quarter of the
209
+ * 5 MiB limit. With realistic refnames (≤ 64 bytes) a full-cap event is
210
+ * ≈ 260 KiB.
211
+ *
212
+ * NOTE the asymmetry: the 1000-ref cap is a READ-side constant. `buildRepoRefs`
213
+ * still writes every ref it is given, so a repo with more than 1000 refs can
214
+ * publish an event past the figures above — that is the ref tags' problem, not
215
+ * the map's, and it is the ref shape work's to fix (#153 §Repository state).
216
+ * This cap's job is to stop the OBJECT MAP being the term that grows without
217
+ * limit, which it was: it grew with the object count, not the ref count.
218
+ *
219
+ * Dropping an entry loses nothing: the object is still on Arweave under its
220
+ * `Git-SHA` / `Repo` tags and resolves through the GraphQL resolver, which is
221
+ * already the documented fallback (`RemoteState.resolveMissing`). The map is
222
+ * a cache, not the index — so push planning must treat "absent from the map"
223
+ * as "ask the resolver", NEVER as "needs upload".
180
224
  */
181
- declare function buildRepoAnnouncement(repoId: string, name: string, description: string, maintainers?: string[], payout?: PayoutPointer | null): UnsignedEvent;
225
+ declare const MAX_ARWEAVE_TAGS_PER_EVENT = 2000;
182
226
  /**
183
227
  * Build a kind:30618 repository refs/state event.
184
228
  *
229
+ * Writes each ref in BOTH shapes, with identical SHAs (rig#157, dual-write
230
+ * window): the NIP-34 shape `[<refPath>, <sha>]`, where the ref path IS the
231
+ * tag name — what ngit, gitworkshop.dev and every other conformant client
232
+ * read — alongside rig's legacy `["r", <refPath>, <sha>]`, so rig installs
233
+ * older than this release keep fetching. Both shapes are read by
234
+ * {@link parseStateRefTags} in `./nip34-refs.ts` and by any legacy-only
235
+ * reader that only knows the `r` shape. `HEAD` and `arweave` tags are
236
+ * unaffected by this dual-write. The legacy write is removed in a later,
237
+ * separately ticketed change once older rig versions are unsupported.
238
+ *
239
+ * At most {@link MAX_ARWEAVE_TAGS_PER_EVENT} `arweave` tags are emitted; a
240
+ * larger `arweaveMap` is truncated to its first entries in iteration order,
241
+ * so the caller decides priority (see `executePush`) and a repo under the cap
242
+ * publishes byte-identically to before the cap existed.
243
+ *
185
244
  * @param repoId - Repository identifier (d tag, matches kind:30617)
186
245
  * @param refs - Map of ref paths to commit SHAs (e.g., { 'refs/heads/main': 'abc123' })
187
246
  * @param arweaveMap - Map of git SHAs to Arweave transaction IDs
@@ -198,16 +257,67 @@ declare function buildRepoRefs(repoId: string, refs: Record<string, string>, arw
198
257
  */
199
258
  declare function buildIssue(repoOwnerPubkey: string, repoId: string, title: string, body: string, labels?: string[]): UnsignedEvent;
200
259
  /**
201
- * Build a kind:1622 comment event.
260
+ * The event a comment thread hangs off: the kind:1621 issue or kind:1617
261
+ * patch being discussed — NEVER the repository. Becomes the comment's
262
+ * uppercase NIP-22 root scope (`E`/`K`/`P`).
263
+ */
264
+ interface CommentRoot {
265
+ /** Event id of the issue/patch (uppercase `E`). */
266
+ eventId: string;
267
+ /** Kind of that event, e.g. 1621 or 1617 (uppercase `K`). */
268
+ kind: number;
269
+ /** Pubkey of that event's author (uppercase `P`). */
270
+ authorPubkey: string;
271
+ }
272
+ /**
273
+ * The kind:1111 comment being replied to, when the new comment is a nested
274
+ * reply rather than a top-level comment. Becomes the lowercase parent
275
+ * (`e`/`k`/`p`), with `k` always {@link COMMENT_KIND}.
276
+ */
277
+ interface CommentParent {
278
+ /** Event id of the comment replied to (lowercase `e`). */
279
+ eventId: string;
280
+ /** Pubkey of that comment's author (lowercase `p`). */
281
+ authorPubkey: string;
282
+ }
283
+ /**
284
+ * Build a NIP-22 kind:1111 comment on a NIP-34 issue or patch (rig#159).
285
+ *
286
+ * NIP-22: "Comments MUST point to the root scope using uppercase tag names
287
+ * (e.g. `K`, `E`, `A` or `I`)" and "MUST point to the parent item with
288
+ * lowercase ones (e.g. `k`, `e`, `a` or `i`)", with "`P` for the root scope
289
+ * and `p` for the author of the parent item". So:
290
+ *
291
+ * - top-level comment → parent === root: `e`/`k`/`p` repeat `E`/`K`/`P`;
292
+ * - reply → `e` is the parent comment, `k` is `1111`, `p` its author, while
293
+ * `E`/`K`/`P` still name the issue/patch at the top of the thread.
294
+ *
295
+ * The repo coordinate rides along as an ordinary lowercase `a` tag so a
296
+ * subscription can scope a whole repo's comments with one `#a` filter (spec
297
+ * rig#153). It is deliberately NOT the NIP-22 parent pointer — the parent is
298
+ * the `e` tag — so it is emitted last, after the six threading tags.
202
299
  *
203
300
  * @param repoOwnerPubkey - Pubkey of the repository owner
204
- * @param repoId - Repository identifier
205
- * @param issueOrPrEventId - Event ID of the issue or PR being commented on
206
- * @param authorPubkey - Pubkey of the issue/PR author (NIP-34 `p` tag for threading), NOT the comment author
301
+ * @param repoId - Repository identifier (NIP-34 `d` tag)
302
+ * @param root - The issue/patch the thread hangs off
207
303
  * @param body - Comment body (Markdown content)
208
- * @param marker - Event reference marker: 'root' or 'reply' (default: 'reply')
304
+ * @param parent - The kind:1111 comment being replied to; omit for a
305
+ * top-level comment, whose parent is the root itself
209
306
  */
210
- declare function buildComment(repoOwnerPubkey: string, repoId: string, issueOrPrEventId: string, authorPubkey: string, body: string, marker?: 'root' | 'reply'): UnsignedEvent;
307
+ declare function buildComment(repoOwnerPubkey: string, repoId: string, root: CommentRoot, body: string, parent?: CommentParent): UnsignedEvent;
308
+ /**
309
+ * Does `event` belong to the comment thread rooted at `rootEventId`?
310
+ *
311
+ * kind:1111 membership is decided by the **uppercase** `E` tag, matched
312
+ * case-sensitively — the same rule rig already applies to kind:1619 PR
313
+ * updates. A kind:1111 whose only match is a lowercase `e` is a reply to
314
+ * something else and is NOT part of this thread. Legacy kind:1622 has no
315
+ * uppercase form: its `e` tag is the thread root.
316
+ */
317
+ declare function commentBelongsToThread(event: {
318
+ kind: number;
319
+ tags: string[][];
320
+ }, rootEventId: string): boolean;
211
321
  /**
212
322
  * Build a kind:1617 patch event.
213
323
  *
@@ -222,7 +332,11 @@ declare function buildComment(repoOwnerPubkey: string, repoId: string, issueOrPr
222
332
  * @param repoId - Repository identifier
223
333
  * @param title - Patch/PR title (subject tag)
224
334
  * @param commits - Array of { sha, parentSha } for commit and parent-commit tags
225
- * @param branchTag - Branch name for the t tag
335
+ * @param branchTag - Branch name, written as the `branch-name` tag (#161) —
336
+ * the wider NIP-34 ecosystem's spelling (used verbatim by
337
+ * kind:1618 pull requests; confirmed against the NIP-34
338
+ * source at implementation time). Never written to `t`:
339
+ * that tag is reserved for real labels.
226
340
  * @param content - Real `git format-patch` text (NIP-34 patch body); defaults
227
341
  * to '' for callers that only reference commits by tag
228
342
  * @param description - PR body/cover text (`description` tag) — kept out of
@@ -237,11 +351,23 @@ type StatusKind = typeof STATUS_OPEN_KIND | typeof STATUS_APPLIED_KIND | typeof
237
351
  /**
238
352
  * Build a status event (kind 1630-1633).
239
353
  *
354
+ * rig#153/#160: the `e` tag carries the NIP-10 `root` marker
355
+ * (`["e", <target>, "", "root"]`) rather than the old bare `["e", <id>]`, so
356
+ * clients that resolve a status's root via the marker (ngit's
357
+ * `get_event_root`, among others) honor rig statuses. The repo `a` tag rides
358
+ * along so a status stream can be scoped to the repository without first
359
+ * resolving the target. Readers (this package's tracker and rig-web's
360
+ * parsers) accept BOTH this marker form and the legacy bare form — a status
361
+ * is matched by `tags.find(t => t[0] === 'e')?.[1] === targetEventId`
362
+ * regardless of any trailing marker elements.
363
+ *
364
+ * @param repoOwnerPubkey - Pubkey of the repository owner
365
+ * @param repoId - Repository identifier
240
366
  * @param targetEventId - Event ID of the patch, PR, or issue being updated
241
367
  * @param statusKind - One of 1630 (open), 1631 (applied), 1632 (closed), 1633 (draft)
242
- * @param targetPubkey - Optional pubkey of the target event author (p tag per NIP-34 StatusEvent)
368
+ * @param targetPubkey - Optional pubkey of the target event author (p tag per NIP-34 StatusEvent), when known
243
369
  */
244
- declare function buildStatus(targetEventId: string, statusKind: StatusKind, targetPubkey?: string): UnsignedEvent;
370
+ declare function buildStatus(repoOwnerPubkey: string, repoId: string, targetEventId: string, statusKind: StatusKind, targetPubkey?: string): UnsignedEvent;
245
371
 
246
372
  /**
247
373
  * Publisher — the paid-write seam between push planning (this package) and
@@ -432,4 +558,4 @@ interface Publisher {
432
558
  publishEvent(event: UnsignedEvent, relayUrls: string[]): Promise<PublishReceipt>;
433
559
  }
434
560
 
435
- export { type BlobUpload as B, COMMENT_KIND as C, type FeeRates as F, type GitObjectUpload as G, MAINTAINERS_TAG as M, type Publisher as P, REPOSITORY_STATE_KIND as R, type StoreJobRequest as S, type UploadReceipt as U, type StoreJobResponse as a, type UnsignedEvent as b, type PublishReceipt as c, type GitObjectType as d, type PayoutPointer as e, type GitObject as f, MAX_OBJECT_SIZE as g, PAYOUT_TAG as h, type PayoutChain as i, type StatusKind as j, authorizedStatusAuthors as k, buildComment as l, buildIssue as m, buildPatch as n, buildRepoAnnouncement as o, buildRepoRefs as p, buildStatus as q, createGitBlob as r, createGitCommit as s, createGitTag as t, createGitTree as u, hashGitObject as v, isValidEvmPayoutAddress as w, parseMaintainers as x, parsePayout as y };
561
+ export { parseMaintainers as A, type BlobUpload as B, COMMENT_KIND as C, parsePayout as D, type FeeRates as F, type GitObjectUpload as G, LEGACY_COMMENT_KIND as L, MAINTAINERS_TAG as M, type Publisher as P, REPOSITORY_STATE_KIND as R, type StoreJobRequest as S, type UploadReceipt as U, type StoreJobResponse as a, type UnsignedEvent as b, type PublishReceipt as c, type PayoutPointer as d, type GitObjectType as e, type CommentParent as f, type CommentRoot as g, type GitObject as h, MAX_ARWEAVE_TAGS_PER_EVENT as i, MAX_OBJECT_SIZE as j, PAYOUT_TAG as k, type PayoutChain as l, type StatusKind as m, authorizedStatusAuthors as n, buildComment as o, buildIssue as p, buildPatch as q, buildRepoRefs as r, buildStatus as s, commentBelongsToThread as t, createGitBlob as u, createGitCommit as v, createGitTag as w, createGitTree as x, hashGitObject as y, isValidEvmPayoutAddress as z };
@@ -1,4 +1,4 @@
1
- import { P as Publisher, F as FeeRates, G as GitObjectUpload, U as UploadReceipt, B as BlobUpload, S as StoreJobRequest, a as StoreJobResponse, b as UnsignedEvent, c as PublishReceipt } from '../publisher-Cf6tKnZ3.js';
1
+ import { P as Publisher, F as FeeRates, G as GitObjectUpload, U as UploadReceipt, B as BlobUpload, S as StoreJobRequest, a as StoreJobResponse, b as UnsignedEvent, c as PublishReceipt } from '../publisher-DgbfziOm.js';
2
2
  import '@toon-protocol/core/nip34';
3
3
 
4
4
  /**
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  fetchRemoteState,
6
6
  resolveIdentity
7
- } from "./chunk-HMED76GU.js";
7
+ } from "./chunk-JBFBD7HV.js";
8
8
  import {
9
9
  ChannelMapStore,
10
10
  RIG_CHANNEL_MAP_FILENAME
@@ -386,4 +386,4 @@ export {
386
386
  resolveConnectorSettings,
387
387
  resolveDestinations
388
388
  };
389
- //# sourceMappingURL=standalone-mode-W3ZEKC6M.js.map
389
+ //# sourceMappingURL=standalone-mode-UQSP4677.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toon-protocol/rig",
3
- "version": "4.5.0",
3
+ "version": "4.6.0",
4
4
  "description": "Git-to-TOON write path core — build git objects and NIP-34 events for the Rig control plane",
5
5
  "license": "MIT",
6
6
  "author": "Jonathan Green",