Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,16 +169,23 @@ catalog when the inventory started (`catalogRevision`), the list the inventory p
models from — the inventory's OMP version, inventory and benchmark times, the account
providers and a digest of the exact account pool. A catalog republished while the
inventory runs stops the verification before its charge is answered. A catalog stops
being verified when the selected destination's inventory operation is installed from
another artifact (`describeDestination`'s pins), OMP bundles a catalog with another
revision, or the saved choices select another pool. Because the identities are content
digests, an OMP upgrade, its SDK's or its model list's, is seen whatever version it
reports and without a new Code build; no version string is compared. A catalog recorded
before verification carried these identities reads as unverified. An identity not yet
observed, or whose read failed, claims no change: the destination's failure keeps the
launch refused, a failed model list read is named in the launch line with Retry and
Models, and the session door re-checks that every routed model is still published. A
current verification can still be renewed: Verify needs only its own preconditions
being verified when the saved choices select another pool, or when OMP's bundled catalog
has moved to another revision that no longer publishes a model the profile routes to,
leads and fallbacks; the launch line then says OMP no longer lists a model this profile
uses, and Models names it, or counts several. An OMP update alone leaves the verification current: the
selected destination's inventory operation installed from another artifact
(`describeDestination`'s pins), or another catalog revision that still publishes every
routed model. What OMP publishes is read only once its catalog has moved, since the
revision a verification recorded publishes every model it laddered. Because the identities
are content digests, an update is seen whatever version OMP reports and without a new
Code build, and Models says it beside the verification's date; no version string is
compared. A catalog recorded before verification carried these identities reads as
unverified. An identity not yet observed, or whose read failed, claims no change: the
destination's failure keeps the launch refused, a failed model list read is named in the
launch line with Retry and Models, and a failed read of what a moved catalog publishes names
nothing and leaves a dropped model to the session door, which re-checks that every routed
model is still published (`code_model_unpublished`). A current verification can still be
renewed: Verify needs only its own preconditions
(write access, the machine, readable accounts that include one, discovery and benchmark
readiness), and its charge still waits on Confirm. A model that draws a
quota of its own (any OMP class but chat, Spark's for one, as each inventory row states) is
Expand Down Expand Up @@ -763,9 +770,11 @@ one the family offers names the rung the model row lands on instead, while one a
family's top (an elite it lacks) says only that it has no model, as the model row refuses
it. A family Code requires that lacks a rung of its own at fast, normal or smart reads
**· short** beside its name. The head says
how many models are in use and when they were verified, or that a staged list waits
beside them, that they are verifying, or that they are unverified or the bundled
starter, with **import** (`i`) and **export** (`x`) as two quiet words. Beside the list
how many models are in use and when they were verified, with **OMP updated since** when
OMP's runtime or catalog moved after that, or that accounts changed or OMP dropped a routed
model since they were verified, that a staged list waits beside them, that they are
verifying, or that they are unverified or the bundled starter, with **import**
(`i`) and **export** (`x`) as two quiet words. Beside the list
is what the step is about: the charge per provider and the models not probed while
verifying, a staged list's changes (field by field, a model added or dropped, the old
value struck), or, after a verification in this panel, the models it left out, each
Expand Down
16 changes: 10 additions & 6 deletions docs/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,16 @@ files, service state, credentials or backups.
`quotaTier`, which the derivation reads directly. OMP's bundled catalog is stamped
with its separately pinned SDK's version, now also 18.8.6. No version string decides
whether a verification holds: it records the artifact its inventory ran from and the bundled
catalog's revision, and goes stale when the destination pins another artifact or OMP
bundles another revision, with no Code rebuild. A verification recorded before this
change, without those identities, reads as unverified and asks to be verified again,
and a current one can be renewed on purpose. Unit tests and the disposable browser
prove this against a synthetic destination and the real bundled catalog; no
installed OMP upgrade has been observed. This source repin is not a release, preview
catalog's revision. An OMP update alone, another artifact or another catalog revision that
still publishes every routed model, leaves it current and is noted in Models; it goes stale
when a moved catalog no longer publishes a routed model, or the saved choices select another
pool, with no Code rebuild. A verification recorded before these identities, without them,
reads as unverified and asks to be verified again, and a current one can be renewed on
purpose. Unit tests prove the rule against a synthetic destination and catalog, and the
disposable browser against the real bundled catalog: it moves a verified list's runtime
artifact, then serves a synthetic republish of that catalog without the list's default lead.
Applied to the preview workspace's live reads (destination pins, catalog revision and account
pool), the rule gives current: only the runtime artifact moved. This source repin is not a release, preview
installation, native deployment review or provider observation.
The one-shot-only `agentTools: { runId }` selector carries existing native
authority; it does not grant, infer or acknowledge it. Omission stays unbound.
Expand Down
12 changes: 6 additions & 6 deletions plugins/code/generator/launch-step.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ const blockerAction: Readonly<Partial<Record<LaunchBlockerCode, LaunchBlockerAct
const verifyText: Readonly<Record<Exclude<VerificationStatus, "current" | "verifying">, string>> = {
unverified: "Verify models with your accounts before saving or launching.",
"accounts-changed": "Accounts changed since models were verified. Verify again.",
"omp-changed": "OMP changed since models were verified. Verify again.",
unpublished: "OMP no longer lists a model this profile uses. Verify again.",
};
function blockerReason(code: LaunchBlockerCode): LaunchBlocker {
const action = blockerAction[code];
Expand Down Expand Up @@ -215,11 +215,11 @@ export function saveGate(facts: LaunchFacts): LaunchBlockerCode | null {
* review → skill problems → `launch` when the review is current, else `review`.
*
* Verification precedes saving and reviewing: whatever team is shown, its first save and every
* launch need a catalog verified against the present accounts and OMP. While it is not current
* the step is `verify`, or the first failing verification precondition in its order (verifying →
* read-only → conflict → runtime unavailable → accounts unreadable or none included →
* destination status unavailable → discovery/benchmark permission). Below that the precedence is
* unchanged.
* launch need a catalog verified with the present accounts, whose routed models OMP still
* publishes. While it is not current the step is `verify`, or the first failing verification
* precondition in its order (verifying → read-only → conflict → runtime unavailable → accounts
* unreadable or none included → destination status unavailable → discovery/benchmark
* permission). Below that the precedence is unchanged.
*
* The unsaved branch folds in the Save control: `save` when a save of the local edit may proceed
* (`saveGate`), otherwise the first failing Save precondition (read-only → conflict → models),
Expand Down
35 changes: 23 additions & 12 deletions plugins/code/generator/model-verification.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import type { HostServices } from "@manifold/plugin";
import { BENCHMARK_OPERATION_ID, INVENTORY_OPERATION_ID, type ActionResult as OmpResult } from "@atyrode/manifold-omp";
import type { Selection } from "../../domain/contracts.ts";
import { unpublishedModels, type ModelAddress } from "../../domain/routing.ts";
import type { Exclusion } from "../../domain/probe.ts";
import type { Configuration, Target, VerificationProvenance } from "../contract.ts";
import { VerificationError, WorkflowError, inventoryArtifact, type ChargeReview, type PendingVerification, type VerificationEvidence,
type VerificationProgress, type VerificationStep } from "../workflow.ts";
import { canWriteCodeWorkspace, codeOperationFailure, codeWorkflow, useWorkflowQuery } from "../machine-web.ts";
import { canWriteCodeWorkspace, codeOperationFailure, codeWorkflow, useOmpQuery, useWorkflowQuery } from "../machine-web.ts";
import { operationReady } from "../permission-plan.ts";
import { CHECKING_HOLD_MS, confirmsCharge, ownInitialization, verificationState, type ConfirmActivation, type VerificationState } from "./verification.ts";

Expand All @@ -20,6 +21,8 @@ export type ModelVerificationInput = {
configurationCurrent: boolean;
/** The selection the operator sees; a confirmed verification saves it, narrowed to what the verified catalog hosts. */
selection: Selection | null;
/** The models the shown team's routes name, leads and fallbacks; null while no team is formed. */
routed: readonly ModelAddress[] | null;
/** The destination as OMP describes it now; null until it answers. */
setup: OmpResult<"describeDestination"> | null;
/** The revision of the model catalog OMP bundles now (`readModelCatalog`); null until it answers. */
Expand Down Expand Up @@ -76,14 +79,14 @@ type Session = {

/**
* The first-use and re-verification flow of `createCodeWorkflowClient().verifyModels`, for the
* workbench. Mounting observes only — OMP's account observation and Code's pool composition, and
* the destination and model catalog the workbench already reads, to compare with the recorded
* verification — and nothing runs until `prepare`. A run is bound to the destination, principal,
* container and write authority it started under; losing any of them stops it, which cancels a
* probe job still spending.
* workbench. Mounting observes only — OMP's account observation and Code's pool composition, the
* destination and model catalog the workbench already reads, and, once that catalog has moved,
* what it publishes for the routed providers, to compare with the recorded verification — and
* nothing runs until `prepare`. A run is bound to the destination, principal, container and write
* authority it started under; losing any of them stops it, which cancels a probe job still spending.
*/
export function useModelVerification(input: ModelVerificationInput): ModelVerification {
const { host, target, record, revision, configurationCurrent, setup, catalogRevision, writable, available } = input;
const { host, target, record, revision, configurationCurrent, setup, catalogRevision, routed, writable, available } = input;
const latest = useRef(input);
latest.current = input;
// `pool: null` is an observed absence of any selected account; a null `data` is no observation yet.
Expand All @@ -100,11 +103,19 @@ export function useModelVerification(input: ModelVerificationInput): ModelVerifi
return () => { mounted.current = false; active.current?.controller.abort(); };
}, []);
const provenance = record?.active?.provenance ?? null;
// The OMP a verification recorded is compared by content: the artifact the destination's inventory
// operation is installed from and the bundled model catalog's revision, so an upgrade of either is
// seen whatever version it reports and without a new Code build.
// A routed model OMP's catalog no longer publishes is refused at launch. The revision the
// verification recorded publishes every model it laddered, so what OMP publishes is read only
// once the catalog has moved past it, and judged only when it answers for that same revision.
const moved = provenance !== null && catalogRevision !== null && catalogRevision !== provenance.catalogRevision;
const providers = useMemo(() => [...new Set((routed ?? []).map(model => model.provider))].sort(), [routed]);
const published = useOmpQuery(host, "readModelCatalog", moved && providers.length > 0 ? { providers } : null);
const unpublished = !moved || !routed?.length ? [] : published.data === null || published.data.revision !== catalogRevision ? null
: unpublishedModels(routed, published.data.models);
// The OMP a verification recorded is compared by content, the artifact the destination's inventory
// operation is installed from and the bundled model catalog's revision, so an update is seen
// whatever version it reports and without a new Code build.
const state = verificationState(provenance, { inventoryArtifactSha256: setup === null ? null : inventoryArtifact(setup), catalogRevision,
pool: pool.data === null ? null : pool.data.pool ?? "none" }, run !== null);
unpublished, pool: pool.data === null ? null : pool.data.pool ?? "none" }, run !== null);
const ready = operationReady(setup, INVENTORY_OPERATION_ID) && operationReady(setup, BENCHMARK_OPERATION_ID);
const canPrepare = run === null && writable && available && configurationCurrent && ready && target !== null && host.containerId !== null;

Expand Down
11 changes: 8 additions & 3 deletions plugins/code/generator/models-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,10 @@ export function ModelsSheet({ host, model, machine, onBack, backRef, onPlace }:
if (!verifyGate.open) return verifyRefused(verifyGate.refusal);
const stopped = verification.failure;
const note: readonly Part[] = stopped ? [[stopped.cancelled ? "verification cancelled" : "verification stopped", stopped.cancelled ? null : "attention"]] : [];
const tail: Part = verification.status === "accounts-changed" ? ["accounts changed", "warn"] : verification.status === "omp-changed" ? ["OMP changed", "warn"] : ["unverified", "warn"];
// A model OMP dropped is named as the list shows it; several are counted.
const dropped = verification.changes.unpublished, named = inUse.find(row => `${row.provider}/${row.id}` === dropped[0])?.alias ?? dropped[0];
const tail: Part = verification.status === "accounts-changed" ? ["accounts changed", "warn"]
: verification.status === "unpublished" ? [`OMP dropped ${dropped.length === 1 ? named : count(dropped.length, "model")}`, "warn"] : ["unverified", "warn"];
return { label: "verify models", parts: [...note, ...refused, tail], fixes: [],
said: stopped && !stopped.cancelled ? { value: "verify models", text: stopped.reason, warn: true }
: { value: "verify models", text: "finds the models your accounts reach and measures them; nothing is spent before you confirm" },
Expand Down Expand Up @@ -409,10 +412,12 @@ export function ModelsSheet({ host, model, machine, onBack, backRef, onPlace }:

// ------------------------------------------------------------ the sheet
const verifiedAt = verification.provenance?.benchmarkCompletedAt ?? null;
// An OMP update the routes survived leaves the list verified; it is said beside the date, since verifying again is the way to measure under it.
const ompUpdated = verification.changes.runtimeChanged || verification.changes.catalogChanged;
const [tail, tone]: Part = running ? ["verifying", null] : phase === "staged" ? [active ? "staged list beside it" : "staged, none in use", "warn"]
: verification.status === "current" && verifiedAt !== null ? [`verified ${since(Date.now() - verifiedAt)}`, null]
: verification.status === "current" && verifiedAt !== null ? [`verified ${since(Date.now() - verifiedAt)}${ompUpdated ? " · OMP updated since" : ""}`, null]
: verification.status === "accounts-changed" ? ["accounts changed since verified", "warn"]
: verification.status === "omp-changed" ? ["OMP changed since verified", "warn"]
: verification.status === "unpublished" ? [`OMP dropped ${count(verification.changes.unpublished.length, "model")} since verified`, "warn"]
: !active ? ["starter · unverified", "warn"] : ["unverified", "warn"];
function cell(family: string, tier: number) {
const row = at(family, tier), id = `${family}:${tier}`, tierWord = TIERS[tier - 1]!;
Expand Down
4 changes: 2 additions & 2 deletions plugins/code/generator/sheets-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ export function exclusionWords(exclusion: Exclusion): readonly [string, string]
/**
* The step the Models sheet is at, which decides its one next action. A run in flight comes first
* (`finishing` once its measurements are in), then a staged list waiting beside the one in use, a
* list verified with the present accounts and OMP, and otherwise a list to verify: `refused` when
* verifying needs discovery the machine has not enabled.
* list whose verification holds (verification.ts `current`), and otherwise a list to verify:
* `refused` when verifying needs discovery the machine has not enabled.
*/
export type ModelsPhase = "inventory" | "charge" | "benchmark" | "finishing" | "staged" | "verified" | "refused" | "unverified";
export function modelsPhase(facts: {
Expand Down
Loading
Loading