weightclass 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 weightclass contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,379 @@
1
+ Metadata-Version: 2.4
2
+ Name: weightclass
3
+ Version: 0.1.0
4
+ Summary: Local, policy-driven routing for Codex and Claude workflows.
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/ictechgy/weightclass
7
+ Project-URL: Repository, https://github.com/ictechgy/weightclass
8
+ Project-URL: Issues, https://github.com/ictechgy/weightclass/issues
9
+ Keywords: cli,codex,claude,router,task-classification
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: MacOS
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Build Tools
21
+ Classifier: Topic :: Utilities
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Dynamic: license-file
27
+
28
+ # weightclass
29
+
30
+ **weightclass** is a local, policy-driven router for Codex and Claude Code
31
+ workflows. It classifies a task in memory as `low`, `standard`, or `high`,
32
+ chooses a deterministic model-and-effort route, and can start one selected
33
+ vendor process in the foreground.
34
+
35
+ By default, a request stays with its explicit source vendor. Cross-vendor
36
+ routing is available only through a reviewed policy opt-in. An optional V2
37
+ route can start a separately installed API runtime after explicit review and
38
+ egress acknowledgement; weightclass never reads API credentials or makes
39
+ provider network requests itself.
40
+
41
+ ## Install
42
+
43
+ weightclass has no runtime dependencies beyond Python 3.10 or later.
44
+
45
+ ```sh
46
+ uv tool install weightclass # or: pipx install weightclass
47
+ brew install ictechgy/tap/weightclass
48
+ ```
49
+
50
+ Or from a local checkout:
51
+
52
+ ```sh
53
+ git clone https://github.com/ictechgy/weightclass.git
54
+ cd weightclass
55
+ python3 -m pip install .
56
+ ```
57
+
58
+ All three install the `wclass` command. The native Codex and Claude CLIs must
59
+ already be installed and authenticated; weightclass never reads or changes
60
+ their authentication or subscription state.
61
+
62
+ Releases are cut by pushing a tag; see [RELEASING.md](RELEASING.md).
63
+
64
+ For reviewable native Codex and Claude Code invocation examples, see
65
+ [Native integrations](docs/integrations.md).
66
+
67
+ ## Run locally
68
+
69
+ `wclass --help` lists the whole surface:
70
+
71
+ ```text
72
+ wclass [-h] [--version] {classify,route,run,render,v2} ...
73
+ ```
74
+
75
+ `classify`, `route`, and `run` read the task from standard input. `render`
76
+ prints the command of a policy route named by a workflow descriptor and never
77
+ reads a task. `v2` selects a declarative API route; see
78
+ [V2 API routing](#v2-api-routing-through-an-external-runtime).
79
+
80
+ Every malformed invocation — an unknown subcommand, a missing argument, a bad
81
+ policy — exits `2` with `{"error": "invalid_input"}` on standard error and
82
+ nothing else, so a caller can parse the failure without scraping usage text.
83
+ Flag names are never abbreviated: `--confirm-api-egress` cannot be shortened.
84
+
85
+ Exit codes are weightclass's own; a selected command's status never overwrites
86
+ them:
87
+
88
+ | Code | Meaning |
89
+ | --- | --- |
90
+ | `0` | Success. For `run` and `v2 run`, the selected command exited `0`. |
91
+ | `2` | `invalid_task` or `invalid_input`. |
92
+ | `3` | `unsupported_route` — no policy route matched. |
93
+ | `4` | `executor_unavailable` — the command could not be started. |
94
+ | `5` | `api_confirmation_required` — V2 without `--confirm-api-egress`. |
95
+ | `6` | `route_fingerprint_mismatch` — the reviewed route changed. |
96
+ | `7` | `executor_failed` — the command started and exited non-zero. |
97
+
98
+ Code `1` is not weightclass's; it means the interpreter died on an unhandled
99
+ exception, which is a bug worth reporting.
100
+
101
+ Code `7` carries the real status in its diagnostic, as
102
+ `{"error": "executor_failed", "executor_exit_code": N}` or, for a command killed
103
+ by a signal, `{"error": "executor_failed", "executor_signal": N}`. A selected
104
+ command inherits standard error, so this diagnostic is always written on a fresh
105
+ line and is the **last line** of standard error — parse that line, not the whole
106
+ stream, which also holds whatever the command itself printed.
107
+
108
+ A vendor CLI that reports success while declining to do the work still exits
109
+ `0`; weightclass cannot detect that and does not claim to.
110
+
111
+ Inspect a route before running it:
112
+
113
+ ```sh
114
+ printf '%s' 'Fix a spelling typo in the README.' | wclass route --source-vendor codex
115
+ printf '%s' 'Fix a spelling typo in the README.' | wclass run --source-vendor codex
116
+ ```
117
+
118
+ The built-in routes are intentionally conservative:
119
+
120
+ - Codex: `low`, `standard`, and `high` use an ephemeral `exec` session in a
121
+ workspace-write sandbox with `model_reasoning_effort` set to `low`, `medium`,
122
+ and `high`. Codex has no dedicated effort flag, so the effort is passed as a
123
+ `-c` configuration override for that one invocation.
124
+ - Claude: `low`, `standard`, and `high` use print mode, no session persistence,
125
+ and efforts `low`, `medium`, and `high`. Permissions are `acceptEdits`,
126
+ because print mode is non-interactive: a permission mode that asks a human
127
+ has nobody to ask, so every edit is refused while `claude` still exits `0` —
128
+ the router would report success having changed nothing. `acceptEdits`
129
+ auto-accepts file edits only, which lets the Claude route change files as the
130
+ Codex route already could. It does not make the two identical: Codex's
131
+ `workspace-write` also runs commands, while under `acceptEdits` a non-edit
132
+ tool still goes to a prompt that print mode cannot answer.
133
+
134
+ Neither default route pins a model. Model selection stays your reviewed
135
+ policy's decision, expressed inside that policy's `command`; see
136
+ [Override the routes](#override-the-routes).
137
+
138
+ `--source-vendor` is required when weightclass is called from a Codex or Claude
139
+ integration. With the default policy, `--source-vendor codex` selects only
140
+ Codex routes and `--source-vendor claude` selects only Claude routes.
141
+ weightclass is a standalone process, so it does not try to infer its parent
142
+ application.
143
+
144
+ When `--source-vendor` is omitted, weightclass still pins every tier to a
145
+ single vendor: the vendor of the first route declared in the policy (`codex`
146
+ for the built-in routes). A tier is never silently served by a second vendor —
147
+ that requires `"allow_mixed_vendors": true`. The `vendor` field is always
148
+ present in `wclass route` output.
149
+
150
+ Classification is local and deterministic. Security, authentication,
151
+ authorization, data, migration, concurrency, performance, production, and
152
+ architecture signals route to `high`. Short typo, spelling, formatting, and
153
+ rename tasks route to `low`; other valid tasks route to `standard`. Unknown or
154
+ oversized task input fails closed.
155
+
156
+ Three rules make the outcome predictable:
157
+
158
+ - Signals are matched on whole words, so `reproduction` does not count as
159
+ `production`. Korean has no word boundaries, so Korean signals are matched by
160
+ containment and a compound word that embeds a signal may over-escalate.
161
+ - When both a `high` and a `low` signal are present, `high` wins. Under-rating a
162
+ task is the more expensive mistake.
163
+ - A task of 1,200 characters or more is treated as `high` on length alone, so
164
+ pasting a large context escalates the tier regardless of wording.
165
+
166
+ ## Override the routes
167
+
168
+ Use `wclass route --policy policy.json` or `wclass run --policy policy.json` to
169
+ use a reviewed local policy. Routes are considered in listed order, so the
170
+ first matching `tier` is selected. Add `--source-vendor codex` or
171
+ `--source-vendor claude` when invoking it from that vendor. Configure model
172
+ labels and vendor-specific effort arguments only with labels you know are
173
+ available to you.
174
+
175
+ ```json
176
+ {
177
+ "allow_mixed_vendors": false,
178
+ "routes": [
179
+ {
180
+ "id": "codex-low",
181
+ "vendor": "codex",
182
+ "tier": "low",
183
+ "command": ["codex", "exec", "--model", "your-low-model-label", "-"]
184
+ },
185
+ {
186
+ "id": "claude-high",
187
+ "vendor": "claude",
188
+ "tier": "high",
189
+ "command": ["claude", "--print", "--model", "your-high-model-label", "--effort", "high"]
190
+ }
191
+ ]
192
+ }
193
+ ```
194
+
195
+ The `command` tokens are opaque policy values. weightclass validates their shape
196
+ but does not assert vendor CLI semantics or subscription access. Always run
197
+ `wclass route` with a representative non-sensitive task to inspect a policy
198
+ before using `wclass run`.
199
+
200
+ A token is passed to the selected program as one `argv` entry, without a shell,
201
+ so a token may contain spaces — an install path such as
202
+ `/Users/me/My Tools/claude`, or a multi-word flag value.
203
+
204
+ A token may not contain a character that a reviewer would not see, since review
205
+ is the whole point of rendering the command. Rejected are every Unicode `C`
206
+ category — control characters, format characters such as zero-width space and
207
+ the bidirectional overrides, surrogates, private-use and unassigned code points
208
+ — along with any whitespace other than the ASCII space, and leading or trailing
209
+ whitespace. The same rule applies to V2's `model` and `effort` labels.
210
+
211
+ ## Bind a run to the selection you reviewed
212
+
213
+ `wclass route` prints a `route_fingerprint` over the selected route id, vendor,
214
+ command, tier, and the policy's `allow_mixed_vendors` setting — every field the
215
+ descriptor itself shows, so you can recompute it from what you read. Pass it
216
+ back to bind the run to that selection:
217
+
218
+ ```sh
219
+ task='Review this authorization change.'
220
+ fingerprint="$(printf '%s' "$task" | wclass route --policy policy.json \
221
+ | python3 -c 'import json,sys; print(json.load(sys.stdin)["route_fingerprint"])')"
222
+ printf '%s' "$task" | wclass run --policy policy.json \
223
+ --ack-route-fingerprint "$fingerprint"
224
+ ```
225
+
226
+ If the policy, the selected route, or the task's tier changed since the review,
227
+ the run stops with exit `6` and `{"error": "route_fingerprint_mismatch"}` rather
228
+ than executing an unreviewed command.
229
+
230
+ Three limits are worth stating plainly:
231
+
232
+ - **The flag is optional, and omitting it binds nothing.** `wclass run` without
233
+ `--ack-route-fingerprint` re-selects from the policy as it finds it. This
234
+ differs from `wclass v2 run`, where the acknowledgement is mandatory because
235
+ that path can send your task to a paid API.
236
+ - **The task is not bound, only its tier.** A fingerprint reviewed for one
237
+ `low` task will run any other `low` task that selects the same route. Binding
238
+ the task would mean retaining a hash of it, and weightclass does not hash task
239
+ content.
240
+ - **The argv is bound, not the program.** If the command names a path whose
241
+ contents are replaced between review and run, the fingerprint still matches.
242
+ It binds the policy's selection, not the identity of the executable — the same
243
+ limit V2 has for `--api-runtime`.
244
+
245
+ A route has no separate `model` field, and a policy that declares one is
246
+ rejected. Only `command` is ever executed, and weightclass cannot verify that a
247
+ label matches the model a command actually selects without asserting vendor CLI
248
+ semantics it deliberately does not assert. A label it cannot verify would let a
249
+ reviewed descriptor advertise one model while another runs, so the model is
250
+ declared once, inside `command`, where `wclass route` prints it in full.
251
+
252
+ Set `"allow_mixed_vendors": true` only when you intentionally want a Codex
253
+ request to select a Claude route, or the reverse. When it is `false` or absent,
254
+ the vendor filter is applied before tier selection — including when
255
+ `--source-vendor` is omitted, in which case the vendor of the first declared
256
+ tier route is used.
257
+
258
+ ## V2 API routing through an external runtime
259
+
260
+ V2 adds declarative API-route selection without turning weightclass into an API
261
+ client. weightclass does not read API keys, inspect authentication, or make
262
+ network requests. Instead, you provide an already-installed, trusted runtime
263
+ at an absolute, executable path. That runtime is responsible for provider
264
+ credentials, HTTP, billing, and any provider output.
265
+
266
+ Use a V2 policy only for API routes; unlike the V1 legacy policy, it cannot
267
+ contain arbitrary command arrays. A route is eligible only for its declared
268
+ source vendors. `codex` maps to the OpenAI provider family and `claude` maps to
269
+ the Anthropic provider family; `allow_cross_provider` must be `true` before a
270
+ route can cross those families.
271
+
272
+ ```json
273
+ {
274
+ "schema_version": 2,
275
+ "allow_cross_provider": false,
276
+ "allow_api": true,
277
+ "routes": [
278
+ {
279
+ "id": "openai-high-api",
280
+ "tier": "high",
281
+ "eligible_source_vendors": ["codex"],
282
+ "provider": "openai",
283
+ "transport": "api",
284
+ "model": "your-openai-model-label",
285
+ "effort": "high",
286
+ "intended_recipient": "OpenAI API",
287
+ "intended_billing_boundary": "your OpenAI API account"
288
+ }
289
+ ]
290
+ }
291
+ ```
292
+
293
+ First review the selected destination and copy the returned fingerprint.
294
+ weightclass reports the intended recipient and billing boundary from the
295
+ reviewed policy; it does not verify either claim.
296
+
297
+ ```sh
298
+ printf '%s' 'Review this authorization change.' | \
299
+ wclass v2 route \
300
+ --policy api-policy.json --source-vendor codex \
301
+ --api-runtime /absolute/path/to/weightclass-runtime
302
+ ```
303
+
304
+ Starting an API route requires both an explicit egress confirmation and the
305
+ exact fingerprint from that review. weightclass recomputes the route before
306
+ spawning the runtime, so a change to the selected model, effort, source,
307
+ destination, runtime path, or API/cross-provider permission invalidates the
308
+ acknowledgement.
309
+
310
+ ```sh
311
+ printf '%s' 'Review this authorization change.' | \
312
+ wclass v2 run \
313
+ --policy api-policy.json --source-vendor codex \
314
+ --api-runtime /absolute/path/to/weightclass-runtime \
315
+ --confirm-api-egress --ack-route-fingerprint 'sha256:copied-from-route'
316
+ ```
317
+
318
+ For a selected V2 route, weightclass invokes exactly this fixed protocol,
319
+ without a shell, and passes the task only on standard input:
320
+
321
+ ```text
322
+ /absolute/path/to/weightclass-runtime --provider PROVIDER --model MODEL --effort EFFORT
323
+ ```
324
+
325
+ Do not put API keys, tokens, task text, or personal information in the policy,
326
+ route metadata, or command line. V2 does not provide retries, failover,
327
+ credential management, background execution, or a bundled provider runtime.
328
+
329
+ ## Security boundary and non-goals
330
+
331
+ - No persistence: weightclass writes no router artifacts or vendor
332
+ configuration.
333
+ - Task text is read only from standard input, held in memory to classify and
334
+ pass to the selected child process, then discarded. weightclass never logs,
335
+ stores, echoes, or places it in diagnostics.
336
+ - weightclass never reads credentials, subscription balances, pricing, cookies,
337
+ or vendor configuration. It does not capture or process vendor output. V2
338
+ does not issue provider HTTP requests; a separately installed runtime may do
339
+ so only after the explicit acknowledgement described above.
340
+ - Route selection is deterministic. Unsupported, malformed, or unsafe input
341
+ fails closed with a redacted JSON diagnostic.
342
+ - weightclass does not infer source vendor, model availability, subscription
343
+ tier, or remaining usage. Supply the source vendor explicitly and put model
344
+ arguments in a reviewed policy's `command` when model routing is required.
345
+ - `wclass run` starts exactly one configured command in the foreground without
346
+ a shell, retry, backgrounding, recovery, or process supervision.
347
+ - weightclass is not an API proxy, credential manager, cloud service,
348
+ subscription checker, bundled provider runtime, or unattended multi-agent
349
+ supervisor.
350
+ - Policies must be reviewed before use. Do not place secrets in a policy.
351
+ - `wclass route` binds a later `wclass run` only when you pass the fingerprint
352
+ it prints, and only to the policy's selection — see
353
+ [Bind a run to the selection you reviewed](#bind-a-run-to-the-selection-you-reviewed)
354
+ for what that does and does not cover. Your control of the policy file, plus
355
+ the built-in routes that live in code and cannot be swapped, is the boundary
356
+ that always applies. Treat a policy file the way you treat a shell script.
357
+ - A selected command receives the task on standard input and inherits standard
358
+ output and error. Whatever it does with the task — including writing it
359
+ somewhere — is outside weightclass's control, and its exit status is its own.
360
+
361
+ ## Development verification
362
+
363
+ weightclass has no runtime dependencies. These development tools are not
364
+ required to use it, only to reproduce what CI checks:
365
+
366
+ ```sh
367
+ PYTHONPATH=src python3 -m unittest discover -s tests
368
+ PYTHONPATH=src python3 -m compileall -q src
369
+
370
+ python3 -m pip install ruff mypy build twine
371
+ ruff check src tests
372
+ ruff format --check src tests
373
+ mypy
374
+ python3 -m build && twine check dist/*
375
+ ```
376
+
377
+ ## License
378
+
379
+ [MIT](LICENSE)