diff --git a/crates/runx-cli/src/official_skills.rs b/crates/runx-cli/src/official_skills.rs index a3c4ff239..8d0f90590 100644 --- a/crates/runx-cli/src/official_skills.rs +++ b/crates/runx-cli/src/official_skills.rs @@ -225,6 +225,11 @@ pub(crate) const OFFICIAL_SKILLS: &[OfficialSkillLockEntry] = &[ version: "sha-2555f66bde78", digest: "991ec474c6013ce9d29d84df810c14db567328607018c4de9606ba3952d8b9c7", }, + OfficialSkillLockEntry { + skill_id: "runx/prospect-sequence", + version: "sha-600560c04285", + digest: "0569de06069526ada7ce1bb23d5979348696f0e77b6c602ec506850626c19a14", + }, OfficialSkillLockEntry { skill_id: "runx/receipt-auditor", version: "sha-acbe46a62851", diff --git a/packages/cli/src/official-skills.lock.json b/packages/cli/src/official-skills.lock.json index aa39784c0..f54cdac64 100644 --- a/packages/cli/src/official-skills.lock.json +++ b/packages/cli/src/official-skills.lock.json @@ -300,6 +300,13 @@ "catalog_visibility": "public", "catalog_role": "context" }, + { + "skill_id": "runx/prospect-sequence", + "version": "sha-600560c04285", + "digest": "0569de06069526ada7ce1bb23d5979348696f0e77b6c602ec506850626c19a14", + "catalog_visibility": "public", + "catalog_role": "canonical" + }, { "skill_id": "runx/receipt-auditor", "version": "sha-acbe46a62851", diff --git a/skills/prospect-sequence/README.md b/skills/prospect-sequence/README.md new file mode 100644 index 000000000..b48c3e801 --- /dev/null +++ b/skills/prospect-sequence/README.md @@ -0,0 +1,41 @@ +# prospect-sequence + +Native runx skill for sourced outbound sequence drafting. It accepts a prospect, +ICP, and allowlisted public source snippets, then emits a cited four-touch +sequence plus a gated `send_proposal`. It never sends messages or mutates an +external system. + +## Local Harness + +```bash +runx harness ./skills/prospect-sequence --json +``` + +Harness cases: + +- `public-sources-yield-sourced-sequence` +- `private-or-missing-sources-refuse` + +## Run + +```bash +runx skill ./skills/prospect-sequence \ + --input-json prospect='{"company":"Northwind Software","contact":"Head of Platform"}' \ + --input-json icp='{"product":"Runx governed agent workflows","audience":"platform and security operators","pain_points":["manual release evidence review"],"value_props":["produce sealed evidence packets before operational changes"]}' \ + --input-json source_allowlist='{"allowed_hosts":["example.com"],"sources":[{"url":"https://example.com/northwind-release-notes","title":"Northwind release notes","text":"Northwind Software described manual release evidence review in its public release notes."}]}' \ + --json +``` + +## Publish + +```bash +runx login --provider github --for publish +runx registry publish ./skills/prospect-sequence --registry https://api.runx.ai --json +``` + +Published package: + +```bash +runx add zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai +runx skill zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai +``` diff --git a/skills/prospect-sequence/SKILL.md b/skills/prospect-sequence/SKILL.md new file mode 100644 index 000000000..891cb66d9 --- /dev/null +++ b/skills/prospect-sequence/SKILL.md @@ -0,0 +1,92 @@ +--- +name: prospect-sequence +version: 0.1.0 +description: Research a prospect from allowlisted public sources, synthesize a cited outreach angle, draft a multi-touch sequence, and emit only a gated send proposal. +source: + type: cli-tool + command: node + args: + - run.mjs +links: + source: https://github.com/zdfgu113/runx/tree/codex/prospect-sequence/skills/prospect-sequence +runx: + category: growth + input_resolution: + required: + - prospect + - icp + - source_allowlist +--- + +# Prospect Sequence + +Research a prospect from allowlisted public source snippets, synthesize a cited +account angle, draft a four-touch outreach sequence, and emit a gated +`send_proposal` for the `send-as` catalog skill. The skill never sends a +message, signs an external request, mutates a CRM, scrapes private networks, or +uses non-allowlisted sources. + +## When to use this skill + +Use it when an operator needs a checkable, source-backed outreach draft before a +human or governed send skill decides whether anything should be sent. The output +keeps every account-specific claim tied to a supplied public source URL. + +## When not to use this skill + +Do not use it for scraping, private-network targets, off-allowlist URLs, +unsourced account claims, spam sending, or bypassing consent and operator +approval. If no allowlisted public source is present, the skill refuses and emits +no send proposal. + +## Procedure + +1. Require typed inputs `prospect`, `icp`, and `source_allowlist`. +2. Accept only `http` or `https` source URLs whose host appears in + `source_allowlist.allowed_hosts` when that list is supplied. +3. Refuse loopback, RFC1918, `.local`, non-HTTP, missing-text, or + off-allowlist sources before using any account fact. +4. Extract account facts only from the accepted public source snippets. +5. Synthesize one outreach angle that cites the source URL used for the claim. +6. Draft a four-touch sequence that stays scoped to the cited public evidence. +7. Emit `send_proposal` only as a gated proposed Effect for `send-as`; the + proposal records that this skill performs no send. +8. Refuse with `no_allowlisted_public_sources` when the source evidence is too + thin or blocked by the SSRF guard. + +## Output + +The runner emits `runx.prospect_sequence.v1` with: + +- `summary`: short decision summary. +- `research`: `{ sources[], angle }`, with source URLs and extracted facts. +- `sequence`: ordered outreach touches. +- `send_proposal`: gated proposal for `send-as`, or `null` on refusal. +- `refusal`: refusal reason and blocked source details when applicable. + +## Example + +```bash +runx skill ./skills/prospect-sequence \ + --input-json prospect='{"company":"Northwind Software","contact":"Head of Platform"}' \ + --input-json icp='{"product":"Runx governed agent workflows","audience":"platform and security operators","pain_points":["manual release evidence review"],"value_props":["produce sealed evidence packets before operational changes"]}' \ + --input-json source_allowlist='{"allowed_hosts":["example.com"],"sources":[{"url":"https://example.com/northwind-release-notes","title":"Northwind release notes","text":"Northwind Software described manual release evidence review in its public release notes."}]}' \ + --json +``` + +## Inputs + +- `prospect` (required): object with `company` and optional `contact` or `role`. +- `icp` (required): object with `product`, `audience`, `pain_points`, and + `value_props`. +- `source_allowlist` (required): object with `allowed_hosts` and `sources[]`. + Each source must include `url`, `title`, and public source `text`. + +## Safety Notes + +- The runner is deterministic over supplied source snippets; a consuming product + can hydrate those snippets through its governed HTTP front. +- The SSRF guard rejects private, loopback, local, off-allowlist, or non-HTTP + targets. +- The `send_proposal` is not a send. It is a gated effect proposal that a + separate `send-as` skill must review and execute only with policy approval. diff --git a/skills/prospect-sequence/X.yaml b/skills/prospect-sequence/X.yaml new file mode 100644 index 000000000..81fd7abfb --- /dev/null +++ b/skills/prospect-sequence/X.yaml @@ -0,0 +1,101 @@ +skill: prospect-sequence +version: "0.1.0" + +catalog: + kind: skill + audience: public + visibility: public + role: canonical + +harness: + cases: + - name: public-sources-yield-sourced-sequence + runner: decide + inputs: + prospect: + company: Northwind Software + contact: Head of Platform + icp: + product: Runx governed agent workflows + audience: platform and security operators + pain_points: + - manual release evidence review + - supply-chain approval drift + value_props: + - produce sealed evidence packets before operational changes + - keep human approval gates before sending or mutating state + source_allowlist: + allowed_hosts: + - example.com + sources: + - url: https://example.com/northwind-release-notes + title: Northwind release notes + text: Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes. + - url: https://example.com/northwind-security + title: Northwind security page + text: Northwind Software says platform and security operators review changes before production release. + expect: + status: sealed + receipt: + schema: runx.receipt.v1 + state: sealed + disposition: closed + reason_code: process_closed + + - name: private-or-missing-sources-refuse + runner: decide + inputs: + prospect: + company: Northwind Software + contact: Head of Platform + icp: + product: Runx governed agent workflows + audience: platform operators + pain_points: + - manual release evidence review + value_props: + - produce sealed evidence packets + source_allowlist: + allowed_hosts: + - example.com + sources: + - url: http://127.0.0.1/internal-account-notes + title: Internal account notes + text: Private CRM note that must not be used. + expect: + status: failure + receipt: + schema: runx.receipt.v1 + state: sealed + disposition: failed + reason_code: process_failed + +runners: + decide: + default: true + type: cli-tool + command: node + args: + - run.mjs + outputs: + summary: string + research: json + sequence: json + send_proposal: json + refusal: json + artifacts: + wrap_as: prospect_sequence_decision + packet: runx.prospect_sequence.v1 + inputs: + prospect: + type: json + required: true + description: Prospect account and optional contact or role. + icp: + type: json + required: true + description: Product, audience, pain points, and value propositions used to shape the angle. + source_allowlist: + type: json + required: true + description: Allowed public hosts and source snippets hydrated through the governed HTTP front. diff --git a/skills/prospect-sequence/fixtures/dogfood-input.json b/skills/prospect-sequence/fixtures/dogfood-input.json new file mode 100644 index 000000000..edf987c61 --- /dev/null +++ b/skills/prospect-sequence/fixtures/dogfood-input.json @@ -0,0 +1,35 @@ +{ + "prospect": { + "company": "Northwind Software", + "contact": "Head of Platform" + }, + "icp": { + "product": "Runx governed agent workflows", + "audience": "platform and security operators", + "pain_points": [ + "manual release evidence review", + "supply-chain approval drift" + ], + "value_props": [ + "produce sealed evidence packets before operational changes", + "keep human approval gates before sending or mutating state" + ] + }, + "source_allowlist": { + "allowed_hosts": [ + "example.com" + ], + "sources": [ + { + "url": "https://example.com/northwind-release-notes", + "title": "Northwind release notes", + "text": "Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes." + }, + { + "url": "https://example.com/northwind-security", + "title": "Northwind security page", + "text": "Northwind Software says platform and security operators review changes before production release." + } + ] + } +} diff --git a/skills/prospect-sequence/fixtures/private-target-input.json b/skills/prospect-sequence/fixtures/private-target-input.json new file mode 100644 index 000000000..d2ae18ee6 --- /dev/null +++ b/skills/prospect-sequence/fixtures/private-target-input.json @@ -0,0 +1,28 @@ +{ + "prospect": { + "company": "Northwind Software", + "contact": "Head of Platform" + }, + "icp": { + "product": "Runx governed agent workflows", + "audience": "platform operators", + "pain_points": [ + "manual release evidence review" + ], + "value_props": [ + "produce sealed evidence packets" + ] + }, + "source_allowlist": { + "allowed_hosts": [ + "example.com" + ], + "sources": [ + { + "url": "http://127.0.0.1/internal-account-notes", + "title": "Internal account notes", + "text": "Private CRM note that must not be used." + } + ] + } +} diff --git a/skills/prospect-sequence/references/dogfood-receipt.json b/skills/prospect-sequence/references/dogfood-receipt.json new file mode 100644 index 000000000..825a861cf --- /dev/null +++ b/skills/prospect-sequence/references/dogfood-receipt.json @@ -0,0 +1 @@ +{"schema":"runx.receipt.v1","id":"sha256:9d5e4f26a9e8b99f4eabaa6c7e50626a45498446b900872d236bbc053814a3df","created_at":"2026-06-30T06:31:48.143Z","canonicalization":"runx.receipt.c14n.v1","issuer":{"type":"hosted","kid":"runx-demo-key","public_key_sha256":"sha256:3097e2dee2cb4a34b53840cdb705aed71067c36f68db0e0f559c3f3fa043315f"},"signature":{"alg":"Ed25519","value":"base64:fzpEn56LLoxmAO0ZyGIl5W_W9nnfJtdwo9BauUiLGTbC4eooMImhZC_qp6kZWgTdhUh7sHBMoP7BQRV5CJ5GDg"},"digest":"sha256:9ec5758e28d771aacadb71a8be2767b0b219e363ecd77ed131be0d681c16450c","idempotency":{"intent_key":"sha256:run_decide_645a58d0d829-decide-intent","trigger_fingerprint":"sha256:run_decide_645a58d0d829-decide-trigger","content_hash":"sha256:run_decide_645a58d0d829-decide-content"},"subject":{"kind":"skill","ref":{"type":"harness","uri":"hrn_run_decide_645a58d0d829_decide"},"commitments":[]},"authority":{"actor_ref":{"type":"principal","uri":"runx:principal:local_runtime"},"grant_refs":[],"scope_refs":[],"authority_proof_refs":[],"attenuation":{"parent_authority_ref":null,"subset_proof":null},"terms":[],"enforcement":{"profile_hash":"sha256:runtime-skeleton-enforcement","redaction_refs":[],"setup_refs":[],"teardown_refs":[]}},"signals":[],"decisions":[{"decision_id":"dec_decide","choice":"open","inputs":{"signal_refs":[],"target_ref":null,"opportunity_refs":[],"selection_ref":null},"proposed_intent":{"purpose":"Open runtime node decide","legitimacy":"Local graph execution requested this node","success_criteria":[],"constraints":[],"derived_from":[]},"selected_act_id":"act_decide","selected_harness_ref":null,"justification":{"summary":"runtime graph planner selected this node","evidence_refs":[]},"closure":null,"artifact_refs":[]}],"acts":[{"id":"act_decide","form":"observation","intent":{"purpose":"Run graph step decide","legitimacy":"Runtime graph execution was admitted by the local harness","success_criteria":[{"criterion_id":"process_exit","statement":"cli-tool exits successfully","required":true}],"constraints":[],"derived_from":[]},"summary":"Executed graph step decide","criterion_bindings":[{"criterion_id":"process_exit","status":"verified","evidence_refs":[],"verification_refs":[],"summary":"cli-tool exited successfully"}],"source_refs":[],"target_refs":[],"artifact_refs":[],"closure":{"disposition":"closed","reason_code":"process_exit","summary":"cli-tool exited successfully","closed_at":"2026-06-30T06:31:48.143Z"}}],"seal":{"disposition":"closed","reason_code":"process_closed","summary":"cli-tool decide completed","closed_at":"2026-06-30T06:31:48.143Z","last_observed_at":"2026-06-30T06:31:48.143Z","criteria":[{"criterion_id":"process_exit","status":"verified","evidence_refs":[],"verification_refs":[],"summary":"cli-tool exited successfully"}]},"lineage":{"children":[],"sync":[]}} \ No newline at end of file diff --git a/skills/prospect-sequence/references/dogfood.json b/skills/prospect-sequence/references/dogfood.json new file mode 100644 index 000000000..fe13e8902 --- /dev/null +++ b/skills/prospect-sequence/references/dogfood.json @@ -0,0 +1,351 @@ +{ + "closure": { + "closed_at": "2026-06-30T06:31:48.143Z", + "disposition": "closed", + "reason_code": "process_closed", + "summary": "cli-tool decide completed" + }, + "execution": { + "exit_code": 0, + "skill_claim": { + "research": { + "angle": "Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes", + "sources": [ + { + "facts": [ + "Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes." + ], + "title": "Northwind release notes", + "url": "https://example.com/northwind-release-notes" + }, + { + "facts": [ + "Northwind Software says platform and security operators review changes before production release." + ], + "title": "Northwind security page", + "url": "https://example.com/northwind-security" + } + ] + }, + "send_proposal": { + "effect": { + "consumer": "send-as", + "kind": "send_proposal", + "performs_send": false + }, + "gated": true, + "id": "send:52bfd53b003fcc4251943bdd", + "prospect": { + "company": "Northwind Software", + "contact": "Head of Platform" + }, + "required_review": [ + "confirm recipient consent or legitimate outreach basis", + "operator approval before send-as" + ], + "sequence_length": 4 + }, + "sequence": [ + { + "body": "Hi Head of Platform, I noticed Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes. (https://example.com/northwind-release-notes). Runx governed agent workflows may help platform and security operators produce sealed evidence packets before operational changes. Worth comparing notes?", + "channel": "email", + "subject": "Northwind Software and manual release evidence review", + "touch": 1 + }, + { + "body": "Following up with the specific angle: Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes If this is a priority, I can share a short checklist tailored to your public workflow.", + "channel": "email", + "subject": "Quick follow-up on Northwind Software", + "touch": 2 + }, + { + "body": "A useful first step is mapping the current manual release evidence review process, the approval owner, and the failure mode. Runx governed agent workflows fits when that process needs a repeatable evidence trail.", + "channel": "email", + "subject": "Checklist for manual release evidence review", + "touch": 3 + }, + { + "body": "I will close the loop unless manual release evidence review is active this quarter. If helpful, I can send a one-page plan based only on public sources already cited above.", + "channel": "email", + "subject": "Close the loop?", + "touch": 4 + } + ], + "summary": "Prepared a sourced 4-touch outreach sequence for Northwind Software; send is gated through send-as." + }, + "stderr": "", + "stdout": "{\n \"summary\": \"Prepared a sourced 4-touch outreach sequence for Northwind Software; send is gated through send-as.\",\n \"research\": {\n \"sources\": [\n {\n \"title\": \"Northwind release notes\",\n \"url\": \"https://example.com/northwind-release-notes\",\n \"facts\": [\n \"Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes.\"\n ]\n },\n {\n \"title\": \"Northwind security page\",\n \"url\": \"https://example.com/northwind-security\",\n \"facts\": [\n \"Northwind Software says platform and security operators review changes before production release.\"\n ]\n }\n ],\n \"angle\": \"Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes\"\n },\n \"sequence\": [\n {\n \"touch\": 1,\n \"channel\": \"email\",\n \"subject\": \"Northwind Software and manual release evidence review\",\n \"body\": \"Hi Head of Platform, I noticed Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes. (https://example.com/northwind-release-notes). Runx governed agent workflows may help platform and security operators produce sealed evidence packets before operational changes. Worth comparing notes?\"\n },\n {\n \"touch\": 2,\n \"channel\": \"email\",\n \"subject\": \"Quick follow-up on Northwind Software\",\n \"body\": \"Following up with the specific angle: Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes If this is a priority, I can share a short checklist tailored to your public workflow.\"\n },\n {\n \"touch\": 3,\n \"channel\": \"email\",\n \"subject\": \"Checklist for manual release evidence review\",\n \"body\": \"A useful first step is mapping the current manual release evidence review process, the approval owner, and the failure mode. Runx governed agent workflows fits when that process needs a repeatable evidence trail.\"\n },\n {\n \"touch\": 4,\n \"channel\": \"email\",\n \"subject\": \"Close the loop?\",\n \"body\": \"I will close the loop unless manual release evidence review is active this quarter. If helpful, I can send a one-page plan based only on public sources already cited above.\"\n }\n ],\n \"send_proposal\": {\n \"id\": \"send:52bfd53b003fcc4251943bdd\",\n \"gated\": true,\n \"effect\": {\n \"kind\": \"send_proposal\",\n \"consumer\": \"send-as\",\n \"performs_send\": false\n },\n \"prospect\": {\n \"company\": \"Northwind Software\",\n \"contact\": \"Head of Platform\"\n },\n \"sequence_length\": 4,\n \"required_review\": [\n \"confirm recipient consent or legitimate outreach basis\",\n \"operator approval before send-as\"\n ]\n }\n}\n", + "structured_output": { + "research": { + "angle": "Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes", + "sources": [ + { + "facts": [ + "Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes." + ], + "title": "Northwind release notes", + "url": "https://example.com/northwind-release-notes" + }, + { + "facts": [ + "Northwind Software says platform and security operators review changes before production release." + ], + "title": "Northwind security page", + "url": "https://example.com/northwind-security" + } + ] + }, + "send_proposal": { + "effect": { + "consumer": "send-as", + "kind": "send_proposal", + "performs_send": false + }, + "gated": true, + "id": "send:52bfd53b003fcc4251943bdd", + "prospect": { + "company": "Northwind Software", + "contact": "Head of Platform" + }, + "required_review": [ + "confirm recipient consent or legitimate outreach basis", + "operator approval before send-as" + ], + "sequence_length": 4 + }, + "sequence": [ + { + "body": "Hi Head of Platform, I noticed Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes. (https://example.com/northwind-release-notes). Runx governed agent workflows may help platform and security operators produce sealed evidence packets before operational changes. Worth comparing notes?", + "channel": "email", + "subject": "Northwind Software and manual release evidence review", + "touch": 1 + }, + { + "body": "Following up with the specific angle: Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes If this is a priority, I can share a short checklist tailored to your public workflow.", + "channel": "email", + "subject": "Quick follow-up on Northwind Software", + "touch": 2 + }, + { + "body": "A useful first step is mapping the current manual release evidence review process, the approval owner, and the failure mode. Runx governed agent workflows fits when that process needs a repeatable evidence trail.", + "channel": "email", + "subject": "Checklist for manual release evidence review", + "touch": 3 + }, + { + "body": "I will close the loop unless manual release evidence review is active this quarter. If helpful, I can send a one-page plan based only on public sources already cited above.", + "channel": "email", + "subject": "Close the loop?", + "touch": 4 + } + ], + "summary": "Prepared a sourced 4-touch outreach sequence for Northwind Software; send is gated through send-as." + } + }, + "payload": { + "research": { + "angle": "Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes", + "sources": [ + { + "facts": [ + "Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes." + ], + "title": "Northwind release notes", + "url": "https://example.com/northwind-release-notes" + }, + { + "facts": [ + "Northwind Software says platform and security operators review changes before production release." + ], + "title": "Northwind security page", + "url": "https://example.com/northwind-security" + } + ] + }, + "send_proposal": { + "effect": { + "consumer": "send-as", + "kind": "send_proposal", + "performs_send": false + }, + "gated": true, + "id": "send:52bfd53b003fcc4251943bdd", + "prospect": { + "company": "Northwind Software", + "contact": "Head of Platform" + }, + "required_review": [ + "confirm recipient consent or legitimate outreach basis", + "operator approval before send-as" + ], + "sequence_length": 4 + }, + "sequence": [ + { + "body": "Hi Head of Platform, I noticed Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes. (https://example.com/northwind-release-notes). Runx governed agent workflows may help platform and security operators produce sealed evidence packets before operational changes. Worth comparing notes?", + "channel": "email", + "subject": "Northwind Software and manual release evidence review", + "touch": 1 + }, + { + "body": "Following up with the specific angle: Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes If this is a priority, I can share a short checklist tailored to your public workflow.", + "channel": "email", + "subject": "Quick follow-up on Northwind Software", + "touch": 2 + }, + { + "body": "A useful first step is mapping the current manual release evidence review process, the approval owner, and the failure mode. Runx governed agent workflows fits when that process needs a repeatable evidence trail.", + "channel": "email", + "subject": "Checklist for manual release evidence review", + "touch": 3 + }, + { + "body": "I will close the loop unless manual release evidence review is active this quarter. If helpful, I can send a one-page plan based only on public sources already cited above.", + "channel": "email", + "subject": "Close the loop?", + "touch": 4 + } + ], + "summary": "Prepared a sourced 4-touch outreach sequence for Northwind Software; send is gated through send-as." + }, + "receipt": { + "acts": [ + { + "artifact_refs": [], + "closure": { + "closed_at": "2026-06-30T06:31:48.143Z", + "disposition": "closed", + "reason_code": "process_exit", + "summary": "cli-tool exited successfully" + }, + "criterion_bindings": [ + { + "criterion_id": "process_exit", + "evidence_refs": [], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [] + } + ], + "form": "observation", + "id": "act_decide", + "intent": { + "constraints": [], + "derived_from": [], + "legitimacy": "Runtime graph execution was admitted by the local harness", + "purpose": "Run graph step decide", + "success_criteria": [ + { + "criterion_id": "process_exit", + "required": true, + "statement": "cli-tool exits successfully" + } + ] + }, + "source_refs": [], + "summary": "Executed graph step decide", + "target_refs": [] + } + ], + "authority": { + "actor_ref": { + "type": "principal", + "uri": "runx:principal:local_runtime" + }, + "attenuation": { + "parent_authority_ref": null, + "subset_proof": null + }, + "authority_proof_refs": [], + "enforcement": { + "profile_hash": "sha256:runtime-skeleton-enforcement", + "redaction_refs": [], + "setup_refs": [], + "teardown_refs": [] + }, + "grant_refs": [], + "scope_refs": [], + "terms": [] + }, + "canonicalization": "runx.receipt.c14n.v1", + "created_at": "2026-06-30T06:31:48.143Z", + "decisions": [ + { + "artifact_refs": [], + "choice": "open", + "closure": null, + "decision_id": "dec_decide", + "inputs": { + "opportunity_refs": [], + "selection_ref": null, + "signal_refs": [], + "target_ref": null + }, + "justification": { + "evidence_refs": [], + "summary": "runtime graph planner selected this node" + }, + "proposed_intent": { + "constraints": [], + "derived_from": [], + "legitimacy": "Local graph execution requested this node", + "purpose": "Open runtime node decide", + "success_criteria": [] + }, + "selected_act_id": "act_decide", + "selected_harness_ref": null + } + ], + "digest": "sha256:9ec5758e28d771aacadb71a8be2767b0b219e363ecd77ed131be0d681c16450c", + "id": "sha256:9d5e4f26a9e8b99f4eabaa6c7e50626a45498446b900872d236bbc053814a3df", + "idempotency": { + "content_hash": "sha256:run_decide_645a58d0d829-decide-content", + "intent_key": "sha256:run_decide_645a58d0d829-decide-intent", + "trigger_fingerprint": "sha256:run_decide_645a58d0d829-decide-trigger" + }, + "issuer": { + "kid": "runx-demo-key", + "public_key_sha256": "sha256:3097e2dee2cb4a34b53840cdb705aed71067c36f68db0e0f559c3f3fa043315f", + "type": "hosted" + }, + "lineage": { + "children": [], + "sync": [] + }, + "schema": "runx.receipt.v1", + "seal": { + "closed_at": "2026-06-30T06:31:48.143Z", + "criteria": [ + { + "criterion_id": "process_exit", + "evidence_refs": [], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [] + } + ], + "disposition": "closed", + "last_observed_at": "2026-06-30T06:31:48.143Z", + "reason_code": "process_closed", + "summary": "cli-tool decide completed" + }, + "signals": [], + "signature": { + "alg": "Ed25519", + "value": "base64:fzpEn56LLoxmAO0ZyGIl5W_W9nnfJtdwo9BauUiLGTbC4eooMImhZC_qp6kZWgTdhUh7sHBMoP7BQRV5CJ5GDg" + }, + "subject": { + "commitments": [], + "kind": "skill", + "ref": { + "type": "harness", + "uri": "hrn_run_decide_645a58d0d829_decide" + } + } + }, + "receipt_id": "sha256:9d5e4f26a9e8b99f4eabaa6c7e50626a45498446b900872d236bbc053814a3df", + "run_id": "run_decide_645a58d0d829", + "schema": "runx.skill_run.v1", + "skill_name": "prospect-sequence", + "status": "sealed" +} diff --git a/skills/prospect-sequence/references/evidence.json b/skills/prospect-sequence/references/evidence.json new file mode 100644 index 000000000..bf9da2138 --- /dev/null +++ b/skills/prospect-sequence/references/evidence.json @@ -0,0 +1,137 @@ +{ + "summary": "prospect-sequence is published as a community runx skill, has a passing two-case harness, sealed local and published dogfood receipts, valid receipt verification, and a gated send_proposal that performs no send.", + "package": { + "owner": "zdfgu113", + "name": "prospect-sequence", + "version": "sha-b4a3b8668802", + "registry": "https://api.runx.ai", + "public_url": "https://runx.ai/x/zdfgu113/prospect-sequence", + "install_command": "runx add zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai", + "run_command": "runx skill zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai" + }, + "observations": [ + { + "id": "runx_cli_version", + "evidence": "Captured `runx --version` output: runx-cli 0.6.13" + }, + { + "id": "published_package", + "evidence": "Published `zdfgu113/prospect-sequence@sha-b4a3b8668802` to https://api.runx.ai with public URL https://runx.ai/x/zdfgu113/prospect-sequence" + }, + { + "id": "local_harness", + "evidence": "Local harness passed 2 cases with zero assertion errors: public-sources-yield-sourced-sequence and private-or-missing-sources-refuse." + }, + { + "id": "dogfood_receipt", + "evidence": "Local dogfood sealed receipt sha256:9d5e4f26a9e8b99f4eabaa6c7e50626a45498446b900872d236bbc053814a3df and verified valid:true." + }, + { + "id": "published_install", + "evidence": "New-user install command succeeded for zdfgu113/prospect-sequence@sha-b4a3b8668802 into /tmp/runx-prospect-install." + }, + { + "id": "published_dogfood", + "evidence": "Published package dogfood sealed receipt sha256:697160868f5c1f292fe3bfc9b50287fe0b896ecb33a573bda7aed41f0b08ec69 and verified valid:true." + }, + { + "id": "send_gate", + "evidence": "The output send_proposal is gated, targets send-as, and records performs_send:false." + }, + { + "id": "source_guard", + "evidence": "The runner refuses loopback, RFC1918, .local, non-HTTP, missing-text, and off-allowlist sources before using account facts." + }, + { + "id": "pr_url", + "evidence": "PR https://github.com/runxhq/runx/pull/179 contains skills/prospect-sequence/X.yaml, SKILL.md, fixtures, and harness evidence." + } + ], + "local_verification": { + "runx_version": "runx-cli 0.6.13", + "harness_command": "runx harness ./.tmp/frantic-prospect-sequence -R /home/zjs/runx-prospect-receipts --json", + "harness_status": "passed", + "harness_cases": [ + "public-sources-yield-sourced-sequence", + "private-or-missing-sources-refuse" + ], + "dogfood_status": "sealed", + "dogfood_receipt_id": "sha256:9d5e4f26a9e8b99f4eabaa6c7e50626a45498446b900872d236bbc053814a3df", + "receipt_verify_valid": true + }, + "published_verification": { + "registry_read_status": "success", + "install_status": "success", + "published_dogfood_status": "sealed", + "published_dogfood_receipt_id": "sha256:697160868f5c1f292fe3bfc9b50287fe0b896ecb33a573bda7aed41f0b08ec69", + "published_receipt_verify_valid": true + }, + "dogfood": { + "package": "zdfgu113/prospect-sequence@sha-b4a3b8668802", + "command": "runx skill zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai -R /home/zjs/runx-prospect-published-receipts --input-json prospect= --input-json icp= --input-json source_allowlist= --json", + "receipt_ref": "runx:receipt:sha256:697160868f5c1f292fe3bfc9b50287fe0b896ecb33a573bda7aed41f0b08ec69", + "verify_verdict": "valid", + "harness_cases": [ + { + "name": "public-sources-yield-sourced-sequence", + "status": "sealed" + }, + { + "name": "private-or-missing-sources-refuse", + "status": "refused" + } + ], + "input": { + "prospect": { + "company": "Northwind Software", + "contact": "Head of Platform" + }, + "icp": { + "product": "Runx governed agent workflows", + "audience": "platform and security operators", + "pain_points": [ + "manual release evidence review", + "supply-chain approval drift" + ], + "value_props": [ + "produce sealed evidence packets before operational changes", + "keep human approval gates before sending or mutating state" + ] + }, + "source_allowlist": { + "allowed_hosts": [ + "example.com" + ], + "sources": [ + { + "url": "https://example.com/northwind-release-notes", + "title": "Northwind release notes" + }, + { + "url": "https://example.com/northwind-security", + "title": "Northwind security page" + } + ] + } + }, + "output_summary": "Prepared a sourced 4-touch outreach sequence for Northwind Software; send is gated through send-as.", + "sequence_length": 4, + "send_proposal": { + "gated": true, + "consumer": "send-as", + "performs_send": false + } + }, + "artifacts": { + "harness": "references/harness.json", + "dogfood": "references/dogfood.json", + "dogfood_receipt": "references/dogfood-receipt.json", + "verification": "references/verification.json", + "publish": "references/publish.json", + "published_verification": "references/published-verification.json", + "install": "references/install.json", + "published_dogfood": "references/published-dogfood.json", + "published_dogfood_receipt": "references/published-dogfood-receipt.json", + "published_receipt_verify": "references/published-receipt-verify.json" + } +} diff --git a/skills/prospect-sequence/references/harness.json b/skills/prospect-sequence/references/harness.json new file mode 100644 index 000000000..da22a8352 --- /dev/null +++ b/skills/prospect-sequence/references/harness.json @@ -0,0 +1,15 @@ +{ + "status": "passed", + "case_count": 2, + "assertion_error_count": 0, + "assertion_errors": [], + "case_names": [ + "public-sources-yield-sourced-sequence", + "private-or-missing-sources-refuse" + ], + "receipt_ids": [ + "sha256:eadaa984e3b37f05d5bb418c91cf0539973fe5cf438dd0dbf2896c4c57157943", + "sha256:a266e891807eaa1407acedf735244dd7158553213c129f5e10a15a7d3f51d3f1" + ], + "graph_case_count": 0 +} diff --git a/skills/prospect-sequence/references/install.json b/skills/prospect-sequence/references/install.json new file mode 100644 index 000000000..a546bead0 --- /dev/null +++ b/skills/prospect-sequence/references/install.json @@ -0,0 +1,43 @@ +{ + "status": "success", + "registry": { + "action": "install", + "source": "remote", + "ref": "zdfgu113/prospect-sequence@sha-b4a3b8668802", + "install": { + "status": "installed", + "destination": "/tmp/runx-prospect-install/zdfgu113/prospect-sequence/sha-b4a3b8668802/SKILL.md", + "skill_name": "prospect-sequence", + "source": "runx-registry", + "source_label": "runx registry", + "skill_id": "zdfgu113/prospect-sequence", + "version": "sha-b4a3b8668802", + "digest": "sha256:693b9f8e694ef8f543b81136efbc64d6554e1b2f1000d4ac49edb6e12358ab06", + "profile_digest": "sha256:e5309e236c14102626b2371e7410fb04008dbab301ae02b5c24674b9972a2e69", + "profile_state_path": "/tmp/runx-prospect-install/zdfgu113/prospect-sequence/sha-b4a3b8668802/.runx/profile.json", + "runner_names": [ + "decide" + ], + "trust_tier": "community" + }, + "receipt_metadata": { + "destination": "/tmp/runx-prospect-install/zdfgu113/prospect-sequence/sha-b4a3b8668802/SKILL.md", + "digest": "sha256:693b9f8e694ef8f543b81136efbc64d6554e1b2f1000d4ac49edb6e12358ab06", + "install_count": 1, + "package_digest": "c64bd529136cfab8e4382982292d12a8082a417406d467a1bc89ef6975ad5f07", + "profile_digest": "sha256:e5309e236c14102626b2371e7410fb04008dbab301ae02b5c24674b9972a2e69", + "publisher": { + "display_name": "zdfgu113", + "handle": "zdfgu113", + "id": "user_029f74b8258386ff624c5320", + "kind": "user" + }, + "ref": "zdfgu113/prospect-sequence@sha-b4a3b8668802", + "skill_id": "zdfgu113/prospect-sequence", + "source_label": "runx registry", + "status": "installed", + "trust_tier": "community", + "version": "sha-b4a3b8668802" + } + } +} diff --git a/skills/prospect-sequence/references/publish.json b/skills/prospect-sequence/references/publish.json new file mode 100644 index 000000000..90f478a2e --- /dev/null +++ b/skills/prospect-sequence/references/publish.json @@ -0,0 +1,20 @@ +{ + "status": "success", + "registry": { + "action": "publish", + "publish": { + "target": "hosted", + "status": "published", + "skill_id": "zdfgu113/prospect-sequence", + "owner": "zdfgu113", + "name": "prospect-sequence", + "version": "sha-b4a3b8668802", + "digest": "693b9f8e694ef8f543b81136efbc64d6554e1b2f1000d4ac49edb6e12358ab06", + "profile_digest": "e5309e236c14102626b2371e7410fb04008dbab301ae02b5c24674b9972a2e69", + "trust_tier": "community", + "install_command": "runx add zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai", + "run_command": "runx skill zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai", + "public_url": "https://runx.ai/x/zdfgu113/prospect-sequence" + } + } +} diff --git a/skills/prospect-sequence/references/published-dogfood-receipt.json b/skills/prospect-sequence/references/published-dogfood-receipt.json new file mode 100644 index 000000000..3ec68adb3 --- /dev/null +++ b/skills/prospect-sequence/references/published-dogfood-receipt.json @@ -0,0 +1 @@ +{"schema":"runx.receipt.v1","id":"sha256:697160868f5c1f292fe3bfc9b50287fe0b896ecb33a573bda7aed41f0b08ec69","created_at":"2026-06-30T06:35:05.067Z","canonicalization":"runx.receipt.c14n.v1","issuer":{"type":"hosted","kid":"runx-demo-key","public_key_sha256":"sha256:3097e2dee2cb4a34b53840cdb705aed71067c36f68db0e0f559c3f3fa043315f"},"signature":{"alg":"Ed25519","value":"base64:1gSM4IOvjoJlSUp_szHrU3TitTaBLgduwxEmK-D_q8D1dinoZVWHq8ybx6xiejSCGarUmGVBI4he2rLUm_0MDw"},"digest":"sha256:9fc69563b7bcf392507c679ef95d96f72c3083fcf885d934eb2e6dc81ea26d05","idempotency":{"intent_key":"sha256:run_decide_645a58d0d829-decide-intent","trigger_fingerprint":"sha256:run_decide_645a58d0d829-decide-trigger","content_hash":"sha256:run_decide_645a58d0d829-decide-content"},"subject":{"kind":"skill","ref":{"type":"harness","uri":"hrn_run_decide_645a58d0d829_decide"},"commitments":[]},"authority":{"actor_ref":{"type":"principal","uri":"runx:principal:local_runtime"},"grant_refs":[],"scope_refs":[],"authority_proof_refs":[],"attenuation":{"parent_authority_ref":null,"subset_proof":null},"terms":[],"enforcement":{"profile_hash":"sha256:runtime-skeleton-enforcement","redaction_refs":[],"setup_refs":[],"teardown_refs":[]}},"signals":[],"decisions":[{"decision_id":"dec_decide","choice":"open","inputs":{"signal_refs":[],"target_ref":null,"opportunity_refs":[],"selection_ref":null},"proposed_intent":{"purpose":"Open runtime node decide","legitimacy":"Local graph execution requested this node","success_criteria":[],"constraints":[],"derived_from":[]},"selected_act_id":"act_decide","selected_harness_ref":null,"justification":{"summary":"runtime graph planner selected this node","evidence_refs":[]},"closure":null,"artifact_refs":[]}],"acts":[{"id":"act_decide","form":"observation","intent":{"purpose":"Run graph step decide","legitimacy":"Runtime graph execution was admitted by the local harness","success_criteria":[{"criterion_id":"process_exit","statement":"cli-tool exits successfully","required":true}],"constraints":[],"derived_from":[]},"summary":"Executed graph step decide","criterion_bindings":[{"criterion_id":"process_exit","status":"verified","evidence_refs":[],"verification_refs":[],"summary":"cli-tool exited successfully"}],"source_refs":[],"target_refs":[],"artifact_refs":[],"closure":{"disposition":"closed","reason_code":"process_exit","summary":"cli-tool exited successfully","closed_at":"2026-06-30T06:35:05.067Z"}}],"seal":{"disposition":"closed","reason_code":"process_closed","summary":"cli-tool decide completed","closed_at":"2026-06-30T06:35:05.067Z","last_observed_at":"2026-06-30T06:35:05.067Z","criteria":[{"criterion_id":"process_exit","status":"verified","evidence_refs":[],"verification_refs":[],"summary":"cli-tool exited successfully"}]},"lineage":{"children":[],"sync":[]}} \ No newline at end of file diff --git a/skills/prospect-sequence/references/published-dogfood.json b/skills/prospect-sequence/references/published-dogfood.json new file mode 100644 index 000000000..030c46206 --- /dev/null +++ b/skills/prospect-sequence/references/published-dogfood.json @@ -0,0 +1,362 @@ +{ + "closure": { + "closed_at": "2026-06-30T06:35:05.067Z", + "disposition": "closed", + "reason_code": "process_closed", + "summary": "cli-tool decide completed" + }, + "execution": { + "exit_code": 0, + "skill_claim": { + "research": { + "angle": "Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes", + "sources": [ + { + "facts": [ + "Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes." + ], + "title": "Northwind release notes", + "url": "https://example.com/northwind-release-notes" + }, + { + "facts": [ + "Northwind Software says platform and security operators review changes before production release." + ], + "title": "Northwind security page", + "url": "https://example.com/northwind-security" + } + ] + }, + "send_proposal": { + "effect": { + "consumer": "send-as", + "kind": "send_proposal", + "performs_send": false + }, + "gated": true, + "id": "send:52bfd53b003fcc4251943bdd", + "prospect": { + "company": "Northwind Software", + "contact": "Head of Platform" + }, + "required_review": [ + "confirm recipient consent or legitimate outreach basis", + "operator approval before send-as" + ], + "sequence_length": 4 + }, + "sequence": [ + { + "body": "Hi Head of Platform, I noticed Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes. (https://example.com/northwind-release-notes). Runx governed agent workflows may help platform and security operators produce sealed evidence packets before operational changes. Worth comparing notes?", + "channel": "email", + "subject": "Northwind Software and manual release evidence review", + "touch": 1 + }, + { + "body": "Following up with the specific angle: Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes If this is a priority, I can share a short checklist tailored to your public workflow.", + "channel": "email", + "subject": "Quick follow-up on Northwind Software", + "touch": 2 + }, + { + "body": "A useful first step is mapping the current manual release evidence review process, the approval owner, and the failure mode. Runx governed agent workflows fits when that process needs a repeatable evidence trail.", + "channel": "email", + "subject": "Checklist for manual release evidence review", + "touch": 3 + }, + { + "body": "I will close the loop unless manual release evidence review is active this quarter. If helpful, I can send a one-page plan based only on public sources already cited above.", + "channel": "email", + "subject": "Close the loop?", + "touch": 4 + } + ], + "summary": "Prepared a sourced 4-touch outreach sequence for Northwind Software; send is gated through send-as." + }, + "stderr": "", + "stdout": "{\n \"summary\": \"Prepared a sourced 4-touch outreach sequence for Northwind Software; send is gated through send-as.\",\n \"research\": {\n \"sources\": [\n {\n \"title\": \"Northwind release notes\",\n \"url\": \"https://example.com/northwind-release-notes\",\n \"facts\": [\n \"Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes.\"\n ]\n },\n {\n \"title\": \"Northwind security page\",\n \"url\": \"https://example.com/northwind-security\",\n \"facts\": [\n \"Northwind Software says platform and security operators review changes before production release.\"\n ]\n }\n ],\n \"angle\": \"Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes\"\n },\n \"sequence\": [\n {\n \"touch\": 1,\n \"channel\": \"email\",\n \"subject\": \"Northwind Software and manual release evidence review\",\n \"body\": \"Hi Head of Platform, I noticed Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes. (https://example.com/northwind-release-notes). Runx governed agent workflows may help platform and security operators produce sealed evidence packets before operational changes. Worth comparing notes?\"\n },\n {\n \"touch\": 2,\n \"channel\": \"email\",\n \"subject\": \"Quick follow-up on Northwind Software\",\n \"body\": \"Following up with the specific angle: Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes If this is a priority, I can share a short checklist tailored to your public workflow.\"\n },\n {\n \"touch\": 3,\n \"channel\": \"email\",\n \"subject\": \"Checklist for manual release evidence review\",\n \"body\": \"A useful first step is mapping the current manual release evidence review process, the approval owner, and the failure mode. Runx governed agent workflows fits when that process needs a repeatable evidence trail.\"\n },\n {\n \"touch\": 4,\n \"channel\": \"email\",\n \"subject\": \"Close the loop?\",\n \"body\": \"I will close the loop unless manual release evidence review is active this quarter. If helpful, I can send a one-page plan based only on public sources already cited above.\"\n }\n ],\n \"send_proposal\": {\n \"id\": \"send:52bfd53b003fcc4251943bdd\",\n \"gated\": true,\n \"effect\": {\n \"kind\": \"send_proposal\",\n \"consumer\": \"send-as\",\n \"performs_send\": false\n },\n \"prospect\": {\n \"company\": \"Northwind Software\",\n \"contact\": \"Head of Platform\"\n },\n \"sequence_length\": 4,\n \"required_review\": [\n \"confirm recipient consent or legitimate outreach basis\",\n \"operator approval before send-as\"\n ]\n }\n}\n", + "structured_output": { + "research": { + "angle": "Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes", + "sources": [ + { + "facts": [ + "Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes." + ], + "title": "Northwind release notes", + "url": "https://example.com/northwind-release-notes" + }, + { + "facts": [ + "Northwind Software says platform and security operators review changes before production release." + ], + "title": "Northwind security page", + "url": "https://example.com/northwind-security" + } + ] + }, + "send_proposal": { + "effect": { + "consumer": "send-as", + "kind": "send_proposal", + "performs_send": false + }, + "gated": true, + "id": "send:52bfd53b003fcc4251943bdd", + "prospect": { + "company": "Northwind Software", + "contact": "Head of Platform" + }, + "required_review": [ + "confirm recipient consent or legitimate outreach basis", + "operator approval before send-as" + ], + "sequence_length": 4 + }, + "sequence": [ + { + "body": "Hi Head of Platform, I noticed Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes. (https://example.com/northwind-release-notes). Runx governed agent workflows may help platform and security operators produce sealed evidence packets before operational changes. Worth comparing notes?", + "channel": "email", + "subject": "Northwind Software and manual release evidence review", + "touch": 1 + }, + { + "body": "Following up with the specific angle: Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes If this is a priority, I can share a short checklist tailored to your public workflow.", + "channel": "email", + "subject": "Quick follow-up on Northwind Software", + "touch": 2 + }, + { + "body": "A useful first step is mapping the current manual release evidence review process, the approval owner, and the failure mode. Runx governed agent workflows fits when that process needs a repeatable evidence trail.", + "channel": "email", + "subject": "Checklist for manual release evidence review", + "touch": 3 + }, + { + "body": "I will close the loop unless manual release evidence review is active this quarter. If helpful, I can send a one-page plan based only on public sources already cited above.", + "channel": "email", + "subject": "Close the loop?", + "touch": 4 + } + ], + "summary": "Prepared a sourced 4-touch outreach sequence for Northwind Software; send is gated through send-as." + } + }, + "payload": { + "research": { + "angle": "Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes", + "sources": [ + { + "facts": [ + "Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes." + ], + "title": "Northwind release notes", + "url": "https://example.com/northwind-release-notes" + }, + { + "facts": [ + "Northwind Software says platform and security operators review changes before production release." + ], + "title": "Northwind security page", + "url": "https://example.com/northwind-security" + } + ] + }, + "send_proposal": { + "effect": { + "consumer": "send-as", + "kind": "send_proposal", + "performs_send": false + }, + "gated": true, + "id": "send:52bfd53b003fcc4251943bdd", + "prospect": { + "company": "Northwind Software", + "contact": "Head of Platform" + }, + "required_review": [ + "confirm recipient consent or legitimate outreach basis", + "operator approval before send-as" + ], + "sequence_length": 4 + }, + "sequence": [ + { + "body": "Hi Head of Platform, I noticed Northwind Software described manual release evidence review and supply-chain approval drift in its public release notes. (https://example.com/northwind-release-notes). Runx governed agent workflows may help platform and security operators produce sealed evidence packets before operational changes. Worth comparing notes?", + "channel": "email", + "subject": "Northwind Software and manual release evidence review", + "touch": 1 + }, + { + "body": "Following up with the specific angle: Northwind Software appears to be dealing with manual release evidence review; position Runx governed agent workflows around produce sealed evidence packets before operational changes. Source: https://example.com/northwind-release-notes If this is a priority, I can share a short checklist tailored to your public workflow.", + "channel": "email", + "subject": "Quick follow-up on Northwind Software", + "touch": 2 + }, + { + "body": "A useful first step is mapping the current manual release evidence review process, the approval owner, and the failure mode. Runx governed agent workflows fits when that process needs a repeatable evidence trail.", + "channel": "email", + "subject": "Checklist for manual release evidence review", + "touch": 3 + }, + { + "body": "I will close the loop unless manual release evidence review is active this quarter. If helpful, I can send a one-page plan based only on public sources already cited above.", + "channel": "email", + "subject": "Close the loop?", + "touch": 4 + } + ], + "summary": "Prepared a sourced 4-touch outreach sequence for Northwind Software; send is gated through send-as." + }, + "receipt": { + "acts": [ + { + "artifact_refs": [], + "closure": { + "closed_at": "2026-06-30T06:35:05.067Z", + "disposition": "closed", + "reason_code": "process_exit", + "summary": "cli-tool exited successfully" + }, + "criterion_bindings": [ + { + "criterion_id": "process_exit", + "evidence_refs": [], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [] + } + ], + "form": "observation", + "id": "act_decide", + "intent": { + "constraints": [], + "derived_from": [], + "legitimacy": "Runtime graph execution was admitted by the local harness", + "purpose": "Run graph step decide", + "success_criteria": [ + { + "criterion_id": "process_exit", + "required": true, + "statement": "cli-tool exits successfully" + } + ] + }, + "source_refs": [], + "summary": "Executed graph step decide", + "target_refs": [] + } + ], + "authority": { + "actor_ref": { + "type": "principal", + "uri": "runx:principal:local_runtime" + }, + "attenuation": { + "parent_authority_ref": null, + "subset_proof": null + }, + "authority_proof_refs": [], + "enforcement": { + "profile_hash": "sha256:runtime-skeleton-enforcement", + "redaction_refs": [], + "setup_refs": [], + "teardown_refs": [] + }, + "grant_refs": [], + "scope_refs": [], + "terms": [] + }, + "canonicalization": "runx.receipt.c14n.v1", + "created_at": "2026-06-30T06:35:05.067Z", + "decisions": [ + { + "artifact_refs": [], + "choice": "open", + "closure": null, + "decision_id": "dec_decide", + "inputs": { + "opportunity_refs": [], + "selection_ref": null, + "signal_refs": [], + "target_ref": null + }, + "justification": { + "evidence_refs": [], + "summary": "runtime graph planner selected this node" + }, + "proposed_intent": { + "constraints": [], + "derived_from": [], + "legitimacy": "Local graph execution requested this node", + "purpose": "Open runtime node decide", + "success_criteria": [] + }, + "selected_act_id": "act_decide", + "selected_harness_ref": null + } + ], + "digest": "sha256:9fc69563b7bcf392507c679ef95d96f72c3083fcf885d934eb2e6dc81ea26d05", + "id": "sha256:697160868f5c1f292fe3bfc9b50287fe0b896ecb33a573bda7aed41f0b08ec69", + "idempotency": { + "content_hash": "sha256:run_decide_645a58d0d829-decide-content", + "intent_key": "sha256:run_decide_645a58d0d829-decide-intent", + "trigger_fingerprint": "sha256:run_decide_645a58d0d829-decide-trigger" + }, + "issuer": { + "kid": "runx-demo-key", + "public_key_sha256": "sha256:3097e2dee2cb4a34b53840cdb705aed71067c36f68db0e0f559c3f3fa043315f", + "type": "hosted" + }, + "lineage": { + "children": [], + "sync": [] + }, + "schema": "runx.receipt.v1", + "seal": { + "closed_at": "2026-06-30T06:35:05.067Z", + "criteria": [ + { + "criterion_id": "process_exit", + "evidence_refs": [], + "status": "verified", + "summary": "cli-tool exited successfully", + "verification_refs": [] + } + ], + "disposition": "closed", + "last_observed_at": "2026-06-30T06:35:05.067Z", + "reason_code": "process_closed", + "summary": "cli-tool decide completed" + }, + "signals": [], + "signature": { + "alg": "Ed25519", + "value": "base64:1gSM4IOvjoJlSUp_szHrU3TitTaBLgduwxEmK-D_q8D1dinoZVWHq8ybx6xiejSCGarUmGVBI4he2rLUm_0MDw" + }, + "subject": { + "commitments": [], + "kind": "skill", + "ref": { + "type": "harness", + "uri": "hrn_run_decide_645a58d0d829_decide" + } + } + }, + "receipt_id": "sha256:697160868f5c1f292fe3bfc9b50287fe0b896ecb33a573bda7aed41f0b08ec69", + "registry_provenance": { + "digest": "sha256:693b9f8e694ef8f543b81136efbc64d6554e1b2f1000d4ac49edb6e12358ab06", + "profile_digest": "sha256:e5309e236c14102626b2371e7410fb04008dbab301ae02b5c24674b9972a2e69", + "registry_key_id": "runx-registry-ed25519-v1", + "registry_source": "remote https://api.runx.ai", + "registry_source_fingerprint": "ba1ac16b631195fd", + "skill_id": "zdfgu113/prospect-sequence", + "trust_state": "trusted", + "trust_tier": "community", + "version": "sha-b4a3b8668802" + }, + "run_id": "run_decide_645a58d0d829", + "schema": "runx.skill_run.v1", + "skill_name": "prospect-sequence", + "status": "sealed" +} diff --git a/skills/prospect-sequence/references/published-receipt-verify.json b/skills/prospect-sequence/references/published-receipt-verify.json new file mode 100644 index 000000000..40c0a8c04 --- /dev/null +++ b/skills/prospect-sequence/references/published-receipt-verify.json @@ -0,0 +1,15 @@ +{ + "receipt_dir": "/home/zjs/runx-prospect-published-receipts", + "signature_mode": "production", + "trees": [ + { + "root_receipt_id": "sha256:697160868f5c1f292fe3bfc9b50287fe0b896ecb33a573bda7aed41f0b08ec69", + "receipt_count": 1, + "parent_missing": null, + "valid": true, + "findings": [] + } + ], + "unreadable_files": [], + "valid": true +} diff --git a/skills/prospect-sequence/references/published-verification.json b/skills/prospect-sequence/references/published-verification.json new file mode 100644 index 000000000..277804ead --- /dev/null +++ b/skills/prospect-sequence/references/published-verification.json @@ -0,0 +1,50 @@ +{ + "status": "success", + "registry": { + "action": "read", + "source": "remote", + "ref": "zdfgu113/prospect-sequence@sha-b4a3b8668802", + "skill": { + "skill_id": "zdfgu113/prospect-sequence", + "owner": "zdfgu113", + "name": "prospect-sequence", + "description": "Research a prospect from allowlisted public sources, synthesize a cited outreach angle, draft a multi-touch sequence, and emit only a gated send proposal.", + "category": "growth", + "version": "sha-b4a3b8668802", + "digest": "693b9f8e694ef8f543b81136efbc64d6554e1b2f1000d4ac49edb6e12358ab06", + "markdown": "---\nname: prospect-sequence\nversion: 0.1.0\ndescription: Research a prospect from allowlisted public sources, synthesize a cited outreach angle, draft a multi-touch sequence, and emit only a gated send proposal.\nsource:\n type: cli-tool\n command: node\n args:\n - run.mjs\nlinks:\n source: https://github.com/zdfgu113/runx/tree/codex/prospect-sequence/skills/prospect-sequence\nrunx:\n category: growth\n input_resolution:\n required:\n - prospect\n - icp\n - source_allowlist\n---\n\n# Prospect Sequence\n\nResearch a prospect from allowlisted public source snippets, synthesize a cited\naccount angle, draft a four-touch outreach sequence, and emit a gated\n`send_proposal` for the `send-as` catalog skill. The skill never sends a\nmessage, signs an external request, mutates a CRM, scrapes private networks, or\nuses non-allowlisted sources.\n\n## When to use this skill\n\nUse it when an operator needs a checkable, source-backed outreach draft before a\nhuman or governed send skill decides whether anything should be sent. The output\nkeeps every account-specific claim tied to a supplied public source URL.\n\n## When not to use this skill\n\nDo not use it for scraping, private-network targets, off-allowlist URLs,\nunsourced account claims, spam sending, or bypassing consent and operator\napproval. If no allowlisted public source is present, the skill refuses and emits\nno send proposal.\n\n## Procedure\n\n1. Require typed inputs `prospect`, `icp`, and `source_allowlist`.\n2. Accept only `http` or `https` source URLs whose host appears in\n `source_allowlist.allowed_hosts` when that list is supplied.\n3. Refuse loopback, RFC1918, `.local`, non-HTTP, missing-text, or\n off-allowlist sources before using any account fact.\n4. Extract account facts only from the accepted public source snippets.\n5. Synthesize one outreach angle that cites the source URL used for the claim.\n6. Draft a four-touch sequence that stays scoped to the cited public evidence.\n7. Emit `send_proposal` only as a gated proposed Effect for `send-as`; the\n proposal records that this skill performs no send.\n8. Refuse with `no_allowlisted_public_sources` when the source evidence is too\n thin or blocked by the SSRF guard.\n\n## Output\n\nThe runner emits `runx.prospect_sequence.v1` with:\n\n- `summary`: short decision summary.\n- `research`: `{ sources[], angle }`, with source URLs and extracted facts.\n- `sequence`: ordered outreach touches.\n- `send_proposal`: gated proposal for `send-as`, or `null` on refusal.\n- `refusal`: refusal reason and blocked source details when applicable.\n\n## Example\n\n```bash\nrunx skill ./skills/prospect-sequence \\\n --input-json prospect='{\"company\":\"Northwind Software\",\"contact\":\"Head of Platform\"}' \\\n --input-json icp='{\"product\":\"Runx governed agent workflows\",\"audience\":\"platform and security operators\",\"pain_points\":[\"manual release evidence review\"],\"value_props\":[\"produce sealed evidence packets before operational changes\"]}' \\\n --input-json source_allowlist='{\"allowed_hosts\":[\"example.com\"],\"sources\":[{\"url\":\"https://example.com/northwind-release-notes\",\"title\":\"Northwind release notes\",\"text\":\"Northwind Software described manual release evidence review in its public release notes.\"}]}' \\\n --json\n```\n\n## Inputs\n\n- `prospect` (required): object with `company` and optional `contact` or `role`.\n- `icp` (required): object with `product`, `audience`, `pain_points`, and\n `value_props`.\n- `source_allowlist` (required): object with `allowed_hosts` and `sources[]`.\n Each source must include `url`, `title`, and public source `text`.\n\n## Safety Notes\n\n- The runner is deterministic over supplied source snippets; a consuming product\n can hydrate those snippets through its governed HTTP front.\n- The SSRF guard rejects private, loopback, local, off-allowlist, or non-HTTP\n targets.\n- The `send_proposal` is not a send. It is a gated effect proposal that a\n separate `send-as` skill must review and execute only with policy approval.", + "profile_digest": "e5309e236c14102626b2371e7410fb04008dbab301ae02b5c24674b9972a2e69", + "runner_names": [ + "decide" + ], + "source_type": "cli-tool", + "trust_tier": "community", + "required_scopes": [], + "tags": [], + "publisher": { + "kind": "user", + "id": "user_029f74b8258386ff624c5320", + "handle": "zdfgu113", + "display_name": "zdfgu113" + }, + "attestations": [ + { + "kind": "publisher", + "id": "publisher:user_029f74b8258386ff624c5320", + "status": "declared", + "summary": "zdfgu113", + "issued_at": "2026-06-30T06:34:38.433Z", + "metadata": { + "publisher_display_name": "zdfgu113", + "publisher_handle": "zdfgu113", + "publisher_id": "user_029f74b8258386ff624c5320", + "publisher_kind": "user", + "trust_tier": "community" + } + } + ], + "install_command": "runx add zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai", + "run_command": "runx skill zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai" + } + } +} diff --git a/skills/prospect-sequence/references/report.md b/skills/prospect-sequence/references/report.md new file mode 100644 index 000000000..4d21d7912 --- /dev/null +++ b/skills/prospect-sequence/references/report.md @@ -0,0 +1,49 @@ +# prospect-sequence evidence report + +## Package + +- Registry ref: `zdfgu113/prospect-sequence@sha-b4a3b8668802` +- Public URL: `https://runx.ai/x/zdfgu113/prospect-sequence` +- Pull request: `https://github.com/runxhq/runx/pull/179` +- Install: `runx add zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai` +- Run: `runx skill zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai` +- CLI: `runx-cli 0.6.13` + +## Behavior + +The skill researches only supplied allowlisted public source snippets, extracts cited facts, drafts a four-touch sequence, and emits a gated `send_proposal` for `send-as`. It does not send messages, mutate CRM data, scrape private hosts, or use off-allowlist sources. + +## Harness + +- Command: `runx harness ./.tmp/frantic-prospect-sequence -R /home/zjs/runx-prospect-receipts --json` +- Status: `passed` +- Cases: + - `public-sources-yield-sourced-sequence` + - `private-or-missing-sources-refuse` +- Raw evidence: `references/harness.json` + +## Dogfood + +- Local dogfood status: `sealed` +- Local receipt: `sha256:9d5e4f26a9e8b99f4eabaa6c7e50626a45498446b900872d236bbc053814a3df` +- Local verify verdict: `valid` +- Published package dogfood status: `sealed` +- Published package receipt: `sha256:697160868f5c1f292fe3bfc9b50287fe0b896ecb33a573bda7aed41f0b08ec69` +- Published package verify verdict: `valid` + +Dogfood input used a Northwind Software prospect, public `example.com` source snippets, and ICP pain points around manual release evidence review and supply-chain approval drift. Output produced a sourced account angle, four email touches, and a gated `send_proposal` with `performs_send: false`. + +## New-user check + +- `runx registry read zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai --json` succeeded. +- `runx add zdfgu113/prospect-sequence@sha-b4a3b8668802 --registry https://api.runx.ai --to /tmp/runx-prospect-install --json` succeeded. +- Running the published ref with the dogfood input sealed a receipt and verified cleanly. + +## Files + +- `SKILL.md`: operator-facing contract and safety notes. +- `X.yaml`: harness and cli-tool runner profile. +- `run.mjs`: deterministic runner with public-source allowlist and private-host refusal. +- `fixtures/dogfood-input.json`: successful dogfood input. +- `fixtures/private-target-input.json`: refusal-path input. +- `references/*.json`: raw harness, dogfood, publish, install, and verification artifacts. diff --git a/skills/prospect-sequence/references/verification.json b/skills/prospect-sequence/references/verification.json new file mode 100644 index 000000000..949561fe7 --- /dev/null +++ b/skills/prospect-sequence/references/verification.json @@ -0,0 +1,15 @@ +{ + "receipt_dir": "/home/zjs/runx-prospect-receipts", + "signature_mode": "production", + "trees": [ + { + "root_receipt_id": "sha256:9d5e4f26a9e8b99f4eabaa6c7e50626a45498446b900872d236bbc053814a3df", + "receipt_count": 1, + "parent_missing": null, + "valid": true, + "findings": [] + } + ], + "unreadable_files": [], + "valid": true +} diff --git a/skills/prospect-sequence/run.mjs b/skills/prospect-sequence/run.mjs new file mode 100644 index 000000000..04c02fc73 --- /dev/null +++ b/skills/prospect-sequence/run.mjs @@ -0,0 +1,206 @@ +#!/usr/bin/env node +import crypto from "node:crypto"; +import fs from "node:fs"; + +const inputs = readInputs(); +const prospect = objectInput(inputs.prospect, "prospect"); +const icp = objectInput(inputs.icp, "icp"); +const sourceAllowlist = normalizeSourceAllowlist(inputs.source_allowlist); + +const result = decide(prospect, icp, sourceAllowlist); +process.stdout.write(`${JSON.stringify(result.output, null, 2)}\n`); +if (!result.ok) process.exit(64); + +function decide(prospectInput, icpInput, allowlist) { + const company = stringValue(prospectInput.company); + const contact = stringValue(prospectInput.contact) ?? stringValue(prospectInput.role) ?? "operator"; + const product = stringValue(icpInput.product) ?? "the product"; + const audience = stringValue(icpInput.audience) ?? "operators"; + const valueProps = arrayStrings(icpInput.value_props); + const pains = arrayStrings(icpInput.pain_points); + + const validSources = []; + const refusedSources = []; + for (const source of allowlist.sources) { + const checked = checkSource(source, allowlist.allowedHosts); + if (checked.ok) validSources.push(checked.source); + else refusedSources.push({ url: stringValue(source.url), reason: checked.reason }); + } + + if (!company) return refusal("missing_company", prospectInput, icpInput, allowlist, refusedSources); + if (validSources.length === 0) { + return refusal("no_allowlisted_public_sources", prospectInput, icpInput, allowlist, refusedSources); + } + + const sourceFacts = validSources.map((source) => ({ + title: source.title, + url: source.url, + facts: extractFacts(source.text, company, pains, valueProps), + })); + const citedFact = sourceFacts.find((source) => source.facts.length > 0) ?? sourceFacts[0]; + const factText = citedFact.facts[0] ?? `${company} has public source material available for account research.`; + const valueProp = valueProps[0] ?? "reduce operational risk with a focused workflow"; + const pain = pains[0] ?? "manual review load"; + const angle = `${company} appears to be dealing with ${pain}; position ${product} around ${valueProp}. Source: ${citedFact.url}`; + const sequence = [ + { + touch: 1, + channel: "email", + subject: `${company} and ${pain}`, + body: `Hi ${contact}, I noticed ${factText} (${citedFact.url}). ${product} may help ${audience} ${valueProp}. Worth comparing notes?`, + }, + { + touch: 2, + channel: "email", + subject: `Quick follow-up on ${company}`, + body: `Following up with the specific angle: ${angle} If this is a priority, I can share a short checklist tailored to your public workflow.`, + }, + { + touch: 3, + channel: "email", + subject: `Checklist for ${pain}`, + body: `A useful first step is mapping the current ${pain} process, the approval owner, and the failure mode. ${product} fits when that process needs a repeatable evidence trail.`, + }, + { + touch: 4, + channel: "email", + subject: `Close the loop?`, + body: `I will close the loop unless ${pain} is active this quarter. If helpful, I can send a one-page plan based only on public sources already cited above.`, + }, + ]; + + const proposalId = `send:${digest({ company, contact, angle })}`; + return { + ok: true, + output: { + summary: `Prepared a sourced ${sequence.length}-touch outreach sequence for ${company}; send is gated through send-as.`, + research: { + sources: sourceFacts, + angle, + }, + sequence, + send_proposal: { + id: proposalId, + gated: true, + effect: { + kind: "send_proposal", + consumer: "send-as", + performs_send: false, + }, + prospect: { company, contact }, + sequence_length: sequence.length, + required_review: ["confirm recipient consent or legitimate outreach basis", "operator approval before send-as"], + }, + }, + }; +} + +function refusal(reason, prospectInput, icpInput, allowlist, refusedSources) { + return { + ok: false, + output: { + summary: `Prospect sequence refused: ${reason}. No send proposal was emitted.`, + research: { + sources: [], + angle: null, + }, + sequence: [], + send_proposal: null, + refusal: { + reason, + prospect: prospectInput, + icp: icpInput, + allowed_hosts: allowlist.allowedHosts, + refused_sources: refusedSources, + required_evidence: ["allowlisted public http(s) source with readable text"], + }, + }, + }; +} + +function normalizeSourceAllowlist(value) { + const input = objectInput(value, "source_allowlist"); + const sources = Array.isArray(input) ? input : input.sources; + const allowedHosts = new Set(arrayStrings(input.allowed_hosts).map((host) => host.toLowerCase())); + return { sources: Array.isArray(sources) ? sources : [], allowedHosts }; +} + +function checkSource(source, allowedHosts) { + if (!source || typeof source !== "object") return { ok: false, reason: "source_not_object" }; + const urlText = stringValue(source.url); + const text = stringValue(source.text); + const title = stringValue(source.title) ?? urlText; + if (!urlText || !text) return { ok: false, reason: "missing_url_or_text" }; + let url; + try { + url = new URL(urlText); + } catch { + return { ok: false, reason: "invalid_url" }; + } + if (!["https:", "http:"].includes(url.protocol)) return { ok: false, reason: "non_http_source" }; + if (isPrivateHost(url.hostname)) return { ok: false, reason: "private_or_loopback_host" }; + if (allowedHosts.size > 0 && !allowedHosts.has(url.hostname.toLowerCase())) { + return { ok: false, reason: "host_not_allowlisted" }; + } + return { ok: true, source: { url: url.href, title, text } }; +} + +function extractFacts(text, company, pains, valueProps) { + const normalized = text.replace(/\s+/g, " ").trim(); + const sentences = normalized.split(/(?<=[.!?])\s+/).filter(Boolean); + const terms = [company, ...pains, ...valueProps].map((term) => term.toLowerCase()).filter(Boolean); + return sentences + .filter((sentence) => terms.some((term) => sentence.toLowerCase().includes(term))) + .slice(0, 3); +} + +function readInputs() { + if (process.env.RUNX_INPUTS_PATH) { + return JSON.parse(fs.readFileSync(process.env.RUNX_INPUTS_PATH, "utf8")); + } + if (process.env.RUNX_INPUTS_JSON) return JSON.parse(process.env.RUNX_INPUTS_JSON); + return { + prospect: parseInputValue(process.env.RUNX_INPUT_PROSPECT), + icp: parseInputValue(process.env.RUNX_INPUT_ICP), + source_allowlist: parseInputValue(process.env.RUNX_INPUT_SOURCE_ALLOWLIST), + }; +} + +function parseInputValue(raw) { + if (raw === undefined || raw === "") return undefined; + try { + return JSON.parse(raw); + } catch { + return raw; + } +} + +function objectInput(value, name) { + if (!value || typeof value !== "object" || Array.isArray(value)) { + process.stderr.write(`${name} must be an object\n`); + process.exit(64); + } + return value; +} + +function stringValue(value) { + return typeof value === "string" && value.trim() ? value.trim() : null; +} + +function arrayStrings(value) { + return Array.isArray(value) ? value.map(stringValue).filter(Boolean) : []; +} + +function isPrivateHost(hostname) { + const host = hostname.toLowerCase(); + if (host === "localhost" || host.endsWith(".local")) return true; + if (/^(10|127)\./.test(host)) return true; + if (/^192\.168\./.test(host)) return true; + if (/^172\.(1[6-9]|2\d|3[0-1])\./.test(host)) return true; + if (host === "::1" || host.startsWith("fc") || host.startsWith("fd")) return true; + return false; +} + +function digest(value) { + return crypto.createHash("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 24); +}