Add granting and revoking affordances to the world kernel (play agent Cut 5, PA-Q10)
ComponentOp::GrantAffordance{subject, affordance} and RevokeAffordance
resolve beside GrantAuthority/RevokeAuthority: a retired or externally
controlled subject, or an unknown catalog entry, is refused (the new
Mismatch::GrantsOutsideControl); a grant already held or a revoke not held is
NoOperationEffect; a revoke that would leave a controlled subject with no
affordance at all is refused with the new Mismatch::WouldLeaveSubjectMute,
because derive_opportunities requires at least one. Applying either writes
state.affordance_grants directly, re-deciding the same three refusals against
committed state. confine_to_ground now refuses both, unconditionally, beside
Retire: a grant is authority over the catalog, not structure over ground, so
the owner (including the seed lane, the owner in Draft) and the play
authority are the only callers.
PATCH_TOOLS gains grant_affordance and revoke_affordance (Operate, subject
plus an affordance reference); catalog counts move to (7, 34, 43). Same
consumer.v6 / controller_work.v17 as Cuts 3 and 4.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
3725ba80fb
commit
732b1d33ad
3 changed files with 427 additions and 14 deletions
|
|
@ -2995,6 +2995,37 @@ fn apply_operation(
|
|||
state.positions.remove(subject);
|
||||
state.affordance_grants.remove(&scope);
|
||||
}
|
||||
ResolvedOp::GrantAffordance { subject, affordance }
|
||||
| ResolvedOp::RevokeAffordance { subject, affordance } => {
|
||||
let granting = matches!(operation, ResolvedOp::GrantAffordance { .. });
|
||||
let scope = DecisionScope {
|
||||
subject_id: *subject,
|
||||
};
|
||||
let assignment = state.controller_assignments.get(&scope).ok_or_else(unknown)?;
|
||||
if assignment.mode().is_none() || !state.affordance_catalog.contains_key(affordance) {
|
||||
return Err(unknown());
|
||||
}
|
||||
let granted = state
|
||||
.affordance_grants
|
||||
.get_mut(&scope)
|
||||
.ok_or_else(unknown)?;
|
||||
let held = granted.contains(affordance);
|
||||
if held == granting {
|
||||
return Err(KernelError::Invariant(
|
||||
"affordance grant operation changes nothing".into(),
|
||||
));
|
||||
}
|
||||
if granting {
|
||||
granted.insert(*affordance);
|
||||
} else {
|
||||
if granted.len() == 1 {
|
||||
return Err(KernelError::Invariant(
|
||||
"revoke would leave a controlled subject with no affordance".into(),
|
||||
));
|
||||
}
|
||||
granted.remove(affordance);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -4625,12 +4656,17 @@ fn confine_to_ground(
|
|||
}
|
||||
for (index, operation) in resolved.operations.iter().enumerate() {
|
||||
let site = Site::Operation(index);
|
||||
// Retirement is authority over the subject's standing, not structure
|
||||
// over ground: a confined author is refused whatever place the
|
||||
// subject stands under, exactly as `require_ruler` refuses a `Ruled`
|
||||
// fact or a `Mint` outright. The owner is unconfined and never
|
||||
// reaches this clause.
|
||||
if matches!(operation, ResolvedOp::Retire { .. }) {
|
||||
// Retirement and an affordance grant are authority over the subject's
|
||||
// standing, not structure over ground: a confined author is refused
|
||||
// whatever place the subject stands under, exactly as `require_ruler`
|
||||
// refuses a `Ruled` fact or a `Mint` outright. The owner is unconfined
|
||||
// and never reaches this clause.
|
||||
if matches!(
|
||||
operation,
|
||||
ResolvedOp::Retire { .. }
|
||||
| ResolvedOp::GrantAffordance { .. }
|
||||
| ResolvedOp::RevokeAffordance { .. }
|
||||
) {
|
||||
mismatches.push(Mismatch::OutsideJurisdiction { site: site.clone() });
|
||||
continue;
|
||||
}
|
||||
|
|
@ -4851,12 +4887,14 @@ fn operation_ground(
|
|||
}
|
||||
(subjects, places(named), routes)
|
||||
}
|
||||
// Confinement never reaches this through the ordinary ground check:
|
||||
// `confine_to_ground` refuses `Retire` unconditionally for every
|
||||
// confined author before this function is called for it. The
|
||||
// subject is named here so the match stays total and correct if
|
||||
// that clause is ever bypassed.
|
||||
ResolvedOp::Retire { subject } => (vec![*subject], Vec::new(), Vec::new()),
|
||||
// Confinement never reaches these through the ordinary ground check:
|
||||
// `confine_to_ground` refuses `Retire`, `GrantAffordance`, and
|
||||
// `RevokeAffordance` unconditionally for every confined author before
|
||||
// this function is called for them. The subject is named here so the
|
||||
// match stays total and correct if that clause is ever bypassed.
|
||||
ResolvedOp::Retire { subject }
|
||||
| ResolvedOp::GrantAffordance { subject, .. }
|
||||
| ResolvedOp::RevokeAffordance { subject, .. } => (vec![*subject], Vec::new(), Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -16734,4 +16772,244 @@ mod clock_tests {
|
|||
.retired
|
||||
);
|
||||
}
|
||||
|
||||
// ---- Cut 5: granting and revoking affordances ---------------------
|
||||
|
||||
fn wave_affordance() -> Declaration {
|
||||
Declaration::Affordance(AffordanceDeclaration {
|
||||
handle: DraftHandle::new("wave"),
|
||||
kind: AffordanceKindName("wave".into()),
|
||||
roles: vec![RoleSpec {
|
||||
role: Role("target".into()),
|
||||
kind: RefKind::Subject(None),
|
||||
}],
|
||||
preconditions: Vec::new(),
|
||||
effect_slots: vec![EffectSlot {
|
||||
op_kind: ComponentOpKind::AdvancePressure {
|
||||
by: PressureMagnitude(1),
|
||||
},
|
||||
roles: vec![Role("target".into())],
|
||||
bounds: Bounds::None,
|
||||
}],
|
||||
outcome_bands: vec![OutcomeBand {
|
||||
weight: 1,
|
||||
effects: vec![0],
|
||||
}],
|
||||
carries_speech: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// P5.1: the seed lane is the owner in Draft, and it grants the player a
|
||||
/// verb the same way any owner-authored `GrantAffordance` does.
|
||||
#[test]
|
||||
fn the_seed_grants_the_player_a_verb_in_draft() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let mut kernel = WorldKernel::create(
|
||||
directory.path().join("world.cc"),
|
||||
creation(CommandId::new(), "SeedGrantsDraft"),
|
||||
&auth_principal(owner()),
|
||||
)
|
||||
.expect("a created world")
|
||||
.0;
|
||||
let before = kernel.snapshot().unwrap();
|
||||
let commons = before.places[0].id;
|
||||
let speak = super::tests::speak_entry(&kernel);
|
||||
submit_owner(
|
||||
&mut kernel,
|
||||
&before,
|
||||
CommandBody::AdmitPatch {
|
||||
answers: None,
|
||||
patch: WorldPatch {
|
||||
declarations: vec![
|
||||
wave_affordance(),
|
||||
Declaration::Subject(SubjectDeclaration {
|
||||
handle: DraftHandle::new("player"),
|
||||
label: "The Player".into(),
|
||||
kind: SubjectKind::Person,
|
||||
controller: NewController::NarrativePersona,
|
||||
affordances: BTreeSet::from([speak]),
|
||||
position: Some(Ref::Existing(commons)),
|
||||
}),
|
||||
],
|
||||
operations: vec![ComponentOp::GrantAffordance {
|
||||
subject: Ref::Draft(DraftHandle::new("player")),
|
||||
affordance: Ref::Draft(DraftHandle::new("wave")),
|
||||
}],
|
||||
evidence: Vec::new(),
|
||||
},
|
||||
},
|
||||
);
|
||||
let snapshot = kernel.snapshot().unwrap();
|
||||
let player = snapshot
|
||||
.subjects
|
||||
.iter()
|
||||
.find(|subject| subject.label == "The Player")
|
||||
.expect("the declared player");
|
||||
assert_eq!(player.affordances.len(), 2);
|
||||
}
|
||||
|
||||
/// P5.1, and the scope digest half of Q10's design: a grant and a revoke
|
||||
/// in Active both move the scope digest, so a stale opportunity is
|
||||
/// `ScopeChanged` rather than silently honoured.
|
||||
#[test]
|
||||
fn the_play_authority_grants_and_revokes_in_active() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let (mut kernel, clockwork, active) =
|
||||
clock_kernel(directory.path(), "PlayGrantRevoke");
|
||||
let stale = opportunity_for(&active, clockwork.reeve);
|
||||
submit_as(
|
||||
&mut kernel,
|
||||
play_caller(),
|
||||
CommandBody::AdmitPatch {
|
||||
answers: None,
|
||||
patch: WorldPatch {
|
||||
declarations: vec![wave_affordance()],
|
||||
operations: vec![ComponentOp::GrantAffordance {
|
||||
subject: Ref::Existing(clockwork.reeve),
|
||||
affordance: Ref::Draft(DraftHandle::new("wave")),
|
||||
}],
|
||||
evidence: Vec::new(),
|
||||
},
|
||||
},
|
||||
)
|
||||
.expect("the play authority grants a new verb");
|
||||
let after_grant = kernel.snapshot().unwrap();
|
||||
assert_eq!(
|
||||
after_grant
|
||||
.subjects
|
||||
.iter()
|
||||
.find(|subject| subject.id == clockwork.reeve)
|
||||
.unwrap()
|
||||
.affordances
|
||||
.len(),
|
||||
4
|
||||
);
|
||||
let error = submit_as(
|
||||
&mut kernel,
|
||||
CallerId::Controller(stale.controller_id),
|
||||
CommandBody::DeclineDecision {
|
||||
opportunity: stale.clone(),
|
||||
},
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(matches!(error, KernelError::ScopeChanged { .. }), "{error:?}");
|
||||
|
||||
submit_as(
|
||||
&mut kernel,
|
||||
play_caller(),
|
||||
CommandBody::AdmitPatch {
|
||||
answers: None,
|
||||
patch: WorldPatch {
|
||||
declarations: Vec::new(),
|
||||
operations: vec![ComponentOp::RevokeAffordance {
|
||||
subject: Ref::Existing(clockwork.reeve),
|
||||
affordance: Ref::Existing(clockwork.threaten),
|
||||
}],
|
||||
evidence: Vec::new(),
|
||||
},
|
||||
},
|
||||
)
|
||||
.expect("the play authority revokes a held verb");
|
||||
let after_revoke = kernel.snapshot().unwrap();
|
||||
let reeve = after_revoke
|
||||
.subjects
|
||||
.iter()
|
||||
.find(|subject| subject.id == clockwork.reeve)
|
||||
.unwrap();
|
||||
assert_eq!(reeve.affordances.len(), 3);
|
||||
assert!(!reeve.affordances.contains(&clockwork.threaten));
|
||||
}
|
||||
|
||||
/// P5.2: `derive_opportunities` requires at least one grant; a revoke
|
||||
/// that would leave the reeve mute is refused before it ever reaches
|
||||
/// that invariant.
|
||||
#[test]
|
||||
fn a_revoke_cannot_leave_a_subject_mute() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let (mut kernel, clockwork, _) = clock_kernel(directory.path(), "RevokeMute");
|
||||
let speak = super::tests::speak_entry(&kernel);
|
||||
for affordance in [clockwork.deliver, clockwork.threaten] {
|
||||
submit_as(
|
||||
&mut kernel,
|
||||
play_caller(),
|
||||
CommandBody::AdmitPatch {
|
||||
answers: None,
|
||||
patch: WorldPatch {
|
||||
declarations: Vec::new(),
|
||||
operations: vec![ComponentOp::RevokeAffordance {
|
||||
subject: Ref::Existing(clockwork.reeve),
|
||||
affordance: Ref::Existing(affordance),
|
||||
}],
|
||||
evidence: Vec::new(),
|
||||
},
|
||||
},
|
||||
)
|
||||
.expect("the play authority revokes down toward one verb");
|
||||
}
|
||||
let Ref::Existing(speak_id) = speak else {
|
||||
panic!("speak is a standing affordance");
|
||||
};
|
||||
let error = submit_as(
|
||||
&mut kernel,
|
||||
play_caller(),
|
||||
CommandBody::AdmitPatch {
|
||||
answers: None,
|
||||
patch: WorldPatch {
|
||||
declarations: Vec::new(),
|
||||
operations: vec![ComponentOp::RevokeAffordance {
|
||||
subject: Ref::Existing(clockwork.reeve),
|
||||
affordance: Ref::Existing(speak_id),
|
||||
}],
|
||||
evidence: Vec::new(),
|
||||
},
|
||||
},
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(
|
||||
matches!(&error, KernelError::PatchRejected(set)
|
||||
if set.contains(&Mismatch::WouldLeaveSubjectMute { operation: 0 })),
|
||||
"{error:?}"
|
||||
);
|
||||
}
|
||||
|
||||
/// P5.1: `confine_to_ground` refuses `GrantAffordance` for a confined
|
||||
/// author unconditionally, even for a subject this same patch declares
|
||||
/// and places inside the elaborator's own jurisdiction.
|
||||
#[test]
|
||||
fn an_elaborator_cannot_grant() {
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let (mut kernel, clockwork, _) = clock_kernel(directory.path(), "GrantElaborator");
|
||||
let speak = super::tests::speak_entry(&kernel);
|
||||
let answered = dead_end_boundary(&kernel);
|
||||
let error = submit_as(
|
||||
&mut kernel,
|
||||
elaborator(JurisdictionKey::PlaceSubtree(clockwork.dead_end)),
|
||||
CommandBody::AdmitPatch {
|
||||
answers: Some(PatchAnswer::Boundary(answered)),
|
||||
patch: WorldPatch {
|
||||
declarations: vec![Declaration::Subject(SubjectDeclaration {
|
||||
handle: DraftHandle::new("stray"),
|
||||
label: "The Roadside Stray".into(),
|
||||
kind: SubjectKind::Person,
|
||||
controller: NewController::NarrativePersona,
|
||||
affordances: BTreeSet::from([speak]),
|
||||
position: Some(Ref::Existing(clockwork.dead_end)),
|
||||
})],
|
||||
operations: vec![ComponentOp::GrantAffordance {
|
||||
subject: Ref::Draft(DraftHandle::new("stray")),
|
||||
affordance: Ref::Existing(clockwork.threaten),
|
||||
}],
|
||||
evidence: Vec::new(),
|
||||
},
|
||||
},
|
||||
)
|
||||
.unwrap_err();
|
||||
assert!(
|
||||
matches!(&error, KernelError::PatchRejected(set)
|
||||
if set.contains(&Mismatch::OutsideJurisdiction {
|
||||
site: Site::Operation(0)
|
||||
})),
|
||||
"{error:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1207,6 +1207,20 @@ pub(crate) enum ComponentOp {
|
|||
Retire {
|
||||
subject: Ref<SubjectId>,
|
||||
},
|
||||
/// Grants one more standing catalog entry to an existing subject. Only the
|
||||
/// owner (including the seed lane, the owner in Draft) and `Play` may
|
||||
/// write it: a grant is authority, not structure, so a confined author is
|
||||
/// refused whatever ground the subject stands on.
|
||||
GrantAffordance {
|
||||
subject: Ref<SubjectId>,
|
||||
affordance: Ref<AffordanceId>,
|
||||
},
|
||||
/// Revokes a grant. Refused if it would leave a controlled subject with
|
||||
/// none: `derive_opportunities` requires at least one.
|
||||
RevokeAffordance {
|
||||
subject: Ref<SubjectId>,
|
||||
affordance: Ref<AffordanceId>,
|
||||
},
|
||||
}
|
||||
|
||||
/// What an Active `AdmitPatch` answers. Draft answers nothing; Active must
|
||||
|
|
@ -1586,6 +1600,18 @@ pub enum Mismatch {
|
|||
RetiresAMirror {
|
||||
operation: usize,
|
||||
},
|
||||
/// `GrantAffordance` or `RevokeAffordance` named a retired or an
|
||||
/// `ExternallyControlled` subject: neither holds a grant this patch may
|
||||
/// touch.
|
||||
GrantsOutsideControl {
|
||||
operation: usize,
|
||||
},
|
||||
/// A `RevokeAffordance` that would leave a controlled subject with no
|
||||
/// affordance at all. `derive_opportunities` requires at least one; a
|
||||
/// subject is never left mute.
|
||||
WouldLeaveSubjectMute {
|
||||
operation: usize,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
|
|
@ -1883,6 +1909,14 @@ pub(crate) enum ResolvedOp {
|
|||
Retire {
|
||||
subject: SubjectId,
|
||||
},
|
||||
GrantAffordance {
|
||||
subject: SubjectId,
|
||||
affordance: AffordanceId,
|
||||
},
|
||||
RevokeAffordance {
|
||||
subject: SubjectId,
|
||||
affordance: AffordanceId,
|
||||
},
|
||||
}
|
||||
|
||||
/// One resource's movement across a whole patch. Accumulators are `u128` so an
|
||||
|
|
@ -4209,6 +4243,61 @@ pub(super) fn resolve_patch(
|
|||
}
|
||||
}
|
||||
}
|
||||
ComponentOp::GrantAffordance { subject, affordance }
|
||||
| ComponentOp::RevokeAffordance { subject, affordance } => {
|
||||
let granting = matches!(operation, ComponentOp::GrantAffordance { .. });
|
||||
let subject_key = resolve_subject(
|
||||
Site::Operation(position),
|
||||
subject,
|
||||
&index,
|
||||
&state.subjects,
|
||||
&mut mismatches,
|
||||
);
|
||||
let affordance_key = resolve_affordance(
|
||||
Site::Operation(position),
|
||||
affordance,
|
||||
&index,
|
||||
&state.affordance_catalog,
|
||||
&mut mismatches,
|
||||
);
|
||||
let (Some(subject_key), Some(affordance_key)) = (subject_key, affordance_key)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
// A grant is authority over the catalog, not structure over
|
||||
// ground: retirement and a mirror both hold no grant a patch
|
||||
// may touch, whatever confinement later decides.
|
||||
if retired.contains(&subject_key) || mirrors.contains(&subject_key) {
|
||||
mismatches.push(Mismatch::GrantsOutsideControl {
|
||||
operation: position,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
let held = affordance_holdings
|
||||
.get(&subject_key)
|
||||
.is_some_and(|granted| granted.contains(&affordance_key));
|
||||
if held == granting {
|
||||
mismatches.push(Mismatch::NoOperationEffect {
|
||||
operation: position,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if granting {
|
||||
affordance_holdings
|
||||
.entry(subject_key)
|
||||
.or_default()
|
||||
.insert(affordance_key);
|
||||
} else {
|
||||
let granted = affordance_holdings.entry(subject_key).or_default();
|
||||
if granted.len() == 1 {
|
||||
mismatches.push(Mismatch::WouldLeaveSubjectMute {
|
||||
operation: position,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
granted.remove(&affordance_key);
|
||||
}
|
||||
}
|
||||
ComponentOp::OpenOffice {
|
||||
institution,
|
||||
office,
|
||||
|
|
@ -5239,6 +5328,20 @@ pub(super) fn resolve_patch(
|
|||
ComponentOp::Retire { subject } => ResolvedOp::Retire {
|
||||
subject: subject_id_of(&key_of(subject)),
|
||||
},
|
||||
ComponentOp::GrantAffordance {
|
||||
subject,
|
||||
affordance,
|
||||
} => ResolvedOp::GrantAffordance {
|
||||
subject: subject_id_of(&key_of(subject)),
|
||||
affordance: affordance_id_of(&key_of(affordance)),
|
||||
},
|
||||
ComponentOp::RevokeAffordance {
|
||||
subject,
|
||||
affordance,
|
||||
} => ResolvedOp::RevokeAffordance {
|
||||
subject: subject_id_of(&key_of(subject)),
|
||||
affordance: affordance_id_of(&key_of(affordance)),
|
||||
},
|
||||
ComponentOp::Relocate { subject, via } => ResolvedOp::Relocate {
|
||||
subject_id: subject_id_of(&key_of(subject)),
|
||||
edge_id: edge_id_of(&key_of(via)),
|
||||
|
|
@ -6282,6 +6385,28 @@ pub(crate) const PATCH_TOOLS: &[PatchTool] = &[
|
|||
fields: &[field("subject", PatchFieldKind::Reference("subject"))],
|
||||
shape: PatchToolShape::Operate { variant: "retire" },
|
||||
},
|
||||
PatchTool {
|
||||
name: "grant_affordance",
|
||||
description: "Grant a subject one more affordance from the standing catalog. Only the owner and the play table may grant.",
|
||||
fields: &[
|
||||
field("subject", PatchFieldKind::Reference("subject")),
|
||||
field("affordance", PatchFieldKind::Reference("affordance")),
|
||||
],
|
||||
shape: PatchToolShape::Operate {
|
||||
variant: "grant_affordance",
|
||||
},
|
||||
},
|
||||
PatchTool {
|
||||
name: "revoke_affordance",
|
||||
description: "Revoke one of a subject's affordances. Refused if it would leave the subject with none. Only the owner and the play table may revoke.",
|
||||
fields: &[
|
||||
field("subject", PatchFieldKind::Reference("subject")),
|
||||
field("affordance", PatchFieldKind::Reference("affordance")),
|
||||
],
|
||||
shape: PatchToolShape::Operate {
|
||||
variant: "revoke_affordance",
|
||||
},
|
||||
},
|
||||
PatchTool {
|
||||
name: RECORD_GAP_PATCH_TOOL,
|
||||
description: "Record something the world needs that this vocabulary cannot say. It changes nothing.",
|
||||
|
|
@ -7023,7 +7148,7 @@ mod catalog_tests {
|
|||
.iter()
|
||||
.filter(|entry| matches!(entry.shape, PatchToolShape::Operate { .. }))
|
||||
.count();
|
||||
assert_eq!((declarations, operations, PATCH_TOOLS.len()), (7, 32, 41));
|
||||
assert_eq!((declarations, operations, PATCH_TOOLS.len()), (7, 34, 43));
|
||||
|
||||
// Every declaration variant the vocabulary owns is reachable, and the
|
||||
// two payload-carrying entity kinds are not exposed as an `Entity`
|
||||
|
|
@ -7119,6 +7244,8 @@ mod catalog_tests {
|
|||
ComponentOp::ReducePressure { .. } => "reduce_pressure",
|
||||
ComponentOp::ResolvePressure { .. } => "resolve_pressure",
|
||||
ComponentOp::Retire { .. } => "retire",
|
||||
ComponentOp::GrantAffordance { .. } => "grant_affordance",
|
||||
ComponentOp::RevokeAffordance { .. } => "revoke_affordance",
|
||||
}
|
||||
}
|
||||
PATCH_TOOLS
|
||||
|
|
@ -7170,7 +7297,7 @@ mod catalog_tests {
|
|||
.count();
|
||||
assert_eq!(
|
||||
(declare_tools, operate_tools, PATCH_TOOLS.len()),
|
||||
(7, 32, 41)
|
||||
(7, 34, 43)
|
||||
);
|
||||
|
||||
let declarations = every_declaration();
|
||||
|
|
|
|||
|
|
@ -95,6 +95,10 @@
|
|||
"name": "forget",
|
||||
"parameters_json": "{\"additionalProperties\":false,\"properties\":{\"fact\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing fact\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"},\"subject\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing subject\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"}},\"required\":[\"subject\",\"fact\"],\"type\":\"object\"}"
|
||||
},
|
||||
{
|
||||
"name": "grant_affordance",
|
||||
"parameters_json": "{\"additionalProperties\":false,\"properties\":{\"affordance\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing affordance\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"},\"subject\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing subject\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"}},\"required\":[\"subject\",\"affordance\"],\"type\":\"object\"}"
|
||||
},
|
||||
{
|
||||
"name": "grant_authority",
|
||||
"parameters_json": "{\"additionalProperties\":false,\"properties\":{\"grant\":{\"additionalProperties\":false,\"properties\":{\"kind\":{\"description\":\"the authority kind\",\"type\":\"string\"},\"over\":{\"anyOf\":[{\"additionalProperties\":false,\"properties\":{\"over\":{\"const\":\"subject\",\"type\":\"string\"},\"ref\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing subject\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"}},\"required\":[\"over\",\"ref\"],\"type\":\"object\"},{\"additionalProperties\":false,\"properties\":{\"over\":{\"const\":\"place_subtree\",\"type\":\"string\"},\"ref\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing place\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"}},\"required\":[\"over\",\"ref\"],\"type\":\"object\"}]}},\"required\":[\"kind\",\"over\"],\"type\":\"object\"},\"holder\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing subject\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"}},\"required\":[\"holder\",\"grant\"],\"type\":\"object\"}"
|
||||
|
|
@ -147,6 +151,10 @@
|
|||
"name": "retire",
|
||||
"parameters_json": "{\"additionalProperties\":false,\"properties\":{\"subject\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing subject\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"}},\"required\":[\"subject\"],\"type\":\"object\"}"
|
||||
},
|
||||
{
|
||||
"name": "revoke_affordance",
|
||||
"parameters_json": "{\"additionalProperties\":false,\"properties\":{\"affordance\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing affordance\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"},\"subject\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing subject\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"}},\"required\":[\"subject\",\"affordance\"],\"type\":\"object\"}"
|
||||
},
|
||||
{
|
||||
"name": "revoke_authority",
|
||||
"parameters_json": "{\"additionalProperties\":false,\"properties\":{\"grant\":{\"additionalProperties\":false,\"properties\":{\"kind\":{\"description\":\"the authority kind\",\"type\":\"string\"},\"over\":{\"anyOf\":[{\"additionalProperties\":false,\"properties\":{\"over\":{\"const\":\"subject\",\"type\":\"string\"},\"ref\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing subject\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"}},\"required\":[\"over\",\"ref\"],\"type\":\"object\"},{\"additionalProperties\":false,\"properties\":{\"over\":{\"const\":\"place_subtree\",\"type\":\"string\"},\"ref\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing place\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"}},\"required\":[\"over\",\"ref\"],\"type\":\"object\"}]}},\"required\":[\"kind\",\"over\"],\"type\":\"object\"},\"holder\":{\"additionalProperties\":false,\"properties\":{\"ref\":{\"enum\":[\"draft\",\"existing\"],\"type\":\"string\"},\"value\":{\"description\":\"a draft handle declared in this same patch, or the canonical id of an existing subject\",\"type\":\"string\"}},\"required\":[\"ref\",\"value\"],\"type\":\"object\"}},\"required\":[\"holder\",\"grant\"],\"type\":\"object\"}"
|
||||
|
|
|
|||
Loading…
Reference in a new issue