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
25 changes: 25 additions & 0 deletions proto/trogonai/policy/model_ceilings/state/v1alpha1/state.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
edition = "2024";

package trogonai.policy.model_ceilings.state.v1alpha1;

import "buf/validate/validate.proto";
import "google/protobuf/empty.proto";
import "trogonai/policy/model_ceilings/v1alpha1/ceiling_policy.proto";
import "trogonai/policy/subjects/v1alpha1/subject.proto";

// Policy fold only. Replay rejects unknown events and a subject change. Clear
// retains the history.
message State {
oneof state {
option (buf.validate.oneof).required = true;

google.protobuf.Empty missing = 1;
ModelCeilingHistory history = 2;
}
}

message ModelCeilingHistory {
trogonai.policy.subjects.v1alpha1.PolicySubject subject = 1 [features.field_presence = LEGACY_REQUIRED];
// Absence means cleared, not a zero cap.
trogonai.policy.model_ceilings.v1alpha1.ModelCeilingPolicy policy = 2;
}
41 changes: 41 additions & 0 deletions proto/trogonai/policy/model_ceilings/v1alpha1/ceiling_policy.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
edition = "2024";

package trogonai.policy.model_ceilings.v1alpha1;

import "buf/validate/validate.proto";

// Exact match on the catalog model identifier of ModelVersionRef, any version of
// it. A version arm is an additive change for when a ceiling needs to pin
// releases. Names, families, and patterns are not arms because a late-bound name
// would widen a ceiling without a policy change.
message ModelMatcher {
oneof matcher {
option (buf.validate.oneof).required = true;

string model_id = 1 [(buf.validate.field).string.min_len = 1];
}
}

// A ceiling bounds which catalog models the subject's Sessions may select,
// including auxiliary roles. A Session's every model selection must match some
// matcher. An empty ceiling is invalid: use ClearModelCeiling to remove the
// ceiling and a hold to stop work.
//
// Absence means no ceiling from this subject, never a zero cap, and never
// bypasses connection-level model authorization under ADR#0032: the
// connection's allowed models authorize, and a ceiling only narrows. Policy
// never substitutes a model; a selection outside the ceiling fails admission
// (ADR#0032 Decision 6).
//
// Ceilings follow the delegation lineage downward exactly like holds and
// budgets, so an Agent limited to one model cannot reach another by delegating.
// Evaluation is a conjunction over every ceiling on the chain.
message ModelCeilingPolicy {
option (buf.validate.message).cel = {
id: "model_ceiling_policy.models_unique"
message: "each model identifier may appear at most once"
expression: "this.within.map(m, m.model_id).unique()"
};

repeated ModelMatcher within = 1 [(buf.validate.field).repeated.min_items = 1];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
edition = "2024";

package trogonai.policy.model_ceilings.v1alpha1;

import "trogonai/policy/subjects/v1alpha1/subject.proto";

// Removes only this subject's ceiling; ceilings elsewhere on a Session's chain
// still apply. Clearing a missing history rejects with ModelCeilingNotSet. An
// already cleared history succeeds without an event after the concurrency
// check. Dispatch requires live administration authority and a nonempty Decider
// command_id plus the original positive expected_revision; retries preserve both.
message ClearModelCeiling {
trogonai.policy.subjects.v1alpha1.PolicySubject subject = 1 [features.field_presence = LEGACY_REQUIRED];
}
18 changes: 18 additions & 0 deletions proto/trogonai/policy/model_ceilings/v1alpha1/events.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
edition = "2024";

package trogonai.policy.model_ceilings.v1alpha1;

import "buf/validate/validate.proto";
import "trogonai/policy/model_ceilings/v1alpha1/model_ceiling_cleared.proto";
import "trogonai/policy/model_ceilings/v1alpha1/model_ceiling_set.proto";

// An unset or unknown event rejects replay; skipping a ceiling change could
// admit a model selection the current ceiling forbids, or refuse one it allows.
message ModelCeilingEvent {
oneof event {
option (buf.validate.oneof).required = true;

ModelCeilingSet model_ceiling_set = 1;
ModelCeilingCleared model_ceiling_cleared = 2;
}
}
39 changes: 39 additions & 0 deletions proto/trogonai/policy/model_ceilings/v1alpha1/faults.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
edition = "2024";

package trogonai.policy.model_ceilings.v1alpha1;

import "trogon/error/v1alpha1/options.proto";

// Schema-only templates for the policy.model_ceilings module's DeciderService
// refusals. These messages are never encoded. Administration authorization and
// model selection admission keep their own failure contracts; this fold only
// owns ceiling mutations.
message ModelCeilingCommandInvalidError {
option (trogon.error.v1alpha1.message).template = {
domain: "policy.model_ceilings"
reason: "MODEL_CEILING_COMMAND_INVALID"
message: "The command violates the model ceiling policy contract."
code: FAILED_PRECONDITION
visibility: VISIBILITY_PUBLIC
};
}

message ModelCeilingNotSetError {
option (trogon.error.v1alpha1.message).template = {
domain: "policy.model_ceilings"
reason: "MODEL_CEILING_NOT_SET"
message: "No model ceiling history exists for this subject."
code: FAILED_PRECONDITION
visibility: VISIBILITY_PUBLIC
};
}

message ModelCeilingSubjectMismatchError {
option (trogon.error.v1alpha1.message).template = {
domain: "policy.model_ceilings"
reason: "MODEL_CEILING_SUBJECT_MISMATCH"
message: "The command names a different subject from this model ceiling history."
code: FAILED_PRECONDITION
visibility: VISIBILITY_PUBLIC
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
edition = "2024";

package trogonai.policy.model_ceilings.v1alpha1;

import "trogonai/policy/subjects/v1alpha1/subject.proto";

// Removes the subject's ceiling and retains its history. A later set reuses the
// same stream.
message ModelCeilingCleared {
trogonai.policy.subjects.v1alpha1.PolicySubject subject = 1 [features.field_presence = LEGACY_REQUIRED];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
edition = "2024";

package trogonai.policy.model_ceilings.v1alpha1;

import "trogonai/policy/model_ceilings/v1alpha1/ceiling_policy.proto";
import "trogonai/policy/subjects/v1alpha1/subject.proto";

// Records the ceiling in force from this event, replacing any earlier one.
message ModelCeilingSet {
trogonai.policy.subjects.v1alpha1.PolicySubject subject = 1 [features.field_presence = LEGACY_REQUIRED];
ModelCeilingPolicy policy = 2 [features.field_presence = LEGACY_REQUIRED];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
edition = "2024";

package trogonai.policy.model_ceilings.v1alpha1;

import "trogonai/policy/model_ceilings/v1alpha1/ceiling_policy.proto";
import "trogonai/policy/subjects/v1alpha1/subject.proto";

// Sets or replaces the subject's ceiling. There is no Initialize command and no
// provisioning participation, because absence needs nothing set up. On a missing
// history it creates the stream: NoStream requires a nonempty Decider command_id
// and no expected_revision. Later commands carry the original positive
// expected_revision; retries preserve both. Dispatch verifies the subject exists
// and live administration authority. Equal matchers succeed without an event
// after the concurrency check; reordering matchers alone is not a change.
message SetModelCeiling {
trogonai.policy.subjects.v1alpha1.PolicySubject subject = 1 [features.field_presence = LEGACY_REQUIRED];
ModelCeilingPolicy policy = 2 [features.field_presence = LEGACY_REQUIRED];
}
1 change: 1 addition & 0 deletions proto/trogonai/policy/subjects/v1alpha1/rule_ref.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum PolicyRuleKind {
POLICY_RULE_KIND_AGENT_BUDGET = 1;
POLICY_RULE_KIND_AGENT_RATE_LIMIT = 2;
POLICY_RULE_KIND_HOLD = 3;
POLICY_RULE_KIND_MODEL_CEILING = 4;
}

message PolicyRuleRef {
Expand Down
Loading