A recruiting team mailed 79 addresses that bounced (2026-09-07). Only one had been
through a verify call. Re-verified through treg, 73 of the 79 come back `invalid` from
LeadMagic or Hunter at under a cent each; the 5 that pass are live mailboxes with
auto-replies or a gateway reject that no verifier can see. The addresses were Hunter
domain-search rows with `verification: null` (confidence as low as 10) and `info@`
guesses for domains treg had returned nothing for. A workflow gap, not a vendor gap,
so this is copy and one contract field, not routing:
- `people.search` gets `advice_unverified`: a search contract has no `verified` output,
so the advice attaches to every hit — rows are directory listings, verify each address,
never send to one the provider did not return. `contracts.py` comment updated: this
reverses "a search result is not something you verify".
- `hunter.companies.emails` summary carries the same warning, because a direct `/call/`
relays the body verbatim and the catalog entry is the only place that caller reads.
- `skill.md` and `llms.txt` get a "verify before you send, every address, every time"
rule covering search rows and guessed addresses; plugin mirrors regenerated.
- Fragment `docs/context/architecture/catalog.md` updated in the same commit.
Nothing is chained: treg still never runs the verify call itself.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XKF4gnLT7N9snnZD2W5vFy
A find returns the provider's best match; only `output.verified: true` means it checked the
mailbox. The routed email and phone contracts now carry `advice_unverified`, one sentence the
router attaches as `_treg.advice` to a hit whose `verified` is not true (Hunter's `accept_all`,
LeadMagic's personal finder, every phone provider), pointing at the verify step. A suggestion
only: treg never chains the verify call, which would double every hit's price and change what
the find bills for. skill.md and llms.txt say the same thing up front; the plugin mirrors are
regenerated.
Also moves the call-matrix B3 case onto the settle rule from the previous commit: an
unreported per_call 400 refunds (reason `rejected_unbilled_400`), a reported one bills exactly
the reported charge (B3b).
Fragments: docs/context/architecture/catalog.md.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gymm1FRGFWBnXjn3oSigUG
A caller that sends a filter a provider cannot express gets the looser answer, billed, with the
filter named in X-Treg-Ignored-Filters. That is the documented default and stays so. Opt-in,
`X-Treg-Route-Strict-Filters: 1` drops every such candidate at planning time (listed in `dropped`
with `strict: true` and the identities the adapter does take) and answers route_no_candidate 422,
unbilled, when nothing is left. A 503 from that path is now reserved for capacity and key drops;
an identity mismatch ("needs {q}") was wrongly counted as one before.
Documented at every agent-facing site: skill.md, llms.txt, USAGE.md, the CLI plan hint and the
routed endpoint's `headers` block.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016DkorqzbwiRGQVQiVLf6iU
Two money regressions from the settlement-basis refactor, found by /code-review:
- An overflow child carried the PARENT's settlement basis, so an aggregator that reports no cost
settled at the parent's direct price (or table ceiling) instead of the aggregator reserve.
`_child` now carries its own observed-kind basis at the aggregator price. Regression test.
- The worker caught only httpx/JSON/RuntimeError; relay() raises GatewayFailed (an unset platform
key, an SSRF refusal), which escaped `_process` and, through a bare asyncio.gather, aborted the
whole tick every run while the reaper deliberately left those holds alone. `_process` now backs
off on any exception and `settle_due` gathers with return_exceptions. Regression test.
Also:
- A 2xx from an async endpoint that is not an accepted submission (not JSON, expect rule failed,
no task id / off-allow-list poll URL) settles at zero on the request path instead of becoming a
24-hour pending row (`_submission_rejected`); defer_submission no longer has a fail-closed branch.
- The CLI reads the submission through the domain's extract_submission (task id and the https +
allow-list rule the worker applies) and no longer calls .json() unguarded.
- reconcile.async_task_settlement bounds its query (pending rows or completed since the window).
- Activity artifacts are looked up by the archive's indexed key hash, not the unindexed req_url,
and a pruned carrier snapshot no longer raises.
- One dotted-path reader: settlement and settle.py use domain.asynctasks.json_path.
- Em dashes on lines this branch added are plain dashes (user house rule); plugin skills rebuilt.
- proxy-model.md documents the async branch of the call path; money/archive fragments updated.
sqlite 2546 passed; Postgres subset 240 passed (local postgres:16).
Conflicts resolved:
- resolve.py: main's authorization-method ladder (chosen_tool / chosen_secret / methods) kept,
with the frozen settlement basis and async descriptor computed before `common` as before.
- routers/call.py: main moved the access dry-run to application/call/access.py; the price-range
and usage-settlement wording is ported there.
- Migration renumbered: main shipped 0010 (oauth authorization_method), so the async task table
is 0011 on top of it; docs and models references updated.
- catalog_validate: PARAM_MULTIPLEXED is the union (instagram + minimax + openrouter).
- catalog_ingest: main's carried-field tuple, still gated by carry_capability for `capability`.
- cli: both `-p/--query` and `--authorization-method`.
- Fragments (catalog, auth-secrets, data-model) keep both sections; MAP regenerated.
sqlite: 2543 passed. Postgres (CI subset, local postgres:16): 269 passed.
The call-path lock moves to its own ratestore namespace (capacity🔒<key>) that the
sweep never writes, so a balance API reading a different meter cannot undo it. A balance
or quota signature is a strike; the second within 10 min with no 2xx between locks - the
provider for a balance signature, only the endpoint for a quota one. While locked, resolve
admits one real call per process per minute as a probe; its 2xx clears exactly that lock
(conditional on the lock id). Every lock is capped at 6 h. With an overflow route the
probe still goes direct, so a recovered account stops paying aggregator prices within a
minute.
The audit row froze a metered async submission's reserve as its charge, so Activity
kept showing "charged $x" for tasks that were still running or had been refunded.
`/calls` and `/calls/{ref}` now join the org's AsyncTaskRecords (application.asynctasks
.views_for), report `async_task` (state, reserve, settled amount, completion time) and
rewrite the charge to what actually hit the balance: null while pending, the settled
figure (0 after a refund) at a terminal state. The artifact is derived from the archived
terminal JSON through the descriptor (domain.asynctasks.artifact): the result URL for
path-mode rows, the exact `treg call` retrieval command for fetch-mode rows, plus the
descriptor's ttl_note. archive.load_terminal_responses reads the evidence back; treg
still never follows or stores media.
Dashboard Activity renders the state chip (generating… / done / failed · refunded /
timed out), "hold $x" while pending, and a `result ↗` link or `result via CLI` chip with
the expiry note. `treg audit` carries the same summary under `task`.
Docs: skill.md and llms.txt gain the "generate a video or an image" task now that the
mechanisms are built and verified (async descriptor, --await, shell-timeout warning for
CLI agents, lazy polling for MCP agents, reserve-then-settle money, expiring URLs);
USAGE.md documents `treg call --await`; plugin SKILL.md copies regenerated; fragments
for api, dashboard, cli, archive, money and the skill updated with the code.
Verified live on the demo server: a $0.003 FLUX Schnell submission showed
"generating… hold $0.003", and after one worker tick "done $0.003" with the result link.
On #278 (hubs): sitetrack.js restored before adtrack.js (the attribution
test caught its removal); render_hero strips the prompt fence with
code_text (it rendered '<code>text…' verbatim) and renders the dropped
bold price line as the subline; the FAQ parser learns the one-line
'**Q?**: answer' form (several visible FAQs were folding into one JSON-LD
acceptedAnswer); the kicker is read from the catalog at build time and
floored to a bound instead of hand-typed; the advertised .md alternates
are gone (the route never served them); brand anchors read treg.to; six
dead spoke links now point at pages that exist; and the lead-enrichment
proof block carries the $3.62 50-company workflow receipt instead of the
1-email demo, selling /workflows/find-and-verify-a-lead-list rather than
competing with it. Pages 1/4/5 gain the alternative + MCP FAQ items.
On #279 (install copy): the broken install-image slots are removed (the
referenced setup PNGs never existed; a page ships without the slot);
llms.txt tells the same one-line install story as the pages instead of an
unverified Connectors-directory flow; llms.txt/skill.md counts move to
the bound style (2,600+/60+) and the plugin skill copies are regenerated;
the agent H1 is the keyword and the promise ('The ChatGPT Connector: call
2,669 APIs without keys') with the role wheel on its own line — a persona
in the H1 read as the page's audience.
On #276 (/tools): the setup line is the canonical em-dash form again (a
colon variant forked the product's one paste-line); 'other tools' counts
via the census; descriptions go through _serp_desc; the HowTo steps
mirror the visible setup order.
docs/context/interface/seo.md updated for all of it in this commit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MZEo61YXvfwUC84WmTJbaU
The adoption release - migrations now execute through Alembic.
Changelog:
- python -m treg upgrade: the explicit release phase (schema + idempotent release tasks);
the default serve path runs it before uvicorn, so self-hosted stays one-command
- first boot on an existing database adopts it: frozen legacy init_db to terminal state,
a full table/column sweep, then alembic stamp head - incomplete schemas refuse by name
and never stamp; this version is the version floor a lagging install must upgrade through
- migration scripts ship inside the wheel (src/treg/alembic/); a pip install can migrate
- raw-ASGI operators must run python -m treg upgrade once per release (ops/deploy.md)
- role startup manifests no longer write data (companion backfill and single-user
provisioning moved to the release phase / serve pre-phase)
- routing: a provider that cannot express a supplied filter never wins on price (#254),
people-search keyword coverage (#256), routed discovery is a runtime switch (#257)
- the refusal messages carry their remedy: the adoption-window instruction names
tools-registry[server]==0.14.* and a rollback past the floor gets a named error
Ships the routed-endpoint CLI to PyPI/brew — the server half has been live since #242.
Changelog:
- treg catalog search groups a capability under its routed parent (5 children + '+N more'),
matched children pull their parent in, --limit hint when results are cut
- treg catalog get on a treg.* row: ROUTES AMONG plan, ALSO (same job, not routed — call by id),
RUN IT uses the identity variant most providers accept
- treg call --header for route options (max-cost, waterfall, prefer/exclude)
- treg org overflow on|off (team opt-out from aggregator overflow)
- skill.md + plugins regenerated (routed endpoints, overflow disclosure)
Build fix, found by the sdist leak scan on this build: hatchling ships every file git does not
ignore, and .git/info/exclude is local-only — docs/evidence/overflow-map-2026-08-26 (127 MB of
aggregator catalogs and probe dumps, naming partners we do not name publicly) would have been
published. sdist now carries an explicit exclude list; .gitignore states the same for git.
Tarball 166 MB → 19 MB.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeYKFQQpvESoBUX4WuUkZy
Jason, 2026-08-28: the routed endpoint's purpose IS to find the thing; misses on the per-success
children are free since settle learned the adapters' miss rules, so the cost argument for opt-in
fell away. The default ceiling keeps the dearer per-call children from running away silently.
docs/CAPABILITY-ROUTING-PLAN.md R1–R3 + R5 for the plan's worked example. Contracts
(catalog/contracts.yaml) and per-endpoint adapters (catalog/adapters.yaml) parsed and fixture-
verified at catalog load (domain/catalog/routing): an adapter that cannot round-trip its
test_request + example_response is not a candidate. One GENERATED treg.<capability> row per
capability with ≥ 2 verified children (8 for people.email.find). application/call/route.py runs
each attempt as a full child execute_call ({call_ref}:r{n}: own hold, audit, overflow, settle,
cancellation unchanged), own keys first, then expected cost per hit (cost_at at the requested
size; ok_rate stands in for hit_rate until a counter exists); vendor 4xx = caller fault, stop;
5xx/429/402 = next candidate (≤ 2); miss stops unless X-Treg-Route-Waterfall, bounded by
X-Treg-Route-Max-Cost. Response {output, raw, _treg.{served_by, tried, charged_micro}} +
X-Treg-Served-By / X-Treg-Providers-Tried; catalog_get on the parent returns the plan.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeYKFQQpvESoBUX4WuUkZy
Org.platform_overflow_disabled (legacy _ensure_bool_col + alembic 0005; last column so alembic's
append matches create_all order) → GET/PATCH /orgs/{id}/settings `platform_overflow`, `treg org
overflow [on|off]`; honoured before any aggregator is contacted on both entry points. The charter's
'not built' row, llms.txt, skill.md (+plugin), README and USAGE now say what treg may do — serve the
SAME endpoint through a treg-owned relay when its own account is out, disclosed via
X-Treg-Served-Via — and that it still never chooses between providers. Rollout runbook in
ops/capacity.md; TREG_OVERFLOW_MODE stays off in render.yaml until the shadow week is done.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeYKFQQpvESoBUX4WuUkZy
Tier 4 consults the in-process capacity view (domain/capacity/view, ratestore-backed, 60 s TTL)
after _platform_offer: an exhausted provider raises the typed CallFailure('provider_capacity',
503, blame=treg) — no hold is ever placed, refused_by='capacity', body names resets_at and the
same-capability alternatives (treg still does not choose). After a tier-4 answer, a confirmed
balance/quota signature (domain/capacity/signatures) marks the provider exhausted in ratestore
on its own session, after the settle — the one new dataplane write, listed in the allowlist as
capacity_exhausted_mark. Burst/unknown 429s only log. Tiers 1/2 never consult any of it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeYKFQQpvESoBUX4WuUkZy
Adds exa.creators.search (creators.search, semantic shape: describe the creators, pinned to the
platform domain; verified live, $0.012 at 15 results), promotes creators.search to the global
taxonomy, rewrites influencers.club's name so 'influencer/instagram/tiktok' queries find it and
records the four filter traps hit live (country-level location, ISO language list, brands vs
keywords_in_bio, nlp_search dropping constraints), adds influencer/kol/ig aliases, and a skill.md
note on running both shapes and merging. On the LessieAI people-search-bench influencer track the
union scored 55 vs 38 for the structured route alone (2026-08-27).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014ekkEhMZcniBQ4aGX5ZsRD
Nine curated endpoints on treg's platform key and BYOK, priced in dollars from Exa's rate card and
verified live against the costDollars meter every response carries (search $7/1k ≤10 results
+$1/1k beyond, deep $12/1k, contents $1/1k page per type, answer $5/1k). Platform billing settles
the exact costDollars.total (same contract as dataforseo's reported cost), so per-result and
per-content riders bill as Exa charges them. Connect probe is a $0.001 cached /contents call —
Exa has no free account route; a bad key is 401 INVALID_API_KEY, observed as a 422 on connect.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014ekkEhMZcniBQ4aGX5ZsRD
Adds minimax/ as a fifth generated shop window: .minimax-plugin/plugin.json
(schemaVersion 1, Productivity), icon, and a SKILL.md rendered by build_plugin.py
with a CLI-only bootstrap (no treg mcp install — it cannot write a MiniMax config).
scripts/minimax_plugin.py pre-runs MiniMax's intake rules and builds a wrapper-free
ZIP; both are pinned by tests/test_plugin.py. docs/MINIMAX-PLUGIN.md carries the
form values, pipeline, review risks and the App/Connector path for native MCP.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBkwAP4DPMJsxBRmG1UGSb
Registry: `influencersclub` key provider (Bearer JWT), probe /public/v1/accounts/credits/ —
the trailing slash is load-bearing (Django 301 otherwise). Bogus key observed rejected live:
422 "influencers.club rejected that token (HTTP 401)"; real key connected and was revoked.
Catalog: 15 endpoints on a new `creators` platform (Enrichment shelf) — discovery by filters or
plain-language brief, lookalikes, audience overlap, three free dictionaries, four enrich-by-handle
depths (raw 0.03 / profile 0.2 / analytics 0.8 / full 1), connected socials, posts, post details,
credits. 14 verified live with examples; enrich-by-email is untestable by PII policy. Every
price reconciled against the vendor's credit meter between calls (all matched the docs).
Tier 4: fx.yaml $0.23/credit (vendor's own API-tier figure), config + render.yaml slot
TREG_PLATFORM_KEY_INFLUENCERSCLUB — unfunded until allow-listed.
Traps recorded: nginx 60s 504 on cold enrichment calls that is sometimes still billed; YouTube
channel ids get lower-cased (use @handle). Front-door counts bumped to 2,856 / 57.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016qpA3abbAaXsSNGYuPy3hi
Maintainer verification of the vendor listing (PR #141), merged onto main past
the enrichment-expansion conflicts:
- All 11 testable endpoints PASS live (examples captured, verified: stamped);
the async poll was exercised manually with a fresh run id (DONE, torvalds ->
linkedin.com/in/linustorvalds, poll free) and stays 'untestable' only because
a static test_request cannot carry a fresh run id.
- Probe verified: real key 200 with the org's credit meter, bogus key 403.
- Charge reconciliation via chargeInfo.creditsCharged: 9 of 10 claimed prices
exact (incl. the NL search's first-page +2 interpretation surcharge). ONE
vendor error fixed: GitHub->LinkedIn trigger claimed 1 credit/person, the
live charge and the account's own rate card both say 5 (email-only 3) —
repriced from observed, confidence verified.
- Reverse-email hit price observed at the claimed 2 credits; the catalogued
test_request is a deliberate free miss, now noted as such.
- Front-door counts: 56 providers / 2,791 endpoints; plugin skills regenerated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SnkCWqc4uDbg7FHd5wZSN
New providers, every endpoint live-tested against the real API on 2026-08-20
(183 of 195 carry a verified stamp + captured example; the rest record why not):
- companyenrich (41): company/people enrich & search, lookalikes, work email,
workforce insights, free counts/autocompletes/geo lookups
- oceanio (23): company/person enrich with web traffic + tech stack + headcount
growth, lookalike company search, ICP segmentation (fx usd null until the
vendor's plan price is machine-readable — tier 4 stays refused)
- tomba (23): email find/verify, domain search, LinkedIn/author→email, phone
finder, tech stack — two-header key+secret pair via the extra-credential flow
- predictleads (27): funding events, hiring, technographics, news, products,
website evolution — key:token pair rides HTTP Basic like dataforseo
- findymail (18): email find/verify, employees, lookalikes, technologies
(separate verifier-credit pool priced honestly as its own unit)
- branddev (13): brand assets from a domain/name/ticker/email — logos, colors,
fonts, styleguide, screenshots
- icypeas (18): email find/verify, domain scan, reverse lookup, lead database
(async start/poll modelled as separate endpoints)
- leadsforge (32): 500M-contact lead search with free filter vocabularies,
email/phone/LinkedIn reveal, company lookalikes & followers
Also: registry entries with live bogus-key rejection recorded per probe,
fx.yaml credit rates with published-price provenance, verifier_credit unit,
platform-key slots (config + render) left unfunded, provider counts refreshed
on the agent-facing front doors, and the two gate-test lists extended.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014SnkCWqc4uDbg7FHd5wZSN
The two that mattered:
* `_observed_cost_micro` changed SHAPE on both sides: this branch gave it the match object (for
billed-oauth resource counting); main extended its body with leadmagic/lusha/apollo parsing.
The function itself merged clean (the branch already contained main's parsers); the TESTS did
not — resolved as the union in the branch's _mk style, keeping every provider row and the
apollo miss test.
* skill.md: main's #127 deliberately stripped billing language from skill descriptions (the
ChatGPT policy scan reads it as commerce). The branch's X-exception re-added dollar figures
there. Resolved: main's policy-clean wording plus ONE factual X note with no prices; the full
rates stay in llms.txt. All four generated skill outputs rebuilt and verified against the
source.
Also: platform_daily_cap_usd stays at main's 100.0 (the branch predated that change), the
oauth_billed_providers kill switch lands beside it, and the reserve meta keeps main's tags +
call_id alongside the branch's oauth tier marker.
1538 pass on the merged tree.
A fourth shop window off the same generated skill. dsh reads no plugin.json:
it installs an npm package whose package.json declares `dsh.bundle`, pointing
at a config layer that composes into the user's profile.
dsh plugin --profile <name> add github:superdesigndev/treg
It is the only surface that can ship the connector AND the CLI path in one
zero-config install. The Claude manifest declares no MCP server because
anything declared there is registered before anyone has signed in - five
always-on tools that 401 on every call. dsh evaluates a row's `disabled`
expression at boot, so the treg MCP row can exist and stay off until
TREG_TOKEN is in the environment: a token-holder gets mcp__treg__* at boot,
everyone else gets a clean install and a skill that walks them to `treg login`.
The dsh variant gets its own bootstrap for two reasons the other three do not
have: the tools are namespaced (`mcp__treg__call`, not `call`), and
`treg mcp install` is the wrong move here - it writes Claude Code / Cursor /
opencode configs, never a dsh profile. `treg mcp install` now says so when it
detects ~/.dsh, and points at the bundle instead.
dsh/index.js is hand-written ESM with no dependencies, both deliberate: a git
install fetches sources rather than build output (a `prepare` script would make
every user allowlist a build first), and an out-of-tree bundle depending on an
in-box @deepseek-ai package installs a second copy that drifts from the host's.
tests/test_plugin.py pins both, plus the no-token gate.
Verified against @deepseek-ai/dsh@0.1.0-rc.6: the bundle installs into a
profile, its layer composes, and the package resolves and returns a valid skill
candidate. The first version of the gate read `!!js !process.env.TREG_TOKEN`,
which took the whole profile down with "duplication of a tag property" - YAML
reads the second `!` as another tag. That is now a test.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsD1EYGHk1an8DLL1yrzqK
Jason's call, and it is the right one: the host runtime already has a permission
model. Claude Code, Codex and Cursor each decide what needs confirming, and a
skill telling the agent when to stop for approval is both redundant and the
single biggest thing the policy scans keyed on.
Deleted outright rather than reworded:
- the whole "Running treg commands" section — batching reads, when to confirm
spending, which verbs need separate approval. Every version of that paragraph
I have written this session got flagged; there is nothing to flag if the skill
simply does not discuss approvals.
- the connected-account guardrail I added earlier ("confirm per post", "never
unsolicited or bulk"). Well-meant, but it was me writing policy into a product
doc, and naming spam is not a great way to avoid a spam classifier either.
- "Rules for spending someone's balance" -> "Notes", stating the fact (the price
is in `catalog get`) without instructing the agent when to ask.
- the upload paragraph's consent commentary, down to what the command does.
What is left is the product: what treg covers, how to search the catalog, how to
read an endpoint's params and price, how to call it, and how to choose between
providers. 223 lines, down from 268 at the start of the session.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012x16Skh3DNWoqv7LgNxUYF
Not softened — removed. Each was either not the product any more, or was being
taught in the most alarming possible way.
1. "ask once, not per command" -> "group the reads, ask for the rest".
The old text told the agent not to stop for approval between commands, in a
skill that can publish to a connected account and spend ad budget. Two
independent scanners flagged that same paragraph (ClawHub as EA2, and it is
my best guess for OpenAI's "Spam mass abuse"). Reads still batch — that part
was always right, browsing a catalog is discovery. Anything that registers a
credential, shares with the team, changes settings, publishes or spends now
asks separately, every time.
2. URL passthrough -> call by TOOL NAME.
`{BASE}/call/https://api.intercom.io/...` taught "prefix any upstream URL and
we attach a credential", which reads as an open credential proxy. Same
capability, but `/call/<tool-name>/<path>` says the true thing: only tools
this org registered resolve at all.
3. `treg run <vendor CLI>` — deleted from the skill. Arbitrary CLI execution
against stripe/gh/gcloud is a big policy surface to carry for something that
is not the core product; the catalog is. Still in the CLI, no longer taught
here.
4. Manifest: "scraping" out of the description, longDescription and keywords in
favour of "web data"; dropped `credentials`/`api-keys` keywords; vendor CLIs
out of the tool list. Also fixed `cli_auth`, described as "material lifted
from a CLI's keychain" — inaccurate as well as alarming: the human copies the
token out and supplies it. ClawHub rated that phrasing HIGH (PE3).
Also sets shortDescription to "OpenRouter for agent tools" (26 chars): Codex
renders it as the subtitle and the form caps that at 30, so it cannot carry the
full positioning sentence. Two tests updated to match rather than deleted — one
now pins the subtitle length, the other pins the tool-name call form and asserts
the raw-URL form does NOT come back.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012x16Skh3DNWoqv7LgNxUYF
I kept rewriting the billing sentence instead of deleting it. The agent treats
treg as a service provider — it needs to know which use cases treg covers and
which endpoints it can call. Prepaid balance, per-call metering, $1.00 free,
"never metered": all of that is between treg and the human, and none of it
belongs in the frontmatter description or the store's longDescription.
Both now list capabilities and nothing else — SEO/SERP, keyword volume,
backlinks and authority, AI visibility, social profiles and trends, enrichment,
ad libraries and campaign management, web data, plus Analytics/Search Console/
Business Profile through connected accounts — and end on how to use it: search
by task, read parameters and response, call it.
Also drops the billing clauses from the two intro bullets and from the head of
the catalog section, for the same reason.
Cost guidance that the agent actually ACTS on stays where it belongs, in the
spending rules further down: the price shows before you call, tell the human
first, batch-confirm cheap runs. That is behaviour, not an explanation of our
business model.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012x16Skh3DNWoqv7LgNxUYF
Jason's call, and the better one. The previous commit answered "access control
circumvention" by explaining WHY treg is allowed to serve a provider the team has
no account with. But the agent never needed to know that in the first place — it
needs to know what treg can do, what it costs, and how to call it. Whose account
sits behind an endpoint is a human billing concern that had leaked into agent
instructions, and carrying it meant also carrying a defence of it.
So the claim is gone, and the defence with it:
"data you have no key for" -> "what treg can do for you"
"No provider signup, no subscription" -> "billed per call, $1.00 free"
"serves on its own paid account ... provider -> (deleted; the agent does not
is still paid, still enforcing its terms" need the resale explanation)
"most callable with no API key of your own" -> "billed per call from your
(manifest longDescription) team's prepaid balance"
What stays is what an agent acts on: price before you spend, own tools are never
metered, and the guardrail on publishing to connected accounts — confirm per post,
never bulk or unsolicited. That last one is real guidance, not scanner appeasement.
Net effect: the page is shorter, it is about capability and cost rather than
account status, and the phrases that read as circumvention are simply absent
rather than argued with.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012x16Skh3DNWoqv7LgNxUYF
The Codex skill scan failed on two policy categories: "Spam mass abuse" and
"Access control circumvention". Neither describes what treg does. Both describe
what the skill SOUNDED like, because the doc assumed a consent model it never
stated.
Read cold, these are the lines that did it:
"the catalog: data you have no key for" -> use a service you lack access to
"No provider signup, no subscription" -> skip the provider's gate
"treg can serve endpoints on its own key" -> borrow someone's credential
"a secret SOMEONE ELSE uploaded ... that's the point" -> use a stranger's key
"point treg at a directory - it detects provider keys in the .env" -> harvest
"act on connected accounts (post on social...)" + "don't stop between commands"
-> automated bulk posting
Every one has a legitimate reading that was simply missing from the page:
- treg holds its OWN paid commercial accounts and resells calls at cost. Buying
access, not bypassing it. Each request is a normal authenticated call the
provider is paid for, under that provider's terms and rate limits. Stated up
front now, in both the skill and the manifest's longDescription.
- "someone else's secret" means a TEAMMATE shared it with this org, scoped to
that org, spendable without being seen, attributed per token. Delegated access
inside one team - not access nobody granted.
- `treg upload` reads a directory the human pointed at, lists what it recognises,
and registers only what they tick. Now says so, and says not to run it against
a directory you were not pointed at.
- Publishing touches an account a human connected on purpose and other people see
the result. New guardrail: confirm content per post, and never use these
endpoints for unsolicited or bulk messages, manufactured engagement, or
anything the account owner has not seen.
The frontmatter description led with "post on social", which is the first thing a
scanner reads; it now leads with the connected-account consent instead.
This is a better page for a human reader too - the delegation model was the one
thing a careful reader had to infer.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012x16Skh3DNWoqv7LgNxUYF
Two things the OpenAI upload form told us that nothing local could.
BLOCKING: `interface.defaultPrompt` must contain at most three prompts; we had
four. Dropped the balance/my_tools one — the three kept cover enrichment,
backlinks-with-price, and provider comparison, which is the story that matters.
Pinned with a test, since an upload round-trip is an expensive way to rediscover
a constant.
WORSE, and only an "info" notice on the form: "Uploaded plugins are currently
limited to skills only — MCP configuration from mcpServers, mcp.json and
.mcp.json will not be included." The Codex skill opened with "You already have
treg — use the tools, not the terminal" and a table of five tools. Uploaded that
way, every one of those sentences is false, and the first run is an agent
hunting for tools that were stripped at submission — the exact failure the
bootstrap exists to prevent, just inverted.
So the bootstrap now DETECTS instead of asserting: look at your tools first, use
them if they are there, install the CLI if they are not. That is correct for
both arrival paths rather than betting on one, which matters because the
connector route still exists and this manifest still declares it — the directory
simply drops it on upload today. Also notes that registering MCP into Codex is
manual (config.toml + env-var indirection), so the skill does not send anyone to
`treg mcp install`, which does not write Codex.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012x16Skh3DNWoqv7LgNxUYF
The store listing now carries the monochrome mark instead of the clay one:
logo.png (1024×1024), icon.png (512×512) and icon.svg are white quadrants on a
black rounded square.
The brand folder only had squares up to 512×512 and the submission form wants
1024, so these are re-rendered from the geometry in
assets/brand/twitter/avatar-dark.svg rather than upscaled from the PNG — same
112/20 corner radii, same insets, scaled 2×. The numbers are written down in
plugin/README.md so the next size is exact rather than eyeballed.
icon.svg is the FILLED variant on purpose, not the transparent mark-white.svg:
composerIcon renders in a host UI whose backdrop we do not control, and a white
mark on transparency disappears on a light background.
interface.brandColor stays clay #e0703f — an accent beside a monochrome mark
rather than a conflict, and still the product colour on treg.to. Flagged in the
README so the two move together if the brand does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012x16Skh3DNWoqv7LgNxUYF
Supersedes the previous take, which told the agent to SKIP `treg mcp install`.
That was wrong twice over.
Wrong on intent: the wanted end state is CLI + skill + tools, not skill alone.
Skills-only is a property of the MANIFEST, not of what the user ends up with.
The manifest still declares no connector — anything it declared would be
registered at install time, before a human has signed in, i.e. five always-on
tools that 401 on every call — and that is what keeps `/plugin install`
zero-config. The SKILL then finishes the setup at first run, when a human IS
present to sign in.
Wrong on fact: it warned that `treg mcp install` would "register a second copy
which would fail with 401". cmd_mcp_install does neither. It reads the token
from config and sys.exits BEFORE writing anything when there is none, and again
on a 401 with "nothing was written". So a premature run is a silent no-op, and
after `treg login` the registration simply works. An overstated warning in the
product's most-read page is its own kind of wrong.
The bootstrap is now an ordered three-step block — install.sh, treg login,
treg mcp install — and the order is load-bearing for the reason above: step 3
ahead of step 2 writes nothing, quietly enough that an agent moves on believing
the tools exist. A test pins the sequence, and pins the restart note too, since
`claude mcp add` does not take effect until the agent restarts.
Also noted for the human rather than acted on: install.sh always runs
`treg skill bootstrap` (no flag suppresses it), so step 1 drops a second copy of
this same skill into ~/.claude/skills/treg/. Harmless but redundant with the
plugin. Deleting files under $HOME is not a skill's call to make.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012x16Skh3DNWoqv7LgNxUYF
The Codex plugin is built around the MCP connector, and that listing sits in a
review queue. The skill never needed MCP: src/treg/web/skill.md already teaches
an agent to install the CLI, sign in, and call the catalog. So it also ships as
a Claude Code plugin, served from this repo as its own marketplace — live the
moment it merges, with no reviewer in the loop.
/plugin marketplace add superdesigndev/treg
/plugin install treg@treg
build_plugin.py grows a second variant rather than a second source. The two
differ ONLY in the prepended bootstrap, because they arrive in opposite worlds:
the Codex plugin ships tools, so its bootstrap says "use the tools, not the
terminal"; the Claude plugin ships none, so its bootstrap says the opposite —
install the CLI first, and do not ask the human for a provider key. The Claude
copy also gets a `version:` stamped into its frontmatter: ClawHub requires it,
Claude Code ignores it, and that is what lets one file satisfy both registries.
skills/treg/ sits at the REPO ROOT, not under plugin/, because that one path is
simultaneously what Claude Code's loader auto-discovers, what `npx skills add`
resolves, and what `clawhub skill publish` takes. `source: "./"` then makes the
plugin root the repo root — so anything auto-discovered there ships to users,
and a test asserts no stray commands/, agents/ or hooks/ appears.
A fourth door needs no registry at all: /.well-known/skills/index.json plus
/.well-known/skills/treg/SKILL.md make this host a first-class skill source
under the agentskills.io convention (Hermes reads it directly). It routes
through the same _serve_md as /skill.md, so {BASE} templates to the SERVING
host — a self-hosted registry advertises itself, not treg.to. The index
description is read from the skill's frontmatter at request time rather than
duplicated, and a test fails the moment the two disagree.
Two things corrected on the way past:
- the Codex manifest claimed MIT. LICENSE is Apache-2.0 plus a hosted-service
restriction, and a store listing is where a wrong licence does real damage.
- the two listings described the product differently. Both now carry the one
line llms.txt opens with, pinned by a test across all three places it lives.
Docs are in the same commit per CLAUDE.md. Note drift.sh has moved to
.agents/skills/ — the path in CLAUDE.md is stale.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012x16Skh3DNWoqv7LgNxUYF