mirror of
https://github.com/getcargohq/cargo-skills.git
synced 2026-09-14 19:17:15 +08:00
docs/script-require-allowlist
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
bf23619313 |
Stop documenting the first-party enrichment provider (#122)
* Stop documenting the first-party enrichment provider
Its playbook, its 21 priced actions, and every recipe step that led with it
are removed. Each one now leads with the rung the pack already named as its
fallback, so nothing here is invented — the replacements were sitting in
stage-action-map.md and alternatives.md the whole time.
The substitutions:
firmographics aiArk.enrichCompany (0.01) -> companyEnrich (0.25) -> waterfall (1)
person aiArk.enrichPerson (0.1) -> waterfall.enrichContact (2)
funding enrichCrm.getFunding (1), now the catalog's only one
technographics builtwith.getDomainSummary (free) -> enrichDomain (1)
dedupe free storage query against the workspace's own models
That last line is the one worth reading twice. The match actions existed to
mint an id the other actions required; every replacement keys on the domain
or a LinkedIn URL, so a step that cost 0.5/record now costs nothing.
The event feed has no replacement and the recipes say so. fetchBusinessEvents
took a timestamp_from and nothing else in the catalog does, so funding-watch,
re-engagement and lost-deal-revival are rewritten as a diff: re-pull, compare
against the stored round date, keep what moved. Cadence becomes the only cost
dial, which is why funding-watch's default drops from daily to weekly.
Every credit budget was recomputed rather than relabelled:
build-tam 2,955 -> 2,210 (500 companies + 1,500 contacts)
prospecting P2 821 -> 107 (200 verified prospects)
icp-discovery 602 -> 204 (200 deals)
context bootstrap 3 -> 1
Catalog counts now follow the generated data file rather than the older
hand-kept figure: 136 integrations, 176 credits-based actions of 513.
Also bumps the CLI pin 1.0.66 -> 1.0.78.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019wkwbhrEoE5KW9A1CKBmcA
* Fix the three generated artifacts the removal invalidated
CI caught what the local lint could not: skills-lint.mjs is only one of five
checks, and the other four all had something to say.
llms.txt was stale — it is generated from the skill descriptions, and
cargo-gtm's dropped a provider from its roster.
The codex package build enforces override version == upstream version, so
bumping only .github/package-overrides/cargo-quickstart.SKILL.md to 1.0.3 broke
it. The guard exists to stop the override silently shipping last month's demo,
so the fix is to bump both, not to revert the override.
That build also patches literal source strings at package time, and one anchor
ran through a line this branch rewrote:
snitcher.searchSessions (0 cred) -> cargo.matchProspect
snitcher.searchSessions (0 cred) -> aiArk.reverseLookup (0.05)
The edit strips the visitor-de-anonymisation path from the packaged skill, so a
silent miss would have shipped it. Re-anchored, and every other PACKAGE_EDITS
find string verified against the tree — 1 broken, now 0.
Also drops the packaged cargo-connection description from 138 integrations to
136, matching the repo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019wkwbhrEoE5KW9A1CKBmcA
* Say where revenue went, and that employee ratings went nowhere
Removing the provider took two data points off the map without replacing the
map entry, which is the failure mode this recipe's own feasibility gate exists
to prevent: an attribute that quietly stops being answerable reads the same as
one nobody asked for.
They are not the same case, so they get different rows.
Revenue and NAICS survive. companyEnrich.enrichByDomain (0.25) returns them in
the same call as firmographics, employees, funding and socials — so it fills
several rows at once and the row says that, because the per-account arithmetic
in step 4 gets it wrong otherwise.
Employee ratings have no successor at any price. Nothing credits-based in the
catalog returns Glassdoor-style employer reputation. The row says none, and
says to report it as a research note rather than substituting a scrape that
reads like the real thing — which is the recipe's stated position on valuable
attributes with no obtainable source, applied to itself.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019wkwbhrEoE5KW9A1CKBmcA
* Fix the data-flow breaks the provider swap left behind
Bugbot found eight, all real, and verifying them turned up five more. The
pattern in every case is the same: I replaced an action but left the code that
consumed its output reading the old response shape, so the step still runs,
still bills, and quietly returns nothing.
Response-shape breaks:
funding-watch Pattern A still filtered funding_rounds[].announced_date
while Pattern B read lastFundingDate. The 90-day select
matched nothing, so the primary path returned an empty
list after a paid pull.
re-engagement Step 5 read .results[] and .events[] from a file step 3
now writes as a bare array. Funding branch always empty.
lost-deal-revival Step 4 read .events[0] and expected a deal_id that
getFunding does not return. Step 3b now carries deal_id
through, which is the only place it exists.
Wrong-source breaks:
re-engagement The diff read last_funding_round_at off the Contacts
segment. It is a Companies column, so the stored date was
always empty and every account re-flagged every week. Now
queries the Companies model, and fans the company-level
signal back out to contacts, since step 5 unions on email.
build-tam Step 4 keyed searchLeads on .linkedinId from the
aiArk.enrichCompany output. No enrichment action returns a
Sales Navigator accountId. Now carried from the sourcing
step, with an explicit gate for rows that have none.
guide snitcher.searchSessions identifies a COMPANY; I had
chained it into aiArk.reverseLookup, which needs an email
or phone. Its own playbook says to route identified
domains through the enrich chain.
Budget break:
prospecting P2 step 5 ran FullEnrich over every lead while the budget
quoted the residue. 200 credits against 60. P1 had the
gate; P2 did not.
Pre-existing, fixed because the fixes above sit inside them:
icp-discovery [.[] | {domain}] over storage query execute output, which
is {rows: [...]}. Enriched one null domain, not 200.
jq inputs[N] `inputs` is a generator, not an array, so the union
expressions in re-engagement and lost-deal-revival errored
out. Slurped to [inputs] as $in.
Every rewritten jq expression was run against fixtures rather than eyeballed.
Also five provider mentions the sweep missed, because they wrote it as bare
`cargo` with a price rather than cargo.actionName: societeInfo, snitcher,
enrichCrm, linkedin, oceanio, apolloio, rocketreach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019wkwbhrEoE5KW9A1CKBmcA
* Make funding-watch Pattern B stand on its own
It re-pulled two hardcoded example domains and then diffed them against
/tmp/targets.json, a Pattern A artifact. Run Pattern B alone and the slurp
fails; run both and it compares a fresh pull of one set against stored dates
for a different set, so the "new round" filter cannot fire correctly either way.
Both sides of a diff have to come from the same query. Now they do: one storage
read supplies the domains to pull *and* the stored dates to compare against, so
the sets align by construction and the pattern has no cross-dependency.
Also states what a missing stored date means — it compares against "" and always
passes, which is right on a first run and is exactly why the write-back step is
not optional. Without it every run is a first run and the watch re-flags the
whole segment forever.
Verified against fixtures: an account whose round moved is kept, one whose
stored date already matches is dropped, one with no stored date passes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019wkwbhrEoE5KW9A1CKBmcA
* Route the questions people ask but the map never answered (#124)
Measured instead of guessed. Of the 176 credits-based actions, 148 were
reachable from a routing surface — the stage map, the phase guides,
alternatives.md, a recipe. The other 28 were reachable only from their own
provider playbook, which is findable if you already know which provider to
open. That is the wrong way round.
The worst of it: headcount had no section at all. It is the most-asked company
attribute and the first row of the custom-datapoints feasibility gate, and not
one of the four salesNavigator.find* actions appeared anywhere in the stage map.
New section carries all four, plus the only historical series
(companyEnrich.getWorkforce), plus linkedin.findCustomHeadcount for the roles
the fixed buckets don't have, plus the companyId prerequisite that quietly
turns a 0.25 action into 0.30 on a list that arrived without one.
"Who works at this domain" was not a documented question either. It is not the
same as searching a population by title, and it has two answers at very
different prices. New section, with the trap stated: looping hunter.searchDomain
to build a list is the pitfall its own playbook warns about.
Also adds rocketreach, mixrank and societeInfo to Enrich/Sourcing —
societeInfo reaches French registry data (NAF code, collective agreement,
filed accounts) that nothing else in the catalog does — and gives Hiring intent
a second index at the same price.
154/176 now. The remaining 22 stay unrouted on purpose, and the CHANGELOG says
why for each: the four LinkedIn engagement actions would undercut the
acceptable-use refusal to batch-blast them, three natives belong to other
skills, searchLeadsLegacy costs 300x its replacement, and the rest are finer
grain on an already-routed surface rather than separate questions.
One thing the build caught: adding aiArk.analyzePersonality re-introduced a
term the codex package deliberately strips, since the directory rejected
person-level inference. Kept for the channels that carry it, with matching
PACKAGE_EDITS so the package still removes it. Dropped the forager rung rather
than add machinery for a last-resort action priced 20x above its own chain.
All 67 anchors verified.
Claude-Session: https://claude.ai/code/session_019wkwbhrEoE5KW9A1CKBmcA
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
be823b3fcb |
Generate the cost table, and stop omitting the native actions (#111)
* Generate the cost table, and stop omitting the native actions
The credits price list was hand-kept. The first attempt to generate it read
`connection integration list`, which is connector-only — so its own header
claimed "every credits-based action in the cargo catalog" while silently
omitting every billed native action:
sendEmail 0.1 fixed
modelAsk 0.5 fixed
fileSearch 0.02 / 1k tokens
`sendEmail` is the whole outbound send path, priced at 0.1 in the mailbox
skill, and it was absent from the canonical price list.
It is now generated by `cargo-ai orchestration action cost-table --raw`
(getcargohq/cargo#5641), which lives beside `action list` and `action execute`
— the surface that spans connector, native, tool and agent. 197 credits-based
actions of the 534 the catalog exposes, up from the 145 the hand-kept table
claimed and the 194 a connector-only generator found.
What the hand-kept table got wrong, now that a generated one can be diffed
against it: 145 actions where there are 197, four prices quoted from memory,
and cost columns that concatenated every tier into an unreadable comma list —
`openAi.instruct` rendered 28 numbers. Per-config pricing moves to its own
section.
Two providers gain playbooks rather than staying unlisted: brightData
(Instagram/TikTok/Facebook/YouTube by URL, the catalog's only non-LinkedIn,
non-X social coverage) and proxycurl. An undocumented provider still shows up
in the cost table, so the acceptable-use framing is better stated than implied;
brightData.md opens with the consumer-targeting refusal that gates it. Only
openRouter remains without a playbook, and it has no credits-based actions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBfa73ishJJRe4pccyhTAa
* Pin the CLI at 1.0.66, and fix five commands that never existed (#117)
* Pin the CLI at 1.0.66, and fix five commands that never existed
The pin had drifted 19 releases behind npm, and the bundle now teaches
`orchestration action list`, which only exists in 1.0.66. The pin is what the
session-start refresh installs, so leaving it meant an agent following these
skills would reach for a command its CLI did not have.
Checked before moving it, not after: the CLI's whole command surface was diffed
across 1.0.47..1.0.66. The only removals are three billing commands no skill
documents; `update-payment-method`, which the skills do use, replaced one of
them and is still there. Everything else in the range is additive.
That verification then turned up five documented commands that do not exist —
and all five were already broken at the old pin, so this is old debt the bump
surfaced rather than caused:
- `orchestration draft-release {get,update,deploy}` (18 sites) is really
`orchestration release {get-draft,update-draft,deploy-draft}`.
- `ai template get <slug>` (6 sites) does not exist. `ai template list` already
returns each template in full, so the fix is to filter that response.
- `action execute --record` (5 sites) is `--data`.
- `storage model list --dataset-uuid` (4 sites): the command takes no options
at all, and every model carries `datasetUuid`, so filter client-side.
- `storage model get-ddl --model-uuid <uuid>` takes the uuid positionally.
Also folds in the skills half of getcargohq/cargo#5740: `get-output-schema`
takes an action plus an optional `--data` rather than demanding a `config`, so
the object `action list` returns now pastes into all four action commands
unchanged. The gotcha row about the 400 becomes a note about `--data`, which is
the part worth knowing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBfa73ishJJRe4pccyhTAa
* Mark --data on get-output-schema as CLI >= 1.0.67
getcargohq/cargo#5740 ships --data in CLI 1.0.67, one above the pin this PR
sets. The config-less --action works on the pinned 1.0.66 as soon as that
backend deploys; --data needs the next release, so it carries a version marker
like every other post-pin flag in the bundle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Document the execution charge the pack said did not exist (#118)
Every node execution bills 0.01 credits (1 per 100) — structural natives
and errored executions included. Three places in the pack said otherwise,
and they reinforced each other:
- troubleshooting.md described executions[].creditsUsedCount as "native
nodes are zero", accurate about the field but read as pricing;
play-optimize-credits.md restated it and cited it as the authority.
- The generated cost table prices actions, and structural natives are
excluded from `action list` entirely, so branch/filter/switch/variables
appear in no price list here — read as free.
- cargo/SKILL.md said "Everything else is free" outright.
Verified live: `billing usage get-metrics --unit orchestration.executions`
returns success/error as execution counts, reconciling row-for-row with
`SELECT execution_status, count() FROM spans`. spans.execution_credits_used_count
reads 0 on natives that billed, so the charge is attributed to no node and
per-node profiling under-counts every graph.
Two adjacent bugs fixed while verifying:
- `--unit credits` was documented and 400s. The flag takes exactly
billing.credits | orchestration.executions | storage.records, and with
no --unit all three interleave in one items[] array with different
meanings for `count`.
- The example slugs `enrichment` / `ai_message` were invented (real:
integration.<slug>.action.<action>, native.<action>, success/error,
insert), as was `.totalUsage` — the response has one key, `metrics`.
Estimates now carry two terms: (provider cost × records) + (nodes ×
records / 100), measured on the pilot rather than inferred from the graph.
cargo-billing 1.1.0 -> 2.0.0 (agents relearn --unit and the slug shapes),
cargo-orchestration 1.11.0, cargo-gtm 1.19.0, cargo-diagnostics 1.4.0,
cargo 1.24.0 + the three plugin manifests.
Still upstream: `orchestration action cost-table` generates the header
"The other 337 actions ... cost no credits" — unfixable from this repo.
Claude-Session: https://claude.ai/code/session_01AbUvyTQMDABiexZgQCsGoe
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Source the cost table from action list, not a bespoke generator
`orchestration action cost-table` is being removed from the CLI: it read
`action list` twice and added no data, and the markdown it generated was
its only consumer. `action list` already returns a `credits` array on every
billed action, so the table is regenerated from that directly.
Regeneration is now two calls (`--kind connector`, `--kind native`) plus
the three cost shapes documented in the header, so a reader can render the
table without the generator's price-formatting logic. Notably `fixedCost`
adds to the metered rate rather than replacing it — the trap that made a
0.175 + 0.025/item search read as 0.025.
Two fixes that fall out of owning the header again:
- The opening line claimed the other 337 actions "cost no credits". True
of provider price, false of anything that runs in a workflow. It was
flagged as unfixable while the CLI generated it; it now states no
*provider* price and points at the execution charge.
- `stage-action-map.md` still carried 194/310 from the connector-only
generation, against 197/337 everywhere else.
Co-Authored-By: Auree Aubert <16732193+aureeaubert@users.noreply.github.com>
Co-Authored-By: replicas-connector[bot] <replicas-connector[bot]@users.noreply.github.com>
Co-authored-by: Auree Aubert <aureeaubert@users.noreply.github.com>
* Fix PACKAGE_EDITS patterns to match regenerated cost table format
Co-authored-by: Auree Aubert <aureeaubert@users.noreply.github.com>
* Add PACKAGE_EDITS rule to remove extractEventAttendees from stage-action-map.md
Co-authored-by: Auree Aubert <aureeaubert@users.noreply.github.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: replicas-connector[bot] <replicas-connector[bot]@users.noreply.github.com>
Co-authored-by: Auree Aubert <aureeaubert@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
|
||
|
|
55d2eaf660 |
Ship the OpenAI package without the surfaces its review rejected (#120)
* Hold the sending skill out of the OpenAI package The directory review rejected two skills under "Spam mass abuse". cargo-mailbox-management is a true positive on category: it provisions sending mailboxes and runs a 5-to-40/day warm-up ramp, which is the shape of cold-email infrastructure whatever the gates around it. So the package now ships without it — EXCLUDED_SKILLS — and says so by shipping eighteen skills rather than by rewording the nineteenth. The alternative, deleting the refusal lists and ramp documentation until the keywords stop matching, would ship a less guarded skill in order to pass a safety review. cargo-gtm looks like a false positive on negation: its description ended "bulk unsolicited messaging, purchased or scraped lists, and consumer targeting are refused", which hands a keyword classifier four spam nouns in one clause. The packaged description now states the same policy as what the skill does — documented lawful basis, suppression screening, per-recipient relevance, sends nothing itself — and keeps every routing trigger. That did not clear review — gtm was rejected again with the new card live, so the trigger is the skill body or the capability itself, not the description. The reframed card is kept because it is the more accurate of the two, not because it fixed anything. Excluding a skill is not just skipping a directory. Fifteen of the remaining skills link to it: a router table row, an ASCII diagram box, a recap section, the domain list, defineMailbox/defineDomain in cargo-cdk, a routing row in cargo-mcp, three passages in cargo-gtm's acceptable-use. PACKAGE_EDITS removes each one in the staged copy, and every find must match exactly once or the build fails — they are anchored to prose that will drift, and a silent no-op would ship a dangling link. Skill counts renumber from the real packaged total, so the router reads "18 skills ... fifteen capability skills" without anyone maintaining the number. Backstopping all of that, zipgrep asserts after zipping that no packaged file names an excluded skill anywhere. That check does not care whether the edits above were written correctly. Every edit applies to the staging copy under dist/. The repo files are read-only inputs, so Claude Code, Cursor, skills.sh and Hermes still install all nineteen skills with every mailbox reference intact. Also fixes the packaged manifest description, which claimed 17 skills — stale since two were added. It derives from the packaged count now. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YL6YdzRFUf27A2WChKxr1C * Narrow cargo-gtm's person-level surface for the OpenAI package Second rejection, with the reframed description already live in the listing — so the card was never the trigger. Reading the skill body as a policy reviewer would, five capabilities carry most of the risk, and on those the reviewer is not wrong: snitcher de-anonymising website visitors aiArk.analyzePersonality OCEAN/DISC inference from a profile forager personal email and personal phone from a URL linkedin.extractEventAttendees harvesting a platform's attendee list x reading a social platform's posts and profiles "We gate it" is a weak answer to visitor de-anonymisation and personality inference. So the packaged skill no longer offers them: three playbooks leave the tree, and the actions are removed from the stack table, the cost table, the stage map, the alternatives table, the finding guide's decision tree, two recipes, the aiArk playbook and the router's glossary. Thirty-four edits, each asserted to match exactly once. What remains is the same skill minus a person-level edge: company research, work-email enrichment from licensed providers, verification, scoring, signal monitoring, CRM sync. That is a real narrowing of what the package can do rather than a rewording of what it says it does, which is also what makes the appeal behind it checkable. REMOVED_TERMS asserts against the built archive that nothing survived, scoped by path — extractEventAttendees stays in cargo-connection, which documents the catalog rather than recommending the action, and which the directory already approved. Metadata is regenerated with the repo's own skills-metadata.mjs over the staged tree: three files left and a dozen changed, so every packaged skill-metadata.json was stale by construction, content hash included. Repo copies are untouched, as before. Every other channel still ships all nineteen skills with the full provider stack. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YL6YdzRFUf27A2WChKxr1C * Give the packaged quickstart a company-level demo Third rejection, same category: the quickstart's headline pull is 25 named people with titles and a "recently hired" flag. Sixteen skills that never touch a person passed on the first try; the three that were rejected are the three that do. Rather than drop the onboarding skill, the package now ships a variant that runs the same two-minute arc against company-level hiring intent — theirStack.searchCompanies on the persona's role, postings inside 30 days. This is the skill's own fallback rung 2, which it already describes as "same wow, different angle". The receipt is unchanged at ~0.5 credits, because searchCompanies bills per call rather than per row, so `limit` is free to set. The demo names no individuals. It ships as a whole file rather than as edits. The spine is rewritten — action, filter, output fields, headline, fallback ladder, next steps — and a dozen brittle anchors through a rewritten section would fail on the next upstream wording change with nothing useful to say about it. The copy is tied to the repo skill's frontmatter version instead. Bump cargo-quickstart and this build fails, naming both versions and asking for the port. That is the maintenance cost of an override, made loud on purpose. Step 3 reads the first row's keys and renders from them rather than hardcoding a shape: the payload of this action is not something I have run, and a demo that dies on line 3 is worse than one that spends a second looking. Worth one 0.5-credit run against a live workspace before the package is resubmitted. Repo copies untouched, as with the two commits before this. Every other channel still ships the leads demo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YL6YdzRFUf27A2WChKxr1C * Rebuild the quickstart demo on aiArk, against a live run theirStack was the wrong pick and the reason is not taste: it needs its own API-key connector, so on the fresh workspace a quickstart exists for it dead-ends on rung 1. It got chosen because the fallback ladder named it, not because anything checked it. aiArk.searchCompanies is better on every axis that matters here. It runs on cargo's managed connection, so a workspace with nothing wired up can still complete the demo. At 0.01 per returned record, 25 rows cost 0.25 credits — half what the leads demo cost. And it answers the persona question with accounts rather than people. The demo also opens on countCompanies, which is fixed-cost 0. Sizing the whole addressable market for free and then sampling it for a quarter of a credit is a better first beat than 25 rows with no denominator, and it puts the pack's own count-first rule in the first thing a new user sees. Everything here is verified against a real run rather than a playbook: countCompanies free, returns {"count": N} — 1,904 on the example searchCompanies 25 rows for 0.25 credits employeeRole employee_title_or / employee_seniority_or / employee_department_or industry industry_or takes plain lowercase strings ("software development") — no autocomplete needed, and it halved the pool to 840 row fields name, industry, employee_count, city, country, domain, linkedin_url, revenue, founded_year, technologies So step 4 renders named columns now instead of printing the keys and hoping. theirStack keeps a place as rung 3, where its own-key connector is a known cost rather than a surprise. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YL6YdzRFUf27A2WChKxr1C * Remove only the mailbox bullet from the CDK critical rules The drop_block helper takes the blank-line-delimited block around a marker, which is right for a standalone diagram and wrong for a markdown bullet list: consecutive bullets have no blank line between them, so the whole list is one block. The cargo-cdk edit anchored on the defineMailbox bullet and took all of "## 6) Critical rules" with it — 71 lines instead of 12. The packaged CDK skill shipped an empty heading, losing the state-file, secrets, handle-wiring, --yes, paid-play sample and alert cost-gate rules. Caught by Bugbot on #120, not by the build: every assertion here checks that a find matched exactly once and that removed things left no trace. Nothing checked that what remained was still there. A repo-vs-package line-count diff over the whole archive now reconciles: every SKILL.md is -12 for the dropped metadata block, and each further delta matches a specific edit. cargo-cdk is the only file that was wrong. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YL6YdzRFUf27A2WChKxr1C * Document aiArk's three undocumented actions Bugbot flagged the quickstart override for calling aiArk.countCompanies, an action absent from the packaged playbook. The action is real — the demo was verified against a live run — so the gap is in the documentation, and it is a repo gap rather than a packaging one. The playbook described six actions and asserted "all six are credits-based". The integration exposes nine: enrichCompany 0.01 the cheapest company enrich in the catalog countCompanies free {"count": N} for a searchCompanies filter countPeople free same, for searchPeople The count pair is the more useful omission. Cost discipline tells an agent to size a pool before paying per record, and on this provider that check costs nothing — but only if the agent knows the actions exist. Added alongside the per-record billing warning, where the question actually comes up. Also corrects the override's claim that industry_or takes ordinary lowercase strings, which contradicted the same playbook two files away. Both are true and neither said so: the enum members are LinkedIn-style lowercase names, identical to the strings each row returns in its own industry field, so a value copied off a result matches while a guess empties the pool. The override now says that, and points at listIndustries for anything less obvious. Repo change, so every channel gets it, not just the OpenAI package. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YL6YdzRFUf27A2WChKxr1C * Remove the personality pitch, not just its slug REMOVED_TERMS asserted on "analyzePersonality" and passed, while the packaged playbook went on selling the capability in prose that named no action: an intro pitching "a personality-analysis action nothing else in the catalog has", and a reason-to-reach-for-it clause offering "personality/selling guidance". A slug-only check waved both through. The counts were wrong too. The repo playbook says nine actions, seven credits-based; the package removes one, so it ships eight and six. Now stated as such, and the packaged action table has exactly eight rows to match. The assertion now looks for "personality" itself. It cannot collide with "personalization", which is a different word, and the sweep it flagged on the first run is what found the second clause — the fix caught its own gap before the archive shipped. Also adds .github/package-overrides/** to the Codex package workflow's path filter. The package depends on that file now, so a change touching only the override needs to run the build and the version guard, and did not. Both found by Bugbot on #120. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YL6YdzRFUf27A2WChKxr1C * Fetch the count, drop the attendee pitch, fail a broken sweep Three from Bugbot on #120, all correct. The quickstart's headline number was never fetched. Step 1 wrote the countCompanies run object to a file and step 4 told the agent to lead with the pool size, but the run object holds no results — the same skill says so two paragraphs earlier — and nothing ever called download-outputs for that run. The demo would have opened on an empty variable. Both steps now go through one fetch_output helper, so the retrieval that was already correct for the search is correct for the count too, and the headline reads $POOL. The linkedin playbook still pitched event-attendee harvesting in prose after the action row was removed: an engagement-sourcing bullet and the per-item billing note. Same failure as the personality pitch — the slug left, the capability stayed. The new assertion is scoped to that one file on purpose: "event attendees" appears four more times in cargo-gtm as a lawful BASIS for contacting someone, and that must stay exactly where it is. And the sweeps themselves could not fail. Every throw was read as "nothing matched", so a missing binary or an unreadable archive reported a clean package. grep-family exit codes distinguish these: 1 is no match, anything else is an error. Verified by putting a zipgrep that exits 2 on PATH — the build now stops with "could not verify ... zipgrep exited 2" where it previously passed. That last one is the pattern worth naming: three of the five findings on this PR were assertions that were true and conclusions that were wrong. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YL6YdzRFUf27A2WChKxr1C --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
07e792124c |
Add a reproducible build for the OpenAI directory package (#90)
* Add a reproducible build for the OpenAI directory package
The OpenAI Plugins Directory is the only channel that does not track this
repo. Its docs are explicit: "published plugins do not update those
skills live." It takes a submission-time snapshot, and every change needs
a new version, a fresh review, and a manual publish -- where skills.sh,
ClawHub, the Gemini gallery and the Claude community catalog all follow
main on their own.
So the archive cannot be a hand-assembled zip. This builds it from the
repo, takes the version from .claude-plugin/plugin.json so the upload can
never claim a version the repo does not have, and discovers skills with
the same rule skills-lint uses so the three cannot disagree.
Two deliberate differences from the repo layout, both documented in the
script: skills go under skills/ (OpenAI's convention, not our root
layout), and the Gemini symlinks are dereferenced because symlinks do not
survive a zip reliably. Skills-only, since codex-hooks.json invokes
${CLAUDE_PLUGIN_ROOT} -- a Claude Code variable -- and shipping an
unverified path into review is not worth it.
Verifies the archive rather than the staging dir: manifest at root, one
SKILL.md per repo skill, no symlinks.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Normalise the package for OpenAI's validator
First upload surfaced three rules no other channel enforces:
- interface.shortDescription is required, 240 chars max.
- Skill descriptions cap at 1024. cargo-gtm (1393) and cargo-connection
(1208) exceed it, both because they enumerate every provider and
integration -- which is exactly what makes them route well elsewhere.
- metadata in SKILL.md is rejected outright. Ours carries OpenClaw
install directives, meaningless to OpenAI.
All three are fixed in the package, not the repo, because the repo copies
serve channels that want the fuller form. The two long descriptions are
rewritten by hand rather than truncated -- every trigger phrase kept, the
rosters cut to a sample plus a count -- so no list ends mid-name.
A description that grows past the limit without a rewrite now fails the
build with the reason, instead of shipping into a rejected review. The
archive is re-read after zipping and checked against the validator's own
rules, since that is what actually gets uploaded.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Add the required plugin icon and verify it is square
OpenAI requires interface.composerIcon and interface.logo, both square.
assets/icon.png is the Cargo product mark at 512x512, lifted from the
browser extension where it already ships -- the real brand asset rather
than a new one. The repo's existing assets/logo.svg would have been the
obvious source, but rendering it here only produced a padded, off-centre
bitmap, and a logo that sits wrong in the UI is worse than none.
The build now resolves both asset paths inside the archive and checks the
image is square, because a dangling path or a 1200x630 crop is invisible
from the manifest and costs a review cycle to discover. Verified against
a deliberately non-square image: it fails with the dimensions.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Automate everything up to the upload, and encode the full ruleset
There is no submission API. The portal is the only path and every version
is human-reviewed, so the upload itself cannot be automated. Everything
before it can:
- On a release, CI builds the archive and attaches it, so publishing is
"download the asset, drop it in the portal" rather than "find whoever
built the zip last time".
- On a PR touching skills, assets or the builder, the same build runs,
so a change that would fail OpenAI's validator fails in CI instead of
months later in a review cycle.
Also encodes the published error catalogue. Two of our values would have
failed the final stage after passing the uploader, because the two tiers
disagree: shortDescription is 240 chars at upload but 30 in the directory
(ours was 201), and displayName drops from 80 to 30. LIMITS targets the
stricter tier throughout, plus name/version/URL shape, skill identity
length, archive entry count and path rules, and icon bounds.
The runner shims sips - scriptable image processing system.
This tool is used to query or modify raster image files and ColorSync ICC profiles.
Its functionality can also be used through the "Image Events" AppleScript suite.
Usages:
sips [image-functions] imagefile ...
sips [profile-functions] profile ...
Profile query functions:
-g, --getProperty key
-X, --extractTag tag tagFile
--verify
-1, --oneLine
Image query functions:
-g, --getProperty key
-x, --extractProfile profile
-1, --oneLine
Profile modification functions:
-s, --setProperty key value
-d, --deleteProperty key
--deleteTag tag
--copyTag srcTag dstTag
--loadTag tag tagFile
--repair
-o, --out file-or-directory
Image modification functions:
-s, --setProperty key value
-d, --deleteProperty key
-e, --embedProfile profile
-E, --embedProfileIfNone profile
-m, --matchTo profile
-M, --matchToWithIntent profile intent
--deleteColorManagementProperties
-r, --rotate degreesCW
-f, --flip horizontal|vertical
-c, --cropToHeightWidth pixelsH pixelsW
--cropOffset offsetY offsetH
-p, --padToHeightWidth pixelsH pixelsW
--padColor hexcolor
-z, --resampleHeightWidth pixelsH pixelsW
--resampleWidth pixelsW
--resampleHeight pixelsH
-Z, --resampleHeightWidthMax pixelsWH
-i, --addIcon
--optimizeColorForSharing
-o, --out file-or-directory
-j, --js file
Other functions:
--debug Enable debugging output
-h, --help Show help
-H, --helpProperties Show help for properties
--man Generate man pages
-v, --version Show the version
--formats Show the read/write formats with ImageMagick -- the icon check is the only
macOS-specific part, and that beats paying for a macOS runner to measure
two PNGs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* Detect packaged symlinks from zipinfo modes, not a listing that never shows them
The post-zip guard tested `unzip -l` output for ` -> `, but that listing
prints only length, date, and name. No zipinfo format prints ` -> target`
either, so the check could never fire and a packaged symlink would have
shipped despite being treated as a hard upload invariant.
Read zipinfo's Unix mode column instead, where a symlink reads
`lrwxrwxrwx`, and name the offending entries in the error. Require a mode
for every entry so an archive whose types cannot be read fails loudly
rather than passing by default.
Co-authored-by: Auree Aubert <aureeaubert@users.noreply.github.com>
* Answer the spam rejection with real gates, not a reworded description (#92)
OpenAI rejected cargo-gtm under "Spam mass abuse". The description was the
whole case against it: "find emails, phones and LinkedIn URLs", "write cold
emails", "build me a list of" — contact harvesting plus bulk cold mail, with
nothing in 8,800 lines about consent, suppression, or volume. Opening the
archive confirmed it; the outreach guide and prompt library are cold-email
prompts end to end.
Rewording alone would be dressing it up, so this does both halves.
references/acceptable-use.md is the substantive half — a peer of
cost-discipline.md at the same mandatory tier. Hard refusals (undifferentiated
fan-out, consumer targeting, lists with no stated origin, contacting a
suppressed record, filter and identity evasion, auto-dialing, batch-blasting
LinkedIn engagement actions), three free blocking checks before any outreach
step (basis, suppression, relevance), what drafted copy must carry, and the
data-hygiene obligations that follow a person out of the workspace.
It is wired into the path an agent actually walks rather than filed next to
it: a mandatory section above Bootstrap, the outreach row of the routing
table, a gate at the top of the outreach guide, and a "before you start" block
in outreach-activation that subtracts suppression before enrichment — which
is also cheaper. The opener prompt now returns NULL when no signal justifies
writing to that person.
The description half stays honest. It leads with B2B, licensed providers, and
the gates, and says the pack sends nothing itself. Every routing-eval trigger
survives except two rewordings ("write cold emails", "find emails for these"),
so evals/routing.jsonl line 14 is worth re-running before the next upload.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Auree Aubert <aureeaubert@users.noreply.github.com>
|