81 Commits
Author SHA1 Message Date
SToneX 7fc6ee3f83 feat(find): semantic channel
v2's recall gains its second channel. infra/embed.py is an
OpenAI-compatible /embeddings client that never raises and caches a
query's vector in-process by model and folded text. application/
find_index.py builds one card matrix per catalog in the background on
the first v2 find: vectors are read from the archive's object store
under find-vectors/<model>/<card sha256>, only missing cards are
embedded, and those are written back. Without a store they live in the
process; without the API the channel stays off and the build is retried
later. The judged event and the searchlog row record the query's
embedding time and error.

The object store gains named objects for these vectors only, each body
carrying its own card hash and size. numpy joins the server extra for the
matrix product. Settings find_embed_api_key (falls back to treg's
OpenRouter key on the OpenRouter URL), find_embed_model, find_embed_url,
find_embed_timeout_s.

The bench builds the vectors before scoring when a key is set, and its
judge cache key now includes the job question's wording.
2026-09-30 18:19:49 +08:00
UncleCode 95e4a10efc Merge remote-tracking branch 'origin/main' into dev/hub 2026-09-30 12:50:31 +08:00
SToneX 1d93bf5176 fix(openapi): give each /call method its own operation id
FastAPI computes one unique_id per route from the first method its set
yields, so the multi-method /call relay published seven operations under
one hash-order-dependent id and warned on every schema build. Build the
schema from per-method views of multi-method routes (and GET-only views
of HEAD-widened routes) without touching the live routing table, drop
the snapshot script's workaround, and fail tests on the warning.
2026-09-27 19:43:00 +08:00
UncleCode a6dbaee88f feat(table): /table/<tool id>, one call answered as rows and columns
For the Google Sheets add-on. /table/ takes exactly the request /call/ takes and runs the same call
on the same road (gates, key injection, hold and settle, audit row, Idempotency-Key); only the
ending differs. /call/ is unchanged: it passes every query parameter to the provider and must
return the provider's answer as is (non-negotiable 4), so this is a sibling route, not an option.

- routers.call.run_call_surface: the body of call_tool, shared by /call/, /catalog/call/ and
  /table/; each passes its own `finish`. /call/ passes _relay_answer (stream unchanged, async
  descriptor, review invitation), so its behavior is the same.
- domain.table: a pure, stdlib-only converter with its own import-linter contract. Routed jobs take
  their columns from the contract (flat: output fields then served_by, a miss is 0 rows; a required
  list: one row per item, people-shaped lists mapped to fixed columns first, the map is data). Hub
  tools take the manifest's output fields. Anything else is flat, list or nested (tables + summary),
  found from the lists of objects in the answer, with one-item wrappers opened. Never raises: an
  answer it cannot shape is raw.
- application.table: reads the answer (8 MiB cap, raw and truncated beyond), asks the provider for
  uncompressed bytes, keeps the call's X-Treg-* headers, maps an upstream non-2xx to a JSON error
  with the same status. No money moves here: the call is settled before the answer returns.
- Behind TREG_TABLE_ENABLED (+ TREG_TABLE_TEAMS / TREG_TABLE_USERS), off by default. With it off
  the route answers a plain 404 that leaves no audit row.

Tests: tests/test_table.py (converter on saved answers; end to end: flag, four shapes, same charge
as /call/, upstream error releases the hold, Idempotency-Key replay costs 0, a hub tool).

Adds docs/context/architecture/table.md; updates composition.md, proxy-model.md, api.md,
import-boundaries.md and the AGENTS.md non-negotiable 4 line.
2026-09-26 14:52:34 +08:00
UncleCode ac5ed26e57 release: 0.22.0 2026-09-26 09:30:58 +08:00
UncleCode 3ee1948c64 Merge origin/main into dev/hub: the tool hub, behind TREG_HUB_ENABLED
Brings main's 86 commits (dashboard boot and loading, legacy dashboard removal, test pruning,
overflow and routing fixes) together with the tool hub branch.

Conflicts, both sides kept unless noted:
- the legacy dashboard stays deleted, as on main;
- App.vue and the dashboard state: main's search page and loading states plus the hub pages;
- ci.yml: main's Postgres job, with the hub tests added to its list;
- dev-local.sh: main's server environment plus the hub flag passthrough;
- test_call_application_contract.py, test_marketplace_call.py: main's pruned files plus the
  hub branch's sync `settle: usage` test.

Not conflicts: main and the hub branch fixed the same CompanyEnrich empty-page billing; main's
rule runs first, so the hub branch's copy and its test are dropped. The Listing-tab test reads
the Vue source instead of the deleted legacy page.
2026-09-26 07:32:53 +08:00
shehjad-dev 035c3959e1 release: 0.21.3 2026-09-24 23:31:04 +06:00
UncleCode 986d5ffa57 merge: main into dev/hub, hub migrations renumbered 0044-0049
Brings main's 24 commits since the 2026-09-21 merge: the new Vue dashboard in frontend/ with
the account-based rollout (legacy frozen as src/treg/web/dashboard-legacy/), Olostep and
Keenable, routed web search, TrestleIQ, call_media and resources_list on MCP, provider
resources, the search log.

Conflicts, every one "both sides added": imports in call/service.py and routers/web.py; the
error-owner table in call/types.py; dev-local.sh keeps the TREG_HUB_ENABLED passthrough in
front of main's new SERVER_ENV; the legacy dashboard keeps both the Resources and the Hub
nav entries and view names; test_mcp.py lists main's two new tools and the hub's three;
test_marketplace_call.py keeps both new test blocks; .gitignore keeps both. MAP.md and
docs/context/README.md regenerated. tests/test_dashboard_markup.py removed, as on main.

The hub's six migrations move from 0041-0046 to 0044-0049 above main's 0043; a fresh database
upgrades to one head, 0049.
2026-09-24 12:38:40 +08:00
shehjad-dev f42487625f release: 0.21.2 2026-09-23 20:08:29 +06:00
shehjad-dev adc4266d98 release: 0.21.1 2026-09-23 13:08:59 +06:00
shehjad-dev 854bed643a build(package): exclude demo videos from PyPI artifacts 2026-09-23 13:02:23 +06:00
SToneX 7279500451 merge: sync main and preserve dashboard resource workflows 2026-09-23 12:07:01 +08:00
SToneX ffc7d4eb6a fix(web): harden landing recovery and session-aware navigation 2026-09-23 11:59:20 +08:00
shehjad-dev d838e851fe release: 0.21.0 2026-09-22 23:49:25 +06:00
SToneX 698898cf14 refactor(dashboard): extract Vue app and integrate production builds 2026-09-22 17:05:52 +08:00
a949e85d83 fix(models): pin sqlmodel<0.0.45 and add NaiveDatetime annotations (#610)
SQLModel 0.0.45 rejects naive datetime bind parameters and may return
aware datetimes from the DB, breaking three crons:
- treg-asynctasks-settle: ValueError on WHERE next_check_at <= :now
- treg-catalog-stats: TypeError on created_at >= since comparison
- treg-arena-insights: ValueError on INSERT scan_until bind

Short-term fix: pin sqlmodel>=0.0.22,<0.0.45 → lockfile uses 0.0.44
Long-term prep: NaiveUTC annotations in place for eventual upgrade

Changes:
- Pin sqlmodel>=0.0.22,<0.0.45 in server extra
- Import NaiveDatetime from pydantic, define NaiveUTC type alias
- Update all datetime field annotations in models.py to use NaiveUTC
- Add regression tests for all three affected crons

Fixes production crashes in settle, catalog-stats, and arena-insights.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Jason Zhou <JayZeeDesign@users.noreply.github.com>
2026-09-21 21:26:43 -07:00
UncleCode 298abe04ad Merge main into dev/hub (210 commits) — migrations renumbered a third time
main moved 210 commits ahead in five days (413 files: AnyAPI, Prospeo, Dropleads and other catalog
listings, routed-parent adaptation, the archive's own-key and repeat-pricing work, video task
settlement, org slug history). Only ONE file conflicted, the GENERATED MAP.md, which was rebuilt
rather than merged by hand. Nothing in the hub needed a code change this time.

Migration numbers collided again: main took 0039 and 0040, so the six hub revisions move
0039-0044 → 0041-0046 and chain onto main's 0040. Single head 0046; the chain applies clean on a
fresh sqlite with both hub switch columns present. The numbers named in architecture/hub.md follow,
and every file it names exists.

Verified after the merge: our phase 10 work survived intact — `scripts/build_plugin.py` still ships
the hub section unconditionally, and all five plugins still carry the hub (9 mentions each), the
"per-deployment switch" instruction for the off case, and no raw markers. main changed skill.md and
llms.txt; the plugin freshness check passes against the merged agent files.

Green: full suite 4393 passed, 8 skipped; hub + callmatrix + mcp 213; agent pages + seo 228;
surface snapshot + alembic parity 8; import-linter 14/14; plugin check OK. One failure in the
parallel run, test_prospeo_platform_email_settles_one_credit, which passes alone and whose whole
file passes alone (363); dev/hub touches no prospeo or marketplace file.
2026-09-21 08:29:45 +08:00
Cursor AgentandJason Zhou 7ec2688a52 fix(cli): release 0.20.0 with treg host; add version note to skill
Closes #557

Root cause: PyPI's 0.19.1 was published before the `treg host` command
landed on main (c8d51c5e), but the skill served from the live server
documented `treg host`. Users installing via `curl | sh` got a CLI
without `host` and a skill that claimed it existed. Worse, because
`host` is also the name of a system DNS tool (/usr/bin/host), the
bare-word fallback (`treg claude` → `treg with claude`) intercepted
`treg host face.jpg` and ran `/usr/bin/host face.jpg` instead,
producing a confusing DNS error.

Changes:
- Bump version to 0.20.0 (0.19.1 is already on PyPI without `host`)
- Add test confirming `host` is a registered subcommand and won't fall
  through to the system binary
- Add version note to skill.md (and all plugin copies): "Requires CLI
  ≥ 0.20.0; run `treg update` if `treg host` is unrecognised."

The existing `treg host` implementation is unchanged — it was already
on main, just never released.

Co-authored-by: Jason Zhou <JayZeeDesign@users.noreply.github.com>
2026-09-17 22:57:00 +00:00
UncleCode acecf1bc19 Merge main into dev/hub (150 commits) — migrations renumbered, two breakages fixed
main had moved 150 commits ahead. Nine files conflicted; every one keeps BOTH sides, except the
catalog search hint, which keeps the hub branch and calls main's new `_run_hint` helper for a
catalog row. The app serves the hub routes and main's new media routes; the worker keeps both its
hub and arena subcommands.

Migration numbers collided a second time: main took 0033-0038, so the six hub revisions move to
0039-0044 and chain onto main's 0038. Single head 0044; the chain applies clean on a fresh sqlite
with both hub switch columns present. The numbers named in architecture/hub.md follow.

Two real breakages the merge caused, both fixed here:

1. Money. main made the signup credit verified-only (`claim_signup_promo`), and `POST /users` is
   legacy registration, which leaves the user UNVERIFIED. Every hub test buyer therefore started at
   zero and each paid run answered 402. New shared helper `conftest.funded_user()` grants the team
   1_000_000 micro; used at the 19 call sites that have to pay. The tests that deliberately exercise
   the out-of-money path are untouched.

2. Identity. main added `ApiKey | None` to `Caller` for managed keys. The hub's scheduled check
   builds a Caller directly and crashed with a TypeError. It now passes `api_key=None`: a scheduled
   check is not an API-key call, it runs as the maker's membership.

Green on the merged tree: full suite 4070 passed, 8 skipped; agent pages + seo 228; import-linter
14/14; plugin check OK; alembic parity 4. `hub_enabled` stays False by default, and with the flag
absent every hub route answers 404 with a valid request, the agent files carry no hub text, and
catalog search returns no hub row.
2026-09-16 12:20:57 +08:00
SToneX 2ac925840e fix(cli): protect MCP config tokens on disk 2026-09-12 17:56:22 +08:00
UncleCode c49d648f5d Merge branch 'main' into dev/hub (8.4)
Bring the team's work since 2026-09-09 into the hub branch: the review/feedback system, the arena
insights, the R2 body storage and cache admission for the archive, the shared key-value store, and
the SSRF CGNAT fix. 108 commits, 233 files.

The one real conflict was the migration numbers: both branches used 0026-0030 for different
changes. main's chain is 0026 (callreview) through 0032 (archive body storage); the five hub
migrations are renumbered to 0033-0037 and rechained onto 0032, so there is a single head 0037.
Everything else was additive and kept from both sides: the HubTool/HubRun models beside
CallReview/FeedbackHandling, the `hub` domain and the key-value store both in the AGENTS.md tables,
the hub CLI family beside `treg review`, the hub MCP verbs beside `review` in the tool sets, the
quickjs and obstore/redis dependencies together. mcp_feedback.py was removed on main; the deletion
is kept. The docs fragment's migration numbers were updated.

Full suite 3508 passed; agent pages + SEO 216; boundaries 14/14; the migration chain a single head
0037 on Postgres 16; the plugin staleness guard green.
2026-09-10 20:19:31 +08:00
SToneX 0c93a1f987 refactor(archive): swap aiobotocore for obstore 2026-09-10 19:06:26 +08:00
SToneX 9f67130169 feat(archive): add R2 body storage and gated double writes 2026-09-10 19:05:57 +08:00
SToneX d4ea298a8f feat(cli): release 0.19.0 with the feedback invitation line
The CLI now prints one stderr line for X-Treg-Hint: feedback beside the charge line, as it already
did for review invitations, and reads the unified header a 0.19 registry sends (the older
X-Treg-Review: requested still works against a pre-0.19 registry). Plugin manifests, the skill
copies and the lock file carry the same version.
2026-09-10 15:48:41 +08:00
SToneX 70896c7496 fix(arena): use per-request indexes for insight evidence (#428) 2026-09-10 15:33:40 +08:00
SToneX 51921c09dd feat(kv): shared key-value store with an in-process fallback
Render Key Value (Redis protocol) at TREG_KV_URL, or a bounded in-process dictionary when unset.
One narrow method, take(key, limit, ttl_s): an atomic INCR + EXPIRE NX window, capped at 100 ms,
failing closed so a store that cannot answer is a spent budget rather than a flood. /admin/kv
(superadmin) reports configured/reachable; startup warns when the configured store is silent.
The first tenant is the review-invitation budget; TREG_REVIEW_BUDGET_PER_HOUR (default 5) is
declared here for it.
2026-09-10 13:36:02 +08:00
SToneX 840a288891 feat(cli): release 0.18.0 with anonymous PostHog telemetry (#412)
* feat(cli): add anonymous PostHog usage telemetry

* fix(ci): recognize public PostHog ingestion token

* fix(ci): match ingestion token in historical diff lines

* release: 0.18.0

* fix(cli): allow cold SDK imports within telemetry exit budget
2026-09-09 22:10:02 +08:00
SToneX b0ef03a30b release: 0.17.0 2026-09-09 19:18:59 +08:00
UncleCodeandClaude Fable 5.1 5279fa4711 feat(hub): phase 3 - the script road: the sandbox
A script recipe's run.js now runs end to end (docs/HUB-DECISIONS.md round 2).

- treg/hub_sandbox.py, the CHILD: QuickJS (the `quickjs` package, hand-added to the server
  extra and uv.lock; cp312 wheels for production, sdist locally) in a separate short-lived
  process with no network, no file system, no require, no process, no timers. A JSON-lines
  bridge over stdin/stdout: ctx.call -> parent -> result | refused; ctx.log; done | error.
  64 MB engine heap; a memory bomb reads `memory`, an endless loop `timeout`, a throw `script`.
- application/hub/sandbox.py, the PARENT: runner.py's discipline - scrubbed env (never the
  server's), private temp HOME, own process group, rlimits (CPU, fsize, no core, RLIMIT_AS on
  Linux), a wall-clock kill of the whole group, cleanup on every exit. Enforces what the engine
  cannot: the 20-call cap, the log caps, the output shape.
- runner.py, the script road: every ctx.call is the same child call the JSON road makes -
  `uses` enforced per call (a URL, an unknown id, or a tool outside the list refuses and stops
  the run), the run ceiling, {run}:s{n} holds, a catalog call as the CALLER, an own-tool call as
  the MAKER. The returned object must carry output.fields (424 hub_output_invalid); a failed run
  is 424 hub_run_failed {hub_script_failed, kind, message, trace, log, charged_micro}.

Two engine facts learned and documented: quickjs cannot call into Python while its own time
limit is set, so the wall clock is the parent's kill; ctx.call is synchronous underneath, so
Promise.all serializes in version one (the JSON road has the real parallelism).

Tests: 13 hostile sandbox cases (no road out, refusal stops the run, memory bomb, endless loop,
throw, missing export, non-object return, log cap, call cap, scrubbed env, parallel runs do not
mix) + 4 end-to-end script runs on the real call path with the four books. 72 hub tests; suite
2900; hub + serial list on real Postgres 16.

Security review of the sandbox is scheduled as its own pass before release (the owner's note).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-09 18:29:44 +08:00
SToneX a17a4281ae release: 0.16.1 2026-09-08 21:08:00 +08:00
SToneX b67256836c release: 0.16.0 2026-09-07 20:30:32 +08:00
SToneX 0333516fd5 fix(feedback): complete agent docs and domain boundaries 2026-09-07 20:15:28 +08:00
SToneX 8031c83d99 docs(agents): contract-first AGENTS.md and a pinned uv floor instead of a lock ban (#335)
* docs(agents): AGENTS.md is the single guide, contract first, no state snapshots

CLAUDE.md now only imports AGENTS.md so Claude Code, Codex and Cursor read one file.

AGENTS.md is rewritten around what an agent cannot learn from the code:

- Non-negotiables move to the top and are corrected against the code. "Own key
  always wins, never metered, never routed or overflowed" is promoted to rule 1.
  The relay rule now scopes to plain /call/ and names routed endpoints and
  overflow as wrappers, since route.py injects `_treg` into the body and overflow
  adds X-Treg-Served-Via; the old wording contradicted both. The hold rule
  defines "hold"; the pool rule says why reserve and settle are two transactions;
  the ledger rule records that there is deliberately no refund entry.
- The dataplane write allowlist becomes guidance that points at
  tests/test_call_architecture.py as the authority instead of a prose copy.
- Enforcement gap inventories, per-file commit lists, contract-by-contract
  recaps and the CLI-module list are removed: they duplicate pyproject, the
  import-boundaries fragment and the tests, and rot without a drift check.
- Endpoint and provider counts are removed (three files carried three values).
- Duplicated statements (own-key, faithful relay, keep-four-in-step, money-only
  path, relay guard, secrets) are stated once each.
- Sections reordered: contract, where the truth lives, architecture,
  development, working agreement, and a closing section for user-facing copy.

* build(uv): pin required-version instead of banning `uv lock`

uv.lock is revision 3, first written by uv 0.8.4. An older uv reads it fine but
rewrites it to revision 2 on any touch, dropping every upload-time field: the
~650-line no-op diff the old "always --frozen, never uv sync or uv lock" rule
worked around. That rule also told agents to hand-edit the lock, which --frozen
would then install unchecked.

- pyproject.toml: `[tool.uv] required-version = ">=0.12"`, so an old uv refuses
  to run instead of rewriting the lock.
- ci.yml: `--frozen` becomes `--locked`, so a stale lock fails CI instead of
  being installed silently. The comment about the team's older uv is gone.
- CONTRIBUTING.md names the floor; AGENTS.md replaces the ban with "change
  dependencies through uv add or uv lock, never by hand".
- docs/context/architecture/import-boundaries.md describes the CI step as it
  now runs.

Verified: uv lock --check, uv sync --locked and uv run --locked lint-imports
(12 kept, 0 broken) on uv 0.12.3.
2026-09-05 14:52:29 +08:00
SToneX 228c0d8156 Merge origin/main into feat/aigc-catalog-grouping
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.
2026-09-02 17:49:46 +08:00
shehjad-dev 0aee9b5a25 release: 0.15.0 2026-09-02 11:34:14 +06:00
SToneX 465de97cec refactor(aigc): simplify the async and settlement seams after review
- A price table prices out as a range everywhere. The store computes the cheapest row into
  cost.table_min at load time and cost_view exposes usd_min beside usd (still the validated
  ceiling that reserve and eligibility read). The wall, `treg catalog search`, the dashboard
  and /access now show "$0.2-$1.96/success" for MiniMax H3 instead of the $1.96 ceiling alone,
  which had made direct routes look several times dearer than they are.
- The 24-hour deadline releases the hold instead of settling it at the reserve: an outcome
  nobody observed is the platform's cost, never the customer's. The row is flagged for
  reconcile (`absorbed_timeouts`) and the worker logs an ERROR-level alert naming the endpoint,
  since the usual cause is a provider that silently changed its status field.
- One reading of the async descriptor. The CLI's --await now imports json_path,
  classify_terminal and artifact from treg.domain.asynctasks (stdlib-only) instead of carrying
  its own copy; the module joins test_import_lightness and gets an import-linter contract that
  forbids every server root, so the light install can never grow a database import that way.
  artifact() returns the fetch target as {endpoint, name, value}; callers format the command.
- An endpoint's async block replaces the provider default whole (effective_async_descriptor);
  the recursive merge with mode pruning had one user, which overrode everything anyway.
- The usage unit "token" is gone from the validator and settle() until a metered token-priced
  listing exists with its fx rule and a live test; its unit derivation was wrong (it ignored
  `per`) and no real traffic had exercised it.
- AsyncTaskRecord.request_data dropped: settlement_basis already freezes the request with the
  rule. The migration is unreleased. The dead observed_override in the defer-failure branch
  is gone; that branch settles the frozen basis, which is what it always did.

Fragments (catalog, money, import-boundaries, cli) updated with the code; 2468 tests pass.
2026-09-02 13:25:31 +08:00
shehjad-dev ba85df209c refactor(connections): enforce OAuth architecture boundaries 2026-09-02 10:54:57 +06:00
SToneX 67f9fd477f Merge pull request #274 from superdesigndev/chore/pr4-followups
chore: PR4 follow-ups - dead aliases and stale paths
2026-08-31 18:02:08 +08:00
SToneX 093b256775 fix: logger names are contracts, labeler globs follow the moved files
The PR4 sweep renamed three loggers to module paths - treg.ledger, treg.proxy, and
treg.billing became treg.domain.money, treg.infra.upstream.relay, and
treg.application.billing - an undeclared behavior change on exactly the settle/release,
cancel-release, and cap-check failure paths where external log filters and alerts hook in.
Reverted to the short functional names the rest of the pipeline uses (treg.idempotency,
treg.capacity, ...): a logger name is an observability contract and does not follow code
location. Also: the area:proxy labeler glob pointed at deleted proxy.py (the label could
never fire again) and area:auth-secrets listed the two deleted shims for injection and
session signing; the stripe dependency comment and a money.md passage named deleted files.
2026-08-31 10:15:59 +08:00
Cursor AgentandJason Zhou 350ac16c12 build(deps): add playwright to test dependencies
Add playwright for mobile viewport testing. Also add tests/screenshots/
to .gitignore as these are regenerated on demand.

Co-authored-by: Jason Zhou <JayZeeDesign@users.noreply.github.com>
2026-08-30 12:52:04 +00:00
SToneX e8b25abc70 release: 0.14.0
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
2026-08-30 14:43:12 +08:00
Jason ZhouandClaude Opus 5 b56588736c release: 0.13.0
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
2026-08-29 10:26:32 +10:00
Jason ZhouandClaude Fable 5 ccde4f37f2 feat(capacity): know layer — policies, snapshots, worker sweep, latest-state view
Step B of docs/PROVIDER-CAPACITY-PLAN.md. The 27 balance collectors move byte-identically from
scripts/provider_balances.py into domain/capacity/collectors.py (the script is now a thin
reconciliation CLI over them). New tables capacitypolicy + capacitysnapshot (create_all legacy
path + alembic 0002, parity-tested). `treg-worker capacity sweep` (new console script, server
extra, worker profile) collects → snapshots → publishes per-provider latest state to ratestore
(capacity:state:*); domain/capacity/view.py loads it on a 60 s TTL. Observe-only: no alerts,
nothing on the call path reads it yet. New import-linter contract keeps the domain a leaf.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeYKFQQpvESoBUX4WuUkZy
2026-08-28 14:32:45 +10:00
SToneX ed3cd9da63 refactor(tools): move binding, ownership, and bundle rules into domain tools
Credential-binding validation, secret ownership, local-run profile and
inject checks, bundle org-scoping, and bundle file hygiene move from
routers/resources.py into domain/tools (bindings.py, bundles.py) with
framework-neutral ToolConfigError/SecretOwnershipError. The router keeps
same-named wrappers that supply the caller facts and injector vocabulary
and translate the errors back into the identical 422/403 shapes, so the
HTTP surface and api.py's re-exports are unchanged. A new import-linter
contract pins the tools domain to the sanctioned tools->connections edge.
2026-08-27 17:34:19 +08:00
SToneX d8cb7e146b refactor(catalog): move the catalog store and stats into domain catalog
catalog_store.py -> domain/catalog/store.py, endpoint_stats.py ->
domain/catalog/stats.py, with compatibility facades at the old paths.
The YAML data stays at src/treg/catalog/; the store now anchors
CATALOG_DIR at the package root instead of module-relative. A new
import-linter contract pins the catalog domain as a leaf.
2026-08-27 17:34:01 +08:00
SToneX bbe8eecbdd test(architecture): enforce the call runtime boundary 2026-08-27 10:37:48 +08:00
SToneX 49d7fe49a7 refactor(money): move the ledger into domain money 2026-08-27 10:35:36 +08:00
SToneX 24f2883d04 refactor(governance): expose framework-neutral call policies 2026-08-27 10:34:54 +08:00
SToneX 6eb2dc6329 refactor(signup): extract team creation use case 2026-08-26 20:08:52 +08:00
SToneX 74251b05c4 refactor(identity): extract identity domain leaf 2026-08-26 20:08:51 +08:00