infosave commited on
Commit
2df7d09
·
verified ·
1 Parent(s): 922a168

0.8.6: auto-skills (API.md 3.2/3.3/6, ORACLE.md Auto-skills, Exploration)

Browse files
Files changed (2) hide show
  1. API.md +75 -6
  2. ORACLE.md +129 -3
API.md CHANGED
@@ -359,6 +359,56 @@ matter to the oracle alone). With L = the option ids of a choice question:
359
  | `superset` | one skill's labels plus labels it does not know | the oracle only; its answer teaches that skill only for a key with `learning_allowed` |
360
  | `untrained` | anything else, and every `score` / `noul` question | the oracle only; without it the request is 422 |
361
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
362
  `cmf.skill` names the skill and skips the search (an unknown id is 400). An
363
  answer is `certified: true` only for an exact match on a string `state`, the
364
  `balanced` or `quality-first` profile, a skill whose gate is certified, and a
@@ -408,7 +458,7 @@ curl -s "$CORTIQ/v1/skills/banking77" -H "Authorization: Bearer $KEY" \
408
  | `source`, `skill`, `match`, `certified` | where the answer came from; section 3.2 |
409
  | `gate` | `accepted`, `p_top` and `tau`, `novelty` and `theta`, `is_novel`, `margin`, `profile` |
410
  | `errors` | reconstruction errors of the 5 best labels (all of them with `cmf.explain`) |
411
- | `flags` | e.g. `oracle_disabled`, `no_key` (with `oracle_disabled`: the key variable is not set), `bad_key` (with `oracle_disabled`: it holds something that is not a key), `consent_off`, `budget`, `stopped`, `oracle_unavailable`, `pii_redacted` |
412
  | `confident` | the answer can be used as is (gate accepted and not novel, or a valid oracle answer) |
413
  | `complexity` | `{score, tier, factors: {base, ambiguity, novelty, margin, length}}`, the cortiq-router formula |
414
  | `routing` | `{target, reason}` when `routing_tiers` maps the tier |
@@ -530,10 +580,10 @@ curl -s "$CORTIQ/v1/feedback" -H "Authorization: Bearer $KEY" \
530
  | Path | Access | Returns |
531
  |---|---|---|
532
  | `GET /v1/models` | open | the model in the shape of an OpenRouter provider listing (`id`, `pricing` as USD-per-token strings, `context_length` 512, `max_output_length` 255) plus `cmf.skills` with each gate |
533
- | `GET /v1/skills` | key | every skill: `id`, `taxonomy_version`, `labels`, `certified`, `tau`, `theta`, `temperature` and `has_rubric` (whether it has a rubric; the rubric itself is per skill) |
534
  | `GET /v1/skills/{id}` | key | the same plus `tasks`, the whole `gate` and `rubric` (`instructions`, `criteria`; `null` without one) |
535
  | `GET /v1/usage` | key | the caller's account (router format; `x-cmf-extensions: 1` adds token and cost totals) |
536
- | `GET /healthz` | open | status, model, generation, skills, `oracle` (on/off) and `oracle_status` (`ready`, `no_key`, `bad_key`, `disabled`, `budget_exhausted`, `budget_too_small`, `stopped: <reason>`; section 5) |
537
 
538
  Without `--jev-compatible`, `/v1/models` has only the native OpenRouter-style
539
  listing shape; Cortiq Decision is not listed on OpenRouter. With the adapter
@@ -762,7 +812,7 @@ unset the admin API answers 404 `ADMIN_DISABLED`.
762
  | `POST / GET /v1/admin/keys`, `DELETE /v1/admin/keys/{account}`, `DELETE /v1/admin/keys/hash/{hash12}` | create (raw key returned once), list, revoke |
763
  | `GET /v1/admin/usage` | usage of every account |
764
  | `GET /v1/admin/oracle`, `POST /v1/admin/oracle {"enabled", "budget_usd", "max_calls"}` | oracle status: `status` (`ready`; `no_key` — the key variable is unset or empty; `bad_key` — it holds something that is not a key, never sent; `disabled` — not configured or switched off by the admin; `budget_exhausted` — something was spent and the budget (what is left cannot hold the smallest possible call, or a call it refused) or `max_calls` is used up; `budget_too_small` — nothing spent, and the budget cannot hold one call, `min_call_usd`, or `max_calls` is 0; `stopped: <reason>` — a stop rule), `configured` = `oracle.enabled` of the configuration, `enabled` = not switched off by a stop rule or the admin, `key_present`, `key_ok`, `key_problem` (by position and length, never a byte of the key), `key_trimmed` (surrounding whitespace was trimmed), `key_env` (the variable's name, never its value), `model`, `max_price`, `min_call_usd` (the least budget a call needs: the smallest possible call's reservation, or, once the budget refused a longer call — a budget below the smallest call included — that call's, the larger), spent, calls, stop reason, `last_error` (the code of the last failed call; both from a closed code set, another value read back from `oracle.state` is `unknown_code`); switch it and lower limits within the configuration (kept in `oracle.state` across restarts: when one of them refuses the next call, `cmf.hint` and the startup line name it and the `POST /v1/admin/oracle` that lifts it) |
765
- | `GET /v1/admin/learning` | buffer, cache, quarantine, attempts, promotions, recent events |
766
  | `GET /v1/admin/generations`, `POST /v1/admin/rollback {"generation": N}` | generations; serve generation N (0 = the base file) |
767
  | `GET /v1/admin/shadow` | agreement statistics in shadow mode, and the routed requests not compared since the start (section 8) |
768
 
@@ -803,7 +853,11 @@ unknown key is an error. The defaults:
803
  "max_tokens_per_question": 64, "deadline_s": 30, "budget_usd": 1.0, "max_calls": 10000,
804
  "max_errors": 30, "redact_pii": true, "title": "cortiq-decision", "data_collection": null},
805
  "cache": {"enabled": true, "threshold": 0.97, "cap": 50000},
806
- "learning": {"enabled": true, "refit_min_new": 25, "dedup": 0.995, "cold_start": true, "synchronous": false},
 
 
 
 
807
  "feedback": {"pending_cap": 50000},
808
  "complexity_weights": {"base": 0.4, "ambiguity": 0.25, "novelty": 0.15, "margin": 0.1, "length": 0.1},
809
  "complexity_tiers": [{"tier": "low", "max": 0.33}, {"tier": "medium", "max": 0.66}, {"tier": "high", "max": 1.0}],
@@ -817,7 +871,22 @@ part of the file: the admin token and the OpenRouter key are read from the
817
  environment variables it names. `auth.plans` may override the plan table.
818
  `oracle.base_url` must be https, or plain http to a loopback address only (a
819
  local proxy). The oracle, cache and learning sections are explained in
820
- [ORACLE.md](ORACLE.md). `cortiq serve --oracle MODEL` and its companions
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
821
  override the `oracle` section (and `--no-oracle-learning` sets
822
  `learning.enabled` false); a file that sets `oracle.provider` keeps its
823
  `max_price` unless `--oracle-max-price` is given.
 
359
  | `superset` | one skill's labels plus labels it does not know | the oracle only; its answer teaches that skill only for a key with `learning_allowed` |
360
  | `untrained` | anything else, and every `score` / `noul` question | the oracle only; without it the request is 422 |
361
 
362
+ **Auto-skills (0.8.6).** A choice question no skill fits (`untrained` because
363
+ of its options, not an ambiguous one and not one forced with `cmf.skill`) is
364
+ learned from the oracle's answers when the caller's key has `learning_allowed`
365
+ and `learning.auto_skills` is on (the default). Its *contract* is the whole
366
+ question — `instructions` and `criteria` with their descriptions, not the id
367
+ set: the key is `auto-<12 hex of the sha256 of the canonical JSON of {type,
368
+ instructions, criteria}>` with the criteria keys sorted (so the criteria in
369
+ any order are one contract, while other instructions, a changed description,
370
+ a dropped or an added option are each another contract — `{yes, no}` under
371
+ "Is this spam?" and under "Is this urgent?" are two skills that never answer
372
+ each other's question, and positional ids `{A, B, C, D}` whose descriptions
373
+ change with every request are never learned as one). The answers accumulate
374
+ as examples of that skill, and once enough evidence is there (2 labels with ≥
375
+ `auto_min_rows` fit rows, the active labels holding ≥ `auto_min_coverage` of
376
+ the examples, a macro agreement with the oracle on a held-out fifth of the
377
+ rows ≥ `auto_min_agreement`) the skill is fitted, written as a generation and
378
+ served: the same contract is then `exact`, `action: local`, `skill: auto-…`,
379
+ always `certified: false` (`decision_path` `router:uncertified`). An
380
+ auto-skill is matched by its contract alone — never as a `subset` or
381
+ `superset` — so to be learned and then answered locally a client keeps the
382
+ question stable: the same `instructions` text, the same option ids with the
383
+ same descriptions (`null` descriptions, and absent instructions, count as
384
+ such and stay stable). Labels the oracle rarely picks stay *quarantined*: they
385
+ count as known within the contract, get probability 0, and a text of such a
386
+ label is expected to abstain on novelty and keep teaching it. A
387
+ young auto-skill's gate comes from a handful of rows, so a local answer also
388
+ needs `p_top ≥ learning.auto_tau` (0.90; `balanced` and `quality-first`) and
389
+ its temperature is floored at `learning.auto_temperature_min` (0.02 — a clean
390
+ calibration subset fits T at its lower bound, where every `p_top` is 1);
391
+ whatever abstains escalates and teaches, and later attempts refit the whole
392
+ skill under a regression gate. *Exploration* is the one exception to the hard
393
+ rule that a gate-accepted question never reaches the oracle: while a label of
394
+ an auto-skill is quarantined, a question the gate accepted is escalated anyway
395
+ when the text's hash says so (`u64le(sha256(φ_P))` ≡ 0 modulo
396
+ `learning.auto_explore_every`, 8 — one text in eight; 0 turns it off), because
397
+ the gate confidently names a quarantined label's texts as a neighbour and they
398
+ would otherwise never teach it. The oracle's answer is served (`action:
399
+ oracle` / `cache`, `decision_path` `escalate→…`, the flag `explore`, the
400
+ `gate` block still `accepted: true`) and learned as usual, so the rare label
401
+ collects examples at a quarter of its traffic until it activates; a refused
402
+ or failed call leaves the local answer. Only auto-skills explore, only while
403
+ a label is quarantined, and only when the answer could teach (the oracle
404
+ consented, learning on, a key with `learning_allowed`); `cortiq decide`,
405
+ shadow mode and `/v1/route` never do. Its rubric is the contract — the first
406
+ caller's instructions and criteria verbatim — visible through
407
+ `/v1/skills/{id}` to every key (as cached answers are shared across accounts).
408
+ Limits, who teaches and the state directory's compatibility:
409
+ [ORACLE.md](ORACLE.md#auto-skills). When a data skill and auto-skills fit a
410
+ question equally, the data skill answers.
411
+
412
  `cmf.skill` names the skill and skips the search (an unknown id is 400). An
413
  answer is `certified: true` only for an exact match on a string `state`, the
414
  `balanced` or `quality-first` profile, a skill whose gate is certified, and a
 
458
  | `source`, `skill`, `match`, `certified` | where the answer came from; section 3.2 |
459
  | `gate` | `accepted`, `p_top` and `tau`, `novelty` and `theta`, `is_novel`, `margin`, `profile` |
460
  | `errors` | reconstruction errors of the 5 best labels (all of them with `cmf.explain`) |
461
+ | `flags` | e.g. `oracle_disabled`, `no_key` (with `oracle_disabled`: the key variable is not set), `bad_key` (with `oracle_disabled`: it holds something that is not a key), `consent_off`, `budget`, `stopped`, `oracle_unavailable`, `pii_redacted`, `explore` (a gate-accepted question of an auto-skill answered by the oracle for exploration, section 3.2) |
462
  | `confident` | the answer can be used as is (gate accepted and not novel, or a valid oracle answer) |
463
  | `complexity` | `{score, tier, factors: {base, ambiguity, novelty, margin, length}}`, the cortiq-router formula |
464
  | `routing` | `{target, reason}` when `routing_tiers` maps the tier |
 
580
  | Path | Access | Returns |
581
  |---|---|---|
582
  | `GET /v1/models` | open | the model in the shape of an OpenRouter provider listing (`id`, `pricing` as USD-per-token strings, `context_length` 512, `max_output_length` 255) plus `cmf.skills` with each gate |
583
+ | `GET /v1/skills` | key | every skill: `id`, `taxonomy_version`, `labels` (the active ones), `certified`, `tau`, `theta`, `temperature`, `has_rubric` (whether it has a rubric; the rubric itself is per skill) and, since 0.8.6, `auto` (an auto-skill), `active_labels` (their count), `quarantined_labels` (the labels not scored yet) and `examples` (learned rows); auto-skills are listed after the file's skills |
584
  | `GET /v1/skills/{id}` | key | the same plus `tasks`, the whole `gate` and `rubric` (`instructions`, `criteria`; `null` without one) |
585
  | `GET /v1/usage` | key | the caller's account (router format; `x-cmf-extensions: 1` adds token and cost totals) |
586
+ | `GET /healthz` | open | status, model, generation, skills (every served skill), `auto_skills` (0.8.6), `oracle` (on/off) and `oracle_status` (`ready`, `no_key`, `bad_key`, `disabled`, `budget_exhausted`, `budget_too_small`, `stopped: <reason>`; section 5) |
587
 
588
  Without `--jev-compatible`, `/v1/models` has only the native OpenRouter-style
589
  listing shape; Cortiq Decision is not listed on OpenRouter. With the adapter
 
812
  | `POST / GET /v1/admin/keys`, `DELETE /v1/admin/keys/{account}`, `DELETE /v1/admin/keys/hash/{hash12}` | create (raw key returned once), list, revoke |
813
  | `GET /v1/admin/usage` | usage of every account |
814
  | `GET /v1/admin/oracle`, `POST /v1/admin/oracle {"enabled", "budget_usd", "max_calls"}` | oracle status: `status` (`ready`; `no_key` — the key variable is unset or empty; `bad_key` — it holds something that is not a key, never sent; `disabled` — not configured or switched off by the admin; `budget_exhausted` — something was spent and the budget (what is left cannot hold the smallest possible call, or a call it refused) or `max_calls` is used up; `budget_too_small` — nothing spent, and the budget cannot hold one call, `min_call_usd`, or `max_calls` is 0; `stopped: <reason>` — a stop rule), `configured` = `oracle.enabled` of the configuration, `enabled` = not switched off by a stop rule or the admin, `key_present`, `key_ok`, `key_problem` (by position and length, never a byte of the key), `key_trimmed` (surrounding whitespace was trimmed), `key_env` (the variable's name, never its value), `model`, `max_price`, `min_call_usd` (the least budget a call needs: the smallest possible call's reservation, or, once the budget refused a longer call — a budget below the smallest call included — that call's, the larger), spent, calls, stop reason, `last_error` (the code of the last failed call; both from a closed code set, another value read back from `oracle.state` is `unknown_code`); switch it and lower limits within the configuration (kept in `oracle.state` across restarts: when one of them refuses the next call, `cmf.hint` and the startup line name it and the `POST /v1/admin/oracle` that lifts it) |
815
+ | `GET /v1/admin/learning` | buffer, cache, quarantine, attempts, promotions, recent events; since 0.8.6 `auto_contracts`, `auto_skipped` and `auto_skills` (per contract: `id`, `labels`, `examples` per label — the rows an attempt fits: the served learned rows plus the pending buffer examples, each row once — `created_unix`, `served`) |
816
  | `GET /v1/admin/generations`, `POST /v1/admin/rollback {"generation": N}` | generations; serve generation N (0 = the base file) |
817
  | `GET /v1/admin/shadow` | agreement statistics in shadow mode, and the routed requests not compared since the start (section 8) |
818
 
 
853
  "max_tokens_per_question": 64, "deadline_s": 30, "budget_usd": 1.0, "max_calls": 10000,
854
  "max_errors": 30, "redact_pii": true, "title": "cortiq-decision", "data_collection": null},
855
  "cache": {"enabled": true, "threshold": 0.97, "cap": 50000},
856
+ "learning": {"enabled": true, "refit_min_new": 25, "dedup": 0.995, "cold_start": true, "synchronous": false,
857
+ "auto_skills": true, "auto_min_rows": 10, "auto_k": 8, "auto_tau": 0.9,
858
+ "auto_min_agreement": 0.8, "auto_min_coverage": 0.8, "auto_max_skills": 256,
859
+ "auto_max_labels": 64, "auto_max_examples_per_label": 1000,
860
+ "auto_temperature_min": 0.02, "auto_explore_every": 8},
861
  "feedback": {"pending_cap": 50000},
862
  "complexity_weights": {"base": 0.4, "ambiguity": 0.25, "novelty": 0.15, "margin": 0.1, "length": 0.1},
863
  "complexity_tiers": [{"tier": "low", "max": 0.33}, {"tier": "medium", "max": 0.66}, {"tier": "high", "max": 1.0}],
 
871
  environment variables it names. `auth.plans` may override the plan table.
872
  `oracle.base_url` must be https, or plain http to a loopback address only (a
873
  local proxy). The oracle, cache and learning sections are explained in
874
+ [ORACLE.md](ORACLE.md). The `learning.auto_*` keys govern auto-skills
875
+ (section 3.2, [ORACLE.md](ORACLE.md#auto-skills)): `auto_skills` learns
876
+ untrained choice contracts at all; `auto_min_rows` fit rows (≥ 2) a label
877
+ needs to be active; `auto_k` the rank of its topologies; `auto_tau` the
878
+ confidence floor of a local answer (0..1; a serving parameter — `cortiq
879
+ decide` uses the default); `auto_min_agreement` the macro agreement with the
880
+ oracle required to activate and `auto_min_coverage` the share of the
881
+ contract's examples its active labels must hold (both 0..1); `auto_max_skills`
882
+ how many contracts are learned at most and `auto_max_labels` (2..255) how
883
+ many options one may have — a contract past either is answered by the
884
+ oracle and not learned; `auto_max_examples_per_label` (≤ 5000) replaces the
885
+ per-label buffer cap for auto-skills; `auto_temperature_min` (0..1) floors
886
+ the gate temperature an attempt records (0: the fitted T, whose lower bound
887
+ makes every `p_top` 1); `auto_explore_every` (an integer, 0 = off) explores
888
+ one text in that many while a label is quarantined — the one case in which a
889
+ gate-accepted question reaches the oracle, auto-skills only. `cortiq serve --oracle MODEL` and its companions
890
  override the `oracle` section (and `--no-oracle-learning` sets
891
  `learning.enabled` false); a file that sets `oracle.provider` keeps its
892
  `max_price` unless `--oracle-max-price` is given.
ORACLE.md CHANGED
@@ -72,7 +72,9 @@ says first whether it is ready — [Check your setup](#check-your-setup)).
72
 
73
  Superset questions (a skill's labels plus new ones) are decided by the oracle
74
  only; for a caller with `learning_allowed` the answer teaches that skill, and
75
- a new label starts a cold start.
 
 
76
 
77
  ## Self-learning
78
 
@@ -127,7 +129,130 @@ a new label starts a cold start.
127
  every label that got new examples, however few (there is no threshold of
128
  25 here), checks each challenger on the holdout and re-certifies the gate
129
  once at the end; if a certified gate would be lost, every promotion of the
130
- skill is undone (see API.md, section 7).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
  ## Budget and stop rules
133
 
@@ -535,7 +660,8 @@ warning: removed the LOCK of state directory cortiq-decision.cmf.state left by p
535
  (`oracle_allowed: true`, like imported router keys); `--oracle-allowed=false`
536
  makes one that never escalates, and `--oracle-budget-usd` caps one key's
537
  oracle spending. Teaching the shared skills is a separate permission,
538
- `--learning-allowed` (off by default). Keys made through `POST
 
539
  /v1/admin/keys` keep the router's default (`oracle_allowed` only when the
540
  body says so).
541
 
 
72
 
73
  Superset questions (a skill's labels plus new ones) are decided by the oracle
74
  only; for a caller with `learning_allowed` the answer teaches that skill, and
75
+ a new label starts a cold start. A choice question no skill fits is learned
76
+ into an *auto-skill* of its own ([Auto-skills](#auto-skills)), for such a
77
+ caller only.
78
 
79
  ## Self-learning
80
 
 
129
  every label that got new examples, however few (there is no threshold of
130
  25 here), checks each challenger on the holdout and re-certifies the gate
131
  once at the end; if a certified gate would be lost, every promotion of the
132
+ skill is undone (see API.md, section 7). An auto-skill (below) learns
133
+ online only.
134
+
135
+ ### Auto-skills
136
+
137
+ A choice question whose option ids match no skill (`match: untrained`, not
138
+ an ambiguous one — several skills fit — and not one forced with `cmf.skill`)
139
+ goes to the oracle on every new text and, until 0.8.5, was never learned. Since
140
+ 0.8.6 its *contract* becomes a skill of its own:
141
+
142
+ * **Key.** The contract is the question itself: `auto-` + the first 12 hex
143
+ characters of the sha256 of the canonical JSON of `{type, instructions,
144
+ criteria}` — the instructions as sent (`null` when absent), the criteria
145
+ object with its keys sorted and every description kept. The criteria in
146
+ any order are one contract; other instructions, a changed description, a
147
+ dropped or an added option are each another contract, learned separately.
148
+ The id set alone is not the key: two clients using `{yes, no}` under
149
+ different questions get two skills, and a multiple-choice benchmark whose
150
+ positional ids `{A, B, C, D}` carry a new description per request is never
151
+ learned as one skill (every request is its own contract, bounded by
152
+ `auto_max_skills`). To be learned, a client keeps its question stable —
153
+ the same instructions text, the same ids with the same descriptions. The
154
+ first request's instructions and criteria are stored verbatim as the
155
+ skill's rubric (the id is recomputed from it), visible through
156
+ `/v1/skills/{id}` to every key. The contract is written to `learn.log`
157
+ before its first example, so a restart rebuilds it.
158
+ * **Who teaches.** Only a caller whose key has `learning_allowed` (the explicit
159
+ open mode `auth.require: false` has it; the implicit open mode of a loopback
160
+ address never teaches). The rule "a question that is exactly the skill's own
161
+ teaches it" does not apply to auto-skills: their rubric *is* the contract
162
+ every caller sends. `learning.auto_skills: false` turns the learning off;
163
+ the oracle answers as before.
164
+ * **Examples.** Each answer is an example of the auto-skill under the
165
+ contract's label (the dedup of 0.995 applies). The labels are closed: a
166
+ feedback with a label outside the contract is refused (a request with one
167
+ more id is another contract, learned on its own).
168
+ At most `learning.auto_max_examples_per_label` (1000) examples per label.
169
+ * **Activation.** When a label collects 25 new examples (the usual trigger)
170
+ the whole skill is attempted: one row in five — by a hash of the row itself,
171
+ so a row never moves between the fit and the calibration subset as more
172
+ arrive — is held out; a label with ≥ `auto_min_rows` (10) fit rows is
173
+ *eligible* and fitted (rank `auto_k`, 8), the others stay quarantined; the
174
+ attempt needs two eligible labels including the triggering one and, the
175
+ first time, the eligible labels must hold ≥ `auto_min_coverage` (0.8) of
176
+ the contract's rows. T and θ are certified on the held-out rows as at build
177
+ time (Ï„ certifies only with 100 accepted odd rows, so the skill reports
178
+ `certified: false` for a long time, and its winners are `cold_start` ones —
179
+ never certified). The first activation (`auto_start`) requires a macro
180
+ agreement of the fitted skill with the oracle's labels on the held-out rows
181
+ ≥ `auto_min_agreement` (0.8) and ≥ 0.5 per label; a later attempt
182
+ (`auto_refit`, the whole skill refitted) must not regress the champion on
183
+ the same rows (the holdout rule). A promotion writes a generation that
184
+ carries the auto-skill entirely; every other skill is byte for byte the
185
+ same.
186
+ * **Serving.** The contract is then decided locally, `match: exact`,
187
+ `skill: auto-…`, `certified: false`. An auto-skill is matched by its
188
+ contract alone: a part of its ids, a superset, other instructions or another
189
+ description is another contract (`untrained`, learned on its own), never a
190
+ `subset` or `superset` of the auto-skill. A quarantined label still counts
191
+ as known within the contract: it gets probability 0, and a text of it is
192
+ expected to abstain on novelty and keep teaching it. Because θ and T of a
193
+ young auto-skill come from a handful of rows (θ is close to the largest
194
+ novelty of the even half, T may sit at a bound), a local answer also needs
195
+ `p_top ≥ learning.auto_tau` (0.9) under `balanced` and `quality-first`
196
+ (`cost-saver` keeps its θ-only rule), and the T an attempt records is
197
+ floored at `learning.auto_temperature_min` (0.02): a clean held-out subset
198
+ fits T at its lower bound, where every `p_top` is 1 and the `auto_tau`
199
+ floor never bites. An abstention escalates and teaches as any other. The
200
+ agreement gate is the real guard while the subset is small; the gate
201
+ improves with every refit. Rejected examples are never forgotten: a
202
+ contract polluted by a noisy oracle needs more consistent examples, not a
203
+ reset (or a rollback to a generation before it).
204
+ * **Exploration.** The hard rule — a question the gate accepted never
205
+ reaches the oracle — has exactly one exception, for auto-skills only and
206
+ only while one of their labels is quarantined: the gate names such a
207
+ label's texts as a neighbour with full confidence (measured on a stand: all
208
+ 21 texts of a quarantined label answered locally as another label at
209
+ `p_top` 1), so without it the label could never learn. One text in
210
+ `learning.auto_explore_every` (8; `u64le(sha256(φ_P))` modulo it is 0, a
211
+ property of the text, never of the order; 0 turns it off) is escalated
212
+ although accepted: the oracle's answer is served (`action: oracle` /
213
+ `cache`, the flag `explore`, the `gate` block still reports `accepted:
214
+ true`) and learned as usual, so the rare label collects examples at a
215
+ quarter of its traffic until the next attempt activates it, after which
216
+ the contract explores no more. A refused or failed call leaves the local
217
+ answer. Exploration draws only when the answer could teach: the oracle
218
+ consented, learning and `auto_skills` on, a key with `learning_allowed`;
219
+ data skills, `cortiq decide`, shadow mode and `/v1/route` never explore.
220
+ * **Limits.** A contract is learned only with 2..`auto_max_labels` (64) option
221
+ ids and while fewer than `auto_max_skills` (256) contracts are registered;
222
+ otherwise the oracle answers and nothing is recorded (`auto_skipped` in
223
+ `GET /v1/admin/learning`, one warning an hour, never the ids). Every
224
+ generation re-carries every auto-skill's rows: with many contracts run
225
+ `cortiq decision materialize` from time to time and serve the materialised
226
+ file (it holds the auto-skills as ordinary skills; `decide --labels` and
227
+ `verify` read it; such a file needs cortiq ≥ 0.8.6). A served auto-skill
228
+ keeps learning wherever its contract comes from: the contract record of
229
+ `learn.log`, or — a materialised file on a fresh state directory, a
230
+ `learn.log` lost while the generations were kept — the skill's own labels
231
+ and rubric, registered (and written to `learn.log`) at start and after a
232
+ rollback.
233
+ * **Routing.** `/v1/route` without `taxonomy_id` still means the file's only
234
+ data skill; an auto-skill is routed by its id (its question is then the
235
+ whole contract from the rubric). `cortiq decide --labels` never names an
236
+ auto-skill by labels alone: `--skill auto-…` names it and its rubric
237
+ supplies the contract. When a data skill and auto-skills fit a question
238
+ equally, the data skill answers; two auto-skills of one contract cannot
239
+ exist (the contract is the id).
240
+ * **Admin.** `GET /v1/admin/learning` lists `auto_skills` (id, labels,
241
+ `examples` per label — the rows the next attempt fits: the served learned
242
+ rows of the label plus the buffer examples not among them, each row once,
243
+ so their sum is the attempt's `rows.total` — what is served),
244
+ `auto_contracts` and `auto_skipped`;
245
+ attempts carry `kind: auto_start | auto_refit` and an `auto` block with the
246
+ eligible and quarantined labels, the rows and the agreement. `/healthz`
247
+ adds `auto_skills`. Rollback to an earlier generation drops the skill from
248
+ the served model (the contract is untrained again; its examples stay and
249
+ start it over at the next trigger).
250
+ * **Compatibility.** A 0.8.5 binary opening a 0.8.6 state directory truncates
251
+ `learn.log` at the first contract record and refuses a generation that
252
+ carries an auto-skill (the same rule as the `LOCK` of 0.7.9: never run an
253
+ older binary on a newer state directory). The cache scope of a contract
254
+ changes from the contract to the skill at its activation, so its first
255
+ requests after that miss the cache (they are answered locally anyway).
256
 
257
  ## Budget and stop rules
258
 
 
660
  (`oracle_allowed: true`, like imported router keys); `--oracle-allowed=false`
661
  makes one that never escalates, and `--oracle-budget-usd` caps one key's
662
  oracle spending. Teaching the shared skills is a separate permission,
663
+ `--learning-allowed` (off by default); it alone lets a caller's untrained
664
+ contracts become [auto-skills](#auto-skills). Keys made through `POST
665
  /v1/admin/keys` keep the router's default (`oracle_allowed` only when the
666
  body says so).
667