ticket: use boolean ticket capabilities
This commit is contained in:
parent
064965d350
commit
7f7d2fe7fc
|
|
@ -19,8 +19,8 @@ use crate::plugin::PluginConfig;
|
||||||
use crate::{
|
use crate::{
|
||||||
CompactionConfig, EngineManifest, FeatureConfig, FeatureFlagConfig, FileUploadLimits,
|
CompactionConfig, EngineManifest, FeatureConfig, FeatureFlagConfig, FileUploadLimits,
|
||||||
McpConfig, McpEnvValue, McpStdioCwdPolicy, MemoryConfig, ScopeConfig, SessionConfig,
|
McpConfig, McpEnvValue, McpStdioCwdPolicy, MemoryConfig, ScopeConfig, SessionConfig,
|
||||||
SkillsConfig, TicketFeatureAccessConfig, TicketFeatureConfig, ToolOutputLimits,
|
SkillsConfig, TicketFeatureConfig, ToolOutputLimits, ToolPermissionConfig, ToolPermissionRule,
|
||||||
ToolPermissionConfig, ToolPermissionRule, WebConfig, WorkerManifest, WorkerMeta,
|
WebConfig, WorkerManifest, WorkerMeta,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Partial-form Worker manifest. Every field is optional; one or more
|
/// Partial-form Worker manifest. Every field is optional; one or more
|
||||||
|
|
@ -117,19 +117,24 @@ impl FeatureFlagConfigPartial {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
|
||||||
|
#[serde(default, deny_unknown_fields)]
|
||||||
pub struct TicketFeatureConfigPartial {
|
pub struct TicketFeatureConfigPartial {
|
||||||
#[serde(default)]
|
|
||||||
pub enabled: Option<bool>,
|
pub enabled: Option<bool>,
|
||||||
#[serde(default)]
|
pub authoring: Option<bool>,
|
||||||
pub preset: Option<TicketFeatureAccessConfig>,
|
pub thread: Option<bool>,
|
||||||
|
pub intake: Option<bool>,
|
||||||
|
pub orchestration_control: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TicketFeatureConfigPartial {
|
impl TicketFeatureConfigPartial {
|
||||||
fn merge(self, other: Self) -> Self {
|
fn merge(self, other: Self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
enabled: other.enabled.or(self.enabled),
|
enabled: other.enabled.or(self.enabled),
|
||||||
preset: other.preset.or(self.preset),
|
authoring: other.authoring.or(self.authoring),
|
||||||
|
thread: other.thread.or(self.thread),
|
||||||
|
intake: other.intake.or(self.intake),
|
||||||
|
orchestration_control: other.orchestration_control.or(self.orchestration_control),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +184,10 @@ impl From<TicketFeatureConfigPartial> for TicketFeatureConfig {
|
||||||
fn from(value: TicketFeatureConfigPartial) -> Self {
|
fn from(value: TicketFeatureConfigPartial) -> Self {
|
||||||
Self {
|
Self {
|
||||||
enabled: value.enabled.unwrap_or_default(),
|
enabled: value.enabled.unwrap_or_default(),
|
||||||
preset: value.preset.unwrap_or_default(),
|
authoring: value.authoring.unwrap_or_default(),
|
||||||
|
thread: value.thread.unwrap_or_default(),
|
||||||
|
intake: value.intake.unwrap_or_default(),
|
||||||
|
orchestration_control: value.orchestration_control.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -188,7 +196,10 @@ impl From<TicketFeatureConfig> for TicketFeatureConfigPartial {
|
||||||
fn from(value: TicketFeatureConfig) -> Self {
|
fn from(value: TicketFeatureConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
enabled: Some(value.enabled),
|
enabled: Some(value.enabled),
|
||||||
preset: Some(value.preset),
|
authoring: Some(value.authoring),
|
||||||
|
thread: Some(value.thread),
|
||||||
|
intake: Some(value.intake),
|
||||||
|
orchestration_control: Some(value.orchestration_control),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1757,7 +1768,10 @@ enabled = true
|
||||||
|
|
||||||
[feature.ticket]
|
[feature.ticket]
|
||||||
enabled = true
|
enabled = true
|
||||||
preset = "read_only"
|
authoring = false
|
||||||
|
thread = false
|
||||||
|
intake = false
|
||||||
|
orchestration_control = false
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -1787,10 +1801,10 @@ preset = "read_only"
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(manifest.feature.task.enabled);
|
assert!(manifest.feature.task.enabled);
|
||||||
assert!(manifest.feature.ticket.enabled);
|
assert!(manifest.feature.ticket.enabled);
|
||||||
assert_eq!(
|
assert!(!manifest.feature.ticket.authoring);
|
||||||
manifest.feature.ticket.preset,
|
assert!(!manifest.feature.ticket.thread);
|
||||||
TicketFeatureAccessConfig::ReadOnly
|
assert!(!manifest.feature.ticket.intake);
|
||||||
);
|
assert!(!manifest.feature.ticket.orchestration_control);
|
||||||
assert!(!manifest.feature.memory.enabled);
|
assert!(!manifest.feature.memory.enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1803,14 +1817,18 @@ enabled = true
|
||||||
|
|
||||||
[feature.ticket]
|
[feature.ticket]
|
||||||
enabled = true
|
enabled = true
|
||||||
preset = "read_only"
|
authoring = false
|
||||||
|
thread = false
|
||||||
|
intake = false
|
||||||
|
orchestration_control = false
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let upper = WorkerManifestConfig::from_toml(
|
let upper = WorkerManifestConfig::from_toml(
|
||||||
r#"
|
r#"
|
||||||
[feature.ticket]
|
[feature.ticket]
|
||||||
preset = "orchestration_control"
|
thread = true
|
||||||
|
orchestration_control = true
|
||||||
|
|
||||||
[feature.web]
|
[feature.web]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
@ -1844,10 +1862,10 @@ enabled = true
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(manifest.feature.memory.enabled);
|
assert!(manifest.feature.memory.enabled);
|
||||||
assert!(manifest.feature.ticket.enabled);
|
assert!(manifest.feature.ticket.enabled);
|
||||||
assert_eq!(
|
assert!(!manifest.feature.ticket.authoring);
|
||||||
manifest.feature.ticket.preset,
|
assert!(manifest.feature.ticket.thread);
|
||||||
TicketFeatureAccessConfig::OrchestrationControl
|
assert!(!manifest.feature.ticket.intake);
|
||||||
);
|
assert!(manifest.feature.ticket.orchestration_control);
|
||||||
assert!(manifest.feature.web.enabled);
|
assert!(manifest.feature.web.enabled);
|
||||||
assert!(!manifest.feature.workers.enabled);
|
assert!(!manifest.feature.workers.enabled);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -153,38 +153,18 @@ impl Default for FeatureFlagConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
|
||||||
pub struct TicketFeatureConfig {
|
pub struct TicketFeatureConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub preset: TicketFeatureAccessConfig,
|
pub authoring: bool,
|
||||||
}
|
#[serde(default)]
|
||||||
|
pub thread: bool,
|
||||||
impl Default for TicketFeatureConfig {
|
#[serde(default)]
|
||||||
fn default() -> Self {
|
pub intake: bool,
|
||||||
Self {
|
#[serde(default)]
|
||||||
enabled: false,
|
pub orchestration_control: bool,
|
||||||
preset: TicketFeatureAccessConfig::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
pub enum TicketFeatureAccessConfig {
|
|
||||||
ReadOnly,
|
|
||||||
WorkspaceAuthoring,
|
|
||||||
Intake,
|
|
||||||
OrchestrationControl,
|
|
||||||
WorkReport,
|
|
||||||
Review,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TicketFeatureAccessConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::WorkspaceAuthoring
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// External Agent Skills (`SKILL.md`) ingest configuration. Skills are
|
/// External Agent Skills (`SKILL.md`) ingest configuration. Skills are
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ use crate::model::{AuthRef, ModelManifest};
|
||||||
use crate::plugin::PluginConfig;
|
use crate::plugin::PluginConfig;
|
||||||
use crate::{
|
use crate::{
|
||||||
EngineManifestConfig, McpConfig, McpStdioCwdPolicy, MemoryConfig, Permission, ResolveError,
|
EngineManifestConfig, McpConfig, McpStdioCwdPolicy, MemoryConfig, Permission, ResolveError,
|
||||||
ScopeConfig, ScopeRule, SkillsConfig, TicketFeatureAccessConfig, WebConfig, WorkerManifest,
|
ScopeConfig, ScopeRule, SkillsConfig, WebConfig, WorkerManifest, WorkerManifestConfig,
|
||||||
WorkerManifestConfig, WorkerMetaConfig, paths,
|
WorkerMetaConfig, paths,
|
||||||
};
|
};
|
||||||
|
|
||||||
const PROFILE_FORMAT_V1: &str = "yoi.profile.v1";
|
const PROFILE_FORMAT_V1: &str = "yoi.profile.v1";
|
||||||
|
|
@ -971,7 +971,7 @@ fn builtin_default_profile_artifact() -> serde_json::Value {
|
||||||
"memory": { "enabled": true },
|
"memory": { "enabled": true },
|
||||||
"web": { "enabled": true },
|
"web": { "enabled": true },
|
||||||
"workers": { "enabled": true },
|
"workers": { "enabled": true },
|
||||||
"ticket": { "enabled": true, "preset": "workspace_authoring" }
|
"ticket": { "enabled": true, "authoring": true, "thread": true }
|
||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
"extract_threshold": 50000,
|
"extract_threshold": 50000,
|
||||||
|
|
@ -1005,18 +1005,19 @@ fn apply_role_profile(
|
||||||
value["feature"]["memory"] = serde_json::json!({ "enabled": memory });
|
value["feature"]["memory"] = serde_json::json!({ "enabled": memory });
|
||||||
value["feature"]["web"] = serde_json::json!({ "enabled": web });
|
value["feature"]["web"] = serde_json::json!({ "enabled": web });
|
||||||
value["feature"]["workers"] = serde_json::json!({ "enabled": workers });
|
value["feature"]["workers"] = serde_json::json!({ "enabled": workers });
|
||||||
let ticket_access = match slug {
|
let ticket = match slug {
|
||||||
"companion" => TicketFeatureAccessConfig::WorkspaceAuthoring,
|
"companion" => serde_json::json!({ "enabled": true, "authoring": true, "thread": true }),
|
||||||
"intake" => TicketFeatureAccessConfig::Intake,
|
"intake" => {
|
||||||
"orchestrator" => TicketFeatureAccessConfig::OrchestrationControl,
|
serde_json::json!({ "enabled": true, "authoring": true, "thread": true, "intake": true })
|
||||||
"coder" => TicketFeatureAccessConfig::WorkReport,
|
}
|
||||||
"reviewer" => TicketFeatureAccessConfig::Review,
|
"orchestrator" => {
|
||||||
_ => TicketFeatureAccessConfig::WorkspaceAuthoring,
|
serde_json::json!({ "enabled": true, "thread": true, "orchestration_control": true })
|
||||||
|
}
|
||||||
|
"coder" => serde_json::json!({ "enabled": true, "thread": true }),
|
||||||
|
"reviewer" => serde_json::json!({ "enabled": true, "thread": true }),
|
||||||
|
_ => serde_json::json!({ "enabled": true, "authoring": true, "thread": true }),
|
||||||
};
|
};
|
||||||
value["feature"]["ticket"] = serde_json::json!({
|
value["feature"]["ticket"] = ticket;
|
||||||
"enabled": true,
|
|
||||||
"preset": ticket_access,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reject_manifest_shaped_profile(value: &serde_json::Value) -> Result<(), ProfileError> {
|
fn reject_manifest_shaped_profile(value: &serde_json::Value) -> Result<(), ProfileError> {
|
||||||
|
|
@ -1442,10 +1443,10 @@ mod tests {
|
||||||
assert_eq!(companion.model.ref_.as_deref(), Some("codex-oauth/gpt-5.5"));
|
assert_eq!(companion.model.ref_.as_deref(), Some("codex-oauth/gpt-5.5"));
|
||||||
assert!(companion.web.is_some());
|
assert!(companion.web.is_some());
|
||||||
assert!(companion.feature.ticket.enabled);
|
assert!(companion.feature.ticket.enabled);
|
||||||
assert_eq!(
|
assert!(companion.feature.ticket.authoring);
|
||||||
companion.feature.ticket.preset,
|
assert!(companion.feature.ticket.thread);
|
||||||
TicketFeatureAccessConfig::WorkspaceAuthoring
|
assert!(!companion.feature.ticket.intake);
|
||||||
);
|
assert!(!companion.feature.ticket.orchestration_control);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
companion.compaction.as_ref().unwrap().threshold,
|
companion.compaction.as_ref().unwrap().threshold,
|
||||||
Some(240000)
|
Some(240000)
|
||||||
|
|
@ -1467,10 +1468,11 @@ mod tests {
|
||||||
assert!(intake.feature.task.enabled);
|
assert!(intake.feature.task.enabled);
|
||||||
assert!(!intake.feature.workers.enabled);
|
assert!(!intake.feature.workers.enabled);
|
||||||
assert!(intake.feature.ticket.enabled);
|
assert!(intake.feature.ticket.enabled);
|
||||||
assert_eq!(
|
assert!(intake.feature.ticket.enabled);
|
||||||
intake.feature.ticket.preset,
|
assert!(intake.feature.ticket.authoring);
|
||||||
TicketFeatureAccessConfig::Intake
|
assert!(intake.feature.ticket.thread);
|
||||||
);
|
assert!(intake.feature.ticket.intake);
|
||||||
|
assert!(!intake.feature.ticket.orchestration_control);
|
||||||
assert!(intake.scope.allow.is_empty());
|
assert!(intake.scope.allow.is_empty());
|
||||||
assert!(intake.delegation_scope.allow.is_empty());
|
assert!(intake.delegation_scope.allow.is_empty());
|
||||||
assert_eq!(intake.model.ref_.as_deref(), Some("codex-oauth/gpt-5.5"));
|
assert_eq!(intake.model.ref_.as_deref(), Some("codex-oauth/gpt-5.5"));
|
||||||
|
|
@ -1481,10 +1483,11 @@ mod tests {
|
||||||
assert!(orchestrator.feature.task.enabled);
|
assert!(orchestrator.feature.task.enabled);
|
||||||
assert!(orchestrator.feature.workers.enabled);
|
assert!(orchestrator.feature.workers.enabled);
|
||||||
assert!(orchestrator.feature.ticket.enabled);
|
assert!(orchestrator.feature.ticket.enabled);
|
||||||
assert_eq!(
|
assert!(orchestrator.feature.ticket.enabled);
|
||||||
orchestrator.feature.ticket.preset,
|
assert!(!orchestrator.feature.ticket.authoring);
|
||||||
TicketFeatureAccessConfig::OrchestrationControl
|
assert!(orchestrator.feature.ticket.thread);
|
||||||
);
|
assert!(!orchestrator.feature.ticket.intake);
|
||||||
|
assert!(orchestrator.feature.ticket.orchestration_control);
|
||||||
assert!(orchestrator.scope.allow.is_empty());
|
assert!(orchestrator.scope.allow.is_empty());
|
||||||
assert!(orchestrator.delegation_scope.allow.is_empty());
|
assert!(orchestrator.delegation_scope.allow.is_empty());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -1503,19 +1506,20 @@ mod tests {
|
||||||
assert!(coder.web.is_some());
|
assert!(coder.web.is_some());
|
||||||
assert!(coder.compaction.is_some());
|
assert!(coder.compaction.is_some());
|
||||||
assert!(coder.feature.ticket.enabled);
|
assert!(coder.feature.ticket.enabled);
|
||||||
assert_eq!(
|
assert!(coder.feature.ticket.enabled);
|
||||||
coder.feature.ticket.preset,
|
assert!(!coder.feature.ticket.authoring);
|
||||||
TicketFeatureAccessConfig::WorkReport
|
assert!(coder.feature.ticket.thread);
|
||||||
);
|
assert!(!coder.feature.ticket.intake);
|
||||||
|
assert!(!coder.feature.ticket.orchestration_control);
|
||||||
let reviewer = resolve("reviewer");
|
let reviewer = resolve("reviewer");
|
||||||
assert!(reviewer.feature.task.enabled);
|
assert!(reviewer.feature.task.enabled);
|
||||||
assert!(!reviewer.feature.workers.enabled);
|
assert!(!reviewer.feature.workers.enabled);
|
||||||
assert!(reviewer.feature.ticket.enabled);
|
assert!(reviewer.feature.ticket.enabled);
|
||||||
assert_eq!(
|
assert!(reviewer.feature.ticket.enabled);
|
||||||
reviewer.feature.ticket.preset,
|
assert!(!reviewer.feature.ticket.authoring);
|
||||||
TicketFeatureAccessConfig::Review
|
assert!(reviewer.feature.ticket.thread);
|
||||||
);
|
assert!(!reviewer.feature.ticket.intake);
|
||||||
|
assert!(!reviewer.feature.ticket.orchestration_control);
|
||||||
assert!(reviewer.scope.allow.is_empty());
|
assert!(reviewer.scope.allow.is_empty());
|
||||||
assert!(reviewer.delegation_scope.allow.is_empty());
|
assert!(reviewer.delegation_scope.allow.is_empty());
|
||||||
assert_eq!(reviewer.model.ref_.as_deref(), Some("codex-oauth/gpt-5.5"));
|
assert_eq!(reviewer.model.ref_.as_deref(), Some("codex-oauth/gpt-5.5"));
|
||||||
|
|
@ -1664,7 +1668,10 @@ enabled = true
|
||||||
|
|
||||||
[feature.ticket]
|
[feature.ticket]
|
||||||
enabled = true
|
enabled = true
|
||||||
preset = "read_only"
|
authoring = false
|
||||||
|
thread = false
|
||||||
|
intake = false
|
||||||
|
orchestration_control = false
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
let workspace = tmp.path().join("workspace");
|
let workspace = tmp.path().join("workspace");
|
||||||
|
|
@ -1682,10 +1689,10 @@ preset = "read_only"
|
||||||
assert!(resolved.manifest.feature.web.enabled);
|
assert!(resolved.manifest.feature.web.enabled);
|
||||||
assert!(resolved.manifest.feature.workers.enabled);
|
assert!(resolved.manifest.feature.workers.enabled);
|
||||||
assert!(resolved.manifest.feature.ticket.enabled);
|
assert!(resolved.manifest.feature.ticket.enabled);
|
||||||
assert_eq!(
|
assert!(!resolved.manifest.feature.ticket.authoring);
|
||||||
resolved.manifest.feature.ticket.preset,
|
assert!(!resolved.manifest.feature.ticket.thread);
|
||||||
crate::TicketFeatureAccessConfig::ReadOnly
|
assert!(!resolved.manifest.feature.ticket.intake);
|
||||||
);
|
assert!(!resolved.manifest.feature.ticket.orchestration_control);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolved.manifest.delegation_scope.allow[0].target,
|
resolved.manifest.delegation_scope.allow[0].target,
|
||||||
workspace
|
workspace
|
||||||
|
|
@ -1768,14 +1775,11 @@ worker_context_max_tokens = 68000
|
||||||
resolved.manifest.model.ref_.as_deref(),
|
resolved.manifest.model.ref_.as_deref(),
|
||||||
Some("codex-oauth/gpt-5.5")
|
Some("codex-oauth/gpt-5.5")
|
||||||
);
|
);
|
||||||
assert!(resolved.manifest.scope.allow.is_empty());
|
|
||||||
assert!(resolved.manifest.delegation_scope.allow.is_empty());
|
|
||||||
assert!(resolved.manifest.session.record_event_trace);
|
|
||||||
assert!(resolved.manifest.feature.ticket.enabled);
|
assert!(resolved.manifest.feature.ticket.enabled);
|
||||||
assert_eq!(
|
assert!(resolved.manifest.feature.ticket.authoring);
|
||||||
resolved.manifest.feature.ticket.preset,
|
assert!(resolved.manifest.feature.ticket.thread);
|
||||||
crate::TicketFeatureAccessConfig::WorkspaceAuthoring
|
assert!(!resolved.manifest.feature.ticket.intake);
|
||||||
);
|
assert!(!resolved.manifest.feature.ticket.orchestration_control);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolved.profile.as_ref().unwrap().name.as_deref(),
|
resolved.profile.as_ref().unwrap().name.as_deref(),
|
||||||
Some("default")
|
Some("default")
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,8 @@ enabled = false
|
||||||
|
|
||||||
[feature.ticket]
|
[feature.ticket]
|
||||||
enabled = true
|
enabled = true
|
||||||
preset = "workspace_authoring"
|
authoring = true
|
||||||
|
thread = true
|
||||||
|
|
||||||
[memory]
|
[memory]
|
||||||
extract_threshold = 50000
|
extract_threshold = 50000
|
||||||
|
|
@ -296,7 +297,7 @@ mod tests {
|
||||||
assert!(profile.contains("ref = \"codex-oauth/gpt-5.5\""));
|
assert!(profile.contains("ref = \"codex-oauth/gpt-5.5\""));
|
||||||
assert!(profile.contains("scope = \"workspace_write\""));
|
assert!(profile.contains("scope = \"workspace_write\""));
|
||||||
assert!(
|
assert!(
|
||||||
profile.contains("[feature.ticket]\nenabled = true\npreset = \"workspace_authoring\"")
|
profile.contains("[feature.ticket]\nenabled = true\nauthoring = true\nthread = true")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
use llm_engine::EngineError;
|
use llm_engine::EngineError;
|
||||||
use llm_engine::llm_client::client::LlmClient;
|
use llm_engine::llm_client::client::LlmClient;
|
||||||
use manifest::TicketFeatureAccessConfig;
|
|
||||||
use session_store::WorkerMetadataStore;
|
use session_store::WorkerMetadataStore;
|
||||||
use session_store::{LogEntry, Store};
|
use session_store::{LogEntry, Store};
|
||||||
use ticket::LocalTicketBackend;
|
use ticket::LocalTicketBackend;
|
||||||
|
|
@ -547,9 +546,7 @@ fn install_ticket_event_companion_notify_hook<C, St>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let ticket_feature = &worker.manifest().feature.ticket;
|
let ticket_feature = &worker.manifest().feature.ticket;
|
||||||
if !ticket_feature.enabled
|
if !ticket_feature.enabled || !ticket_feature.orchestration_control {
|
||||||
|| ticket_feature.preset != TicketFeatureAccessConfig::OrchestrationControl
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -675,25 +672,11 @@ where
|
||||||
feature_registry.add_module(task_feature);
|
feature_registry.add_module(task_feature);
|
||||||
}
|
}
|
||||||
if feature_config.ticket.enabled {
|
if feature_config.ticket.enabled {
|
||||||
let ticket_access = match feature_config.ticket.preset {
|
let ticket_access = crate::feature::builtin::ticket::TicketFeatureAccess {
|
||||||
TicketFeatureAccessConfig::ReadOnly => {
|
authoring: feature_config.ticket.authoring,
|
||||||
crate::feature::builtin::ticket::TicketFeatureAccess::ReadOnly
|
thread: feature_config.ticket.thread,
|
||||||
}
|
intake: feature_config.ticket.intake,
|
||||||
TicketFeatureAccessConfig::WorkspaceAuthoring => {
|
orchestration_control: feature_config.ticket.orchestration_control,
|
||||||
crate::feature::builtin::ticket::TicketFeatureAccess::WorkspaceAuthoring
|
|
||||||
}
|
|
||||||
TicketFeatureAccessConfig::Intake => {
|
|
||||||
crate::feature::builtin::ticket::TicketFeatureAccess::Intake
|
|
||||||
}
|
|
||||||
TicketFeatureAccessConfig::OrchestrationControl => {
|
|
||||||
crate::feature::builtin::ticket::TicketFeatureAccess::OrchestrationControl
|
|
||||||
}
|
|
||||||
TicketFeatureAccessConfig::WorkReport => {
|
|
||||||
crate::feature::builtin::ticket::TicketFeatureAccess::WorkReport
|
|
||||||
}
|
|
||||||
TicketFeatureAccessConfig::Review => {
|
|
||||||
crate::feature::builtin::ticket::TicketFeatureAccess::Review
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// Ticket tools are typed operations over the current workspace Ticket backend.
|
// Ticket tools are typed operations over the current workspace Ticket backend.
|
||||||
// Runtime-hosted Workers prefer the workspace API URI carried by the
|
// Runtime-hosted Workers prefer the workspace API URI carried by the
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use ticket::{
|
||||||
TicketIntakeSummary, TicketListQuery, TicketRef, TicketRelation, TicketRelationKind,
|
TicketIntakeSummary, TicketListQuery, TicketRef, TicketRelation, TicketRelationKind,
|
||||||
TicketRelationView, TicketReview, TicketStateChange, TicketSummary,
|
TicketRelationView, TicketReview, TicketStateChange, TicketSummary,
|
||||||
config::{DEFAULT_TICKET_BACKEND_RELATIVE_PATH, TicketConfig},
|
config::{DEFAULT_TICKET_BACKEND_RELATIVE_PATH, TicketConfig},
|
||||||
tool::{TicketToolBackend, ticket_tool_description, ticket_tools},
|
tool::{TICKET_TOOL_NAMES, TicketToolBackend, ticket_tool_description, ticket_tools},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::feature::{
|
use crate::feature::{
|
||||||
|
|
@ -27,23 +27,88 @@ const FEATURE_NAME: &str = "Ticket tools";
|
||||||
const FEATURE_DESCRIPTION: &str = "Typed local Ticket work-item operations over a bounded backend root. \
|
const FEATURE_DESCRIPTION: &str = "Typed local Ticket work-item operations over a bounded backend root. \
|
||||||
The tools operate through the ticket crate backend and do not grant generic filesystem write scope.";
|
The tools operate through the ticket crate backend and do not grant generic filesystem write scope.";
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
pub enum TicketFeatureAccess {
|
pub struct TicketFeatureAccess {
|
||||||
/// Status/diagnostic access for views that must not mutate Tickets.
|
pub authoring: bool,
|
||||||
ReadOnly,
|
pub thread: bool,
|
||||||
/// User/Companion authoring access for shaping current Ticket specs and queueing work.
|
pub intake: bool,
|
||||||
WorkspaceAuthoring,
|
pub orchestration_control: bool,
|
||||||
/// Intake access for creating/refining planning Tickets and marking them ready.
|
|
||||||
Intake,
|
|
||||||
/// Orchestrator control access for state/thread/relation/orchestration operations.
|
|
||||||
OrchestrationControl,
|
|
||||||
/// Coder-style access for reading Tickets and writing implementation reports/comments.
|
|
||||||
WorkReport,
|
|
||||||
/// Reviewer-style access for reading Tickets and writing review/comment thread events.
|
|
||||||
Review,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const READ_ONLY_TOOL_NAMES: [&str; 6] = [
|
impl TicketFeatureAccess {
|
||||||
|
pub const fn read_only() -> Self {
|
||||||
|
Self {
|
||||||
|
authoring: false,
|
||||||
|
thread: false,
|
||||||
|
intake: false,
|
||||||
|
orchestration_control: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn workspace_authoring() -> Self {
|
||||||
|
Self {
|
||||||
|
authoring: true,
|
||||||
|
thread: true,
|
||||||
|
intake: false,
|
||||||
|
orchestration_control: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn intake() -> Self {
|
||||||
|
Self {
|
||||||
|
authoring: true,
|
||||||
|
thread: true,
|
||||||
|
intake: true,
|
||||||
|
orchestration_control: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn orchestration_control() -> Self {
|
||||||
|
Self {
|
||||||
|
authoring: false,
|
||||||
|
thread: true,
|
||||||
|
intake: false,
|
||||||
|
orchestration_control: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn work_report() -> Self {
|
||||||
|
Self {
|
||||||
|
authoring: false,
|
||||||
|
thread: true,
|
||||||
|
intake: false,
|
||||||
|
orchestration_control: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn review() -> Self {
|
||||||
|
Self {
|
||||||
|
authoring: false,
|
||||||
|
thread: true,
|
||||||
|
intake: false,
|
||||||
|
orchestration_control: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tool_names(self) -> Vec<&'static str> {
|
||||||
|
TICKET_TOOL_NAMES
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|name| self.allows_tool(name))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn allows_tool(self, name: &str) -> bool {
|
||||||
|
READ_ONLY_TOOL_NAMES.contains(&name)
|
||||||
|
|| (self.authoring && AUTHORING_TOOL_NAMES.contains(&name))
|
||||||
|
|| (self.thread && THREAD_TOOL_NAMES.contains(&name))
|
||||||
|
|| (self.intake && INTAKE_TOOL_NAMES.contains(&name))
|
||||||
|
|| (self.orchestration_control
|
||||||
|
&& ORCHESTRATION_CONTROL_ADDITIONAL_TOOL_NAMES.contains(&name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const READ_ONLY_TOOL_NAMES: &[&str] = &[
|
||||||
"TicketList",
|
"TicketList",
|
||||||
"TicketShow",
|
"TicketShow",
|
||||||
"TicketDependencyCheck",
|
"TicketDependencyCheck",
|
||||||
|
|
@ -52,7 +117,19 @@ const READ_ONLY_TOOL_NAMES: [&str; 6] = [
|
||||||
"TicketOrchestrationPlanQuery",
|
"TicketOrchestrationPlanQuery",
|
||||||
];
|
];
|
||||||
|
|
||||||
const WORKSPACE_AUTHORING_TOOL_NAMES: [&str; 12] = [
|
const AUTHORING_TOOL_NAMES: &[&str] = &[
|
||||||
|
"TicketCreate",
|
||||||
|
"TicketEditItem",
|
||||||
|
"TicketQueue",
|
||||||
|
"TicketClose",
|
||||||
|
"TicketRelationRecord",
|
||||||
|
];
|
||||||
|
|
||||||
|
const THREAD_TOOL_NAMES: &[&str] = &["TicketComment", "TicketReview"];
|
||||||
|
|
||||||
|
const INTAKE_TOOL_NAMES: &[&str] = &["TicketIntakeReady"];
|
||||||
|
|
||||||
|
const WORKSPACE_AUTHORING_TOOL_NAMES: &[&str] = &[
|
||||||
"TicketCreate",
|
"TicketCreate",
|
||||||
"TicketEditItem",
|
"TicketEditItem",
|
||||||
"TicketList",
|
"TicketList",
|
||||||
|
|
@ -65,22 +142,10 @@ const WORKSPACE_AUTHORING_TOOL_NAMES: [&str; 12] = [
|
||||||
"TicketDoctor",
|
"TicketDoctor",
|
||||||
"TicketRelationRecord",
|
"TicketRelationRecord",
|
||||||
"TicketRelationQuery",
|
"TicketRelationQuery",
|
||||||
|
"TicketOrchestrationPlanQuery",
|
||||||
];
|
];
|
||||||
|
|
||||||
const INTAKE_TOOL_NAMES: [&str; 10] = [
|
const ORCHESTRATION_CONTROL_TOOL_NAMES: &[&str] = &[
|
||||||
"TicketCreate",
|
|
||||||
"TicketEditItem",
|
|
||||||
"TicketList",
|
|
||||||
"TicketShow",
|
|
||||||
"TicketComment",
|
|
||||||
"TicketIntakeReady",
|
|
||||||
"TicketDependencyCheck",
|
|
||||||
"TicketDoctor",
|
|
||||||
"TicketRelationRecord",
|
|
||||||
"TicketRelationQuery",
|
|
||||||
];
|
|
||||||
|
|
||||||
const ORCHESTRATION_CONTROL_TOOL_NAMES: [&str; 12] = [
|
|
||||||
"TicketList",
|
"TicketList",
|
||||||
"TicketShow",
|
"TicketShow",
|
||||||
"TicketComment",
|
"TicketComment",
|
||||||
|
|
@ -95,40 +160,13 @@ const ORCHESTRATION_CONTROL_TOOL_NAMES: [&str; 12] = [
|
||||||
"TicketOrchestrationPlanQuery",
|
"TicketOrchestrationPlanQuery",
|
||||||
];
|
];
|
||||||
|
|
||||||
const WORK_REPORT_TOOL_NAMES: [&str; 7] = [
|
const ORCHESTRATION_CONTROL_ADDITIONAL_TOOL_NAMES: &[&str] = &[
|
||||||
"TicketList",
|
"TicketWorkflowState",
|
||||||
"TicketShow",
|
"TicketClose",
|
||||||
"TicketComment",
|
"TicketRelationRecord",
|
||||||
"TicketDependencyCheck",
|
"TicketOrchestrationPlanRecord",
|
||||||
"TicketDoctor",
|
|
||||||
"TicketRelationQuery",
|
|
||||||
"TicketOrchestrationPlanQuery",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const REVIEW_TOOL_NAMES: [&str; 8] = [
|
|
||||||
"TicketList",
|
|
||||||
"TicketShow",
|
|
||||||
"TicketComment",
|
|
||||||
"TicketReview",
|
|
||||||
"TicketDependencyCheck",
|
|
||||||
"TicketDoctor",
|
|
||||||
"TicketRelationQuery",
|
|
||||||
"TicketOrchestrationPlanQuery",
|
|
||||||
];
|
|
||||||
|
|
||||||
impl TicketFeatureAccess {
|
|
||||||
pub fn tool_names(self) -> &'static [&'static str] {
|
|
||||||
match self {
|
|
||||||
Self::ReadOnly => &READ_ONLY_TOOL_NAMES,
|
|
||||||
Self::WorkspaceAuthoring => &WORKSPACE_AUTHORING_TOOL_NAMES,
|
|
||||||
Self::Intake => &INTAKE_TOOL_NAMES,
|
|
||||||
Self::OrchestrationControl => &ORCHESTRATION_CONTROL_TOOL_NAMES,
|
|
||||||
Self::WorkReport => &WORK_REPORT_TOOL_NAMES,
|
|
||||||
Self::Review => &REVIEW_TOOL_NAMES,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum TicketFeatureBackend {
|
pub enum TicketFeatureBackend {
|
||||||
Local {
|
Local {
|
||||||
|
|
@ -173,7 +211,7 @@ pub struct TicketFeature {
|
||||||
|
|
||||||
impl TicketFeature {
|
impl TicketFeature {
|
||||||
pub fn new(backend_root: impl Into<PathBuf>) -> Self {
|
pub fn new(backend_root: impl Into<PathBuf>) -> Self {
|
||||||
Self::new_with_access(backend_root, TicketFeatureAccess::WorkspaceAuthoring)
|
Self::new_with_access(backend_root, TicketFeatureAccess::workspace_authoring())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_access(backend_root: impl Into<PathBuf>, access: TicketFeatureAccess) -> Self {
|
pub fn new_with_access(backend_root: impl Into<PathBuf>, access: TicketFeatureAccess) -> Self {
|
||||||
|
|
@ -198,7 +236,7 @@ impl TicketFeature {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_workspace(workspace: impl AsRef<Path>) -> Self {
|
pub fn for_workspace(workspace: impl AsRef<Path>) -> Self {
|
||||||
Self::for_workspace_with_access(workspace, TicketFeatureAccess::WorkspaceAuthoring)
|
Self::for_workspace_with_access(workspace, TicketFeatureAccess::workspace_authoring())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_workspace_with_access(
|
pub fn for_workspace_with_access(
|
||||||
|
|
@ -237,7 +275,7 @@ impl TicketFeature {
|
||||||
self.access
|
self.access
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enabled_tool_names(&self) -> &'static [&'static str] {
|
fn enabled_tool_names(&self) -> Vec<&'static str> {
|
||||||
self.access.tool_names()
|
self.access.tool_names()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,7 +336,7 @@ impl FeatureModule for TicketFeature {
|
||||||
let enabled_tool_names = self.enabled_tool_names();
|
let enabled_tool_names = self.enabled_tool_names();
|
||||||
for name in enabled_tool_names {
|
for name in enabled_tool_names {
|
||||||
descriptor = descriptor.with_tool(ToolDeclaration::new(
|
descriptor = descriptor.with_tool(ToolDeclaration::new(
|
||||||
*name,
|
name,
|
||||||
ticket_tool_description(name, self.record_language.as_deref()),
|
ticket_tool_description(name, self.record_language.as_deref()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -701,9 +739,10 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn read_only_descriptor_declares_only_state_tools() {
|
fn read_only_descriptor_declares_only_state_tools() {
|
||||||
let temp = TempDir::new().unwrap();
|
let temp = TempDir::new().unwrap();
|
||||||
let feature = ticket_tools_feature_with_access(temp.path(), TicketFeatureAccess::ReadOnly);
|
let feature =
|
||||||
|
ticket_tools_feature_with_access(temp.path(), TicketFeatureAccess::read_only());
|
||||||
let descriptor = feature.descriptor();
|
let descriptor = feature.descriptor();
|
||||||
assert_eq!(feature.access(), TicketFeatureAccess::ReadOnly);
|
assert_eq!(feature.access(), TicketFeatureAccess::read_only());
|
||||||
assert_eq!(descriptor.tools.len(), READ_ONLY_TOOL_NAMES.len());
|
assert_eq!(descriptor.tools.len(), READ_ONLY_TOOL_NAMES.len());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
descriptor
|
descriptor
|
||||||
|
|
@ -720,10 +759,13 @@ mod tests {
|
||||||
let temp = TempDir::new().unwrap();
|
let temp = TempDir::new().unwrap();
|
||||||
let feature = ticket_tools_feature_with_access(
|
let feature = ticket_tools_feature_with_access(
|
||||||
temp.path(),
|
temp.path(),
|
||||||
TicketFeatureAccess::OrchestrationControl,
|
TicketFeatureAccess::orchestration_control(),
|
||||||
);
|
);
|
||||||
let descriptor = feature.descriptor();
|
let descriptor = feature.descriptor();
|
||||||
assert_eq!(feature.access(), TicketFeatureAccess::OrchestrationControl);
|
assert_eq!(
|
||||||
|
feature.access(),
|
||||||
|
TicketFeatureAccess::orchestration_control()
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
descriptor
|
descriptor
|
||||||
.tools
|
.tools
|
||||||
|
|
@ -735,11 +777,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn semantic_access_presets_expose_role_capability_surfaces() {
|
fn additive_ticket_capabilities_expose_expected_tool_surfaces() {
|
||||||
let temp = TempDir::new().unwrap();
|
let temp = TempDir::new().unwrap();
|
||||||
|
|
||||||
let workspace_authoring =
|
let workspace_authoring = ticket_tools_feature_with_access(
|
||||||
ticket_tools_feature_with_access(temp.path(), TicketFeatureAccess::WorkspaceAuthoring);
|
temp.path(),
|
||||||
|
TicketFeatureAccess::workspace_authoring(),
|
||||||
|
);
|
||||||
let workspace_descriptor = workspace_authoring.descriptor();
|
let workspace_descriptor = workspace_authoring.descriptor();
|
||||||
let workspace_tools = workspace_descriptor
|
let workspace_tools = workspace_descriptor
|
||||||
.tools
|
.tools
|
||||||
|
|
@ -753,7 +797,7 @@ mod tests {
|
||||||
|
|
||||||
let orchestration = ticket_tools_feature_with_access(
|
let orchestration = ticket_tools_feature_with_access(
|
||||||
temp.path(),
|
temp.path(),
|
||||||
TicketFeatureAccess::OrchestrationControl,
|
TicketFeatureAccess::orchestration_control(),
|
||||||
);
|
);
|
||||||
let orchestration_descriptor = orchestration.descriptor();
|
let orchestration_descriptor = orchestration.descriptor();
|
||||||
let orchestration_tools = orchestration_descriptor
|
let orchestration_tools = orchestration_descriptor
|
||||||
|
|
@ -769,7 +813,7 @@ mod tests {
|
||||||
assert!(!orchestration_tools.contains(&"TicketQueue"));
|
assert!(!orchestration_tools.contains(&"TicketQueue"));
|
||||||
|
|
||||||
let work_report =
|
let work_report =
|
||||||
ticket_tools_feature_with_access(temp.path(), TicketFeatureAccess::WorkReport);
|
ticket_tools_feature_with_access(temp.path(), TicketFeatureAccess::work_report());
|
||||||
let work_report_descriptor = work_report.descriptor();
|
let work_report_descriptor = work_report.descriptor();
|
||||||
let work_report_tools = work_report_descriptor
|
let work_report_tools = work_report_descriptor
|
||||||
.tools
|
.tools
|
||||||
|
|
@ -777,10 +821,10 @@ mod tests {
|
||||||
.map(|tool| tool.name.as_str())
|
.map(|tool| tool.name.as_str())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
assert!(work_report_tools.contains(&"TicketComment"));
|
assert!(work_report_tools.contains(&"TicketComment"));
|
||||||
assert!(!work_report_tools.contains(&"TicketReview"));
|
assert!(work_report_tools.contains(&"TicketReview"));
|
||||||
assert!(!work_report_tools.contains(&"TicketWorkflowState"));
|
assert!(!work_report_tools.contains(&"TicketWorkflowState"));
|
||||||
|
|
||||||
let review = ticket_tools_feature_with_access(temp.path(), TicketFeatureAccess::Review);
|
let review = ticket_tools_feature_with_access(temp.path(), TicketFeatureAccess::review());
|
||||||
let review_descriptor = review.descriptor();
|
let review_descriptor = review.descriptor();
|
||||||
let review_tools = review_descriptor
|
let review_tools = review_descriptor
|
||||||
.tools
|
.tools
|
||||||
|
|
@ -800,7 +844,7 @@ mod tests {
|
||||||
let report = FeatureRegistryBuilder::new()
|
let report = FeatureRegistryBuilder::new()
|
||||||
.with_module(ticket_tools_feature_with_access(
|
.with_module(ticket_tools_feature_with_access(
|
||||||
temp.path(),
|
temp.path(),
|
||||||
TicketFeatureAccess::ReadOnly,
|
TicketFeatureAccess::read_only(),
|
||||||
))
|
))
|
||||||
.install_into_pending(&mut pending_tools, &mut hooks);
|
.install_into_pending(&mut pending_tools, &mut hooks);
|
||||||
|
|
||||||
|
|
@ -833,7 +877,8 @@ language = "Japanese"
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
make_ticket_root(&temp.path().join(DEFAULT_TICKET_BACKEND_RELATIVE_PATH));
|
make_ticket_root(&temp.path().join(DEFAULT_TICKET_BACKEND_RELATIVE_PATH));
|
||||||
let feature = ticket_tools_feature_with_access(temp.path(), TicketFeatureAccess::ReadOnly);
|
let feature =
|
||||||
|
ticket_tools_feature_with_access(temp.path(), TicketFeatureAccess::read_only());
|
||||||
let descriptor = feature.descriptor();
|
let descriptor = feature.descriptor();
|
||||||
let descriptor_description = descriptor
|
let descriptor_description = descriptor
|
||||||
.tools
|
.tools
|
||||||
|
|
@ -867,7 +912,7 @@ language = "Japanese"
|
||||||
let report = FeatureRegistryBuilder::new()
|
let report = FeatureRegistryBuilder::new()
|
||||||
.with_module(ticket_tools_feature_with_access(
|
.with_module(ticket_tools_feature_with_access(
|
||||||
temp.path(),
|
temp.path(),
|
||||||
TicketFeatureAccess::WorkspaceAuthoring,
|
TicketFeatureAccess::workspace_authoring(),
|
||||||
))
|
))
|
||||||
.install_into_pending(&mut pending_tools, &mut hooks);
|
.install_into_pending(&mut pending_tools, &mut hooks);
|
||||||
|
|
||||||
|
|
@ -906,7 +951,7 @@ language = "Japanese"
|
||||||
let report = FeatureRegistryBuilder::new()
|
let report = FeatureRegistryBuilder::new()
|
||||||
.with_module(ticket_tools_feature_with_access(
|
.with_module(ticket_tools_feature_with_access(
|
||||||
temp.path(),
|
temp.path(),
|
||||||
TicketFeatureAccess::WorkspaceAuthoring,
|
TicketFeatureAccess::workspace_authoring(),
|
||||||
))
|
))
|
||||||
.install_into_pending(&mut pending_tools, &mut hooks);
|
.install_into_pending(&mut pending_tools, &mut hooks);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ import "./default.dcdl" // {
|
||||||
memory = { enabled = true; };
|
memory = { enabled = true; };
|
||||||
web = { enabled = true; };
|
web = { enabled = true; };
|
||||||
workers = { enabled = false; };
|
workers = { enabled = false; };
|
||||||
ticket = { enabled = true; preset = "work_report"; };
|
ticket = { enabled = true; thread = true; };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ import "./default.dcdl" // {
|
||||||
memory = { enabled = true; };
|
memory = { enabled = true; };
|
||||||
web = { enabled = true; };
|
web = { enabled = true; };
|
||||||
workers = { enabled = true; };
|
workers = { enabled = true; };
|
||||||
ticket = { enabled = true; preset = "workspace_authoring"; };
|
ticket = { enabled = true; authoring = true; thread = true; };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ feature = {
|
||||||
memory = { enabled = true; };
|
memory = { enabled = true; };
|
||||||
web = { enabled = true; };
|
web = { enabled = true; };
|
||||||
workers = { enabled = true; };
|
workers = { enabled = true; };
|
||||||
ticket = { enabled = true; preset = "workspace_authoring"; };
|
ticket = { enabled = true; authoring = true; thread = true; };
|
||||||
};
|
};
|
||||||
|
|
||||||
memory = {
|
memory = {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ import "./default.dcdl" // {
|
||||||
memory = { enabled = true; };
|
memory = { enabled = true; };
|
||||||
web = { enabled = true; };
|
web = { enabled = true; };
|
||||||
workers = { enabled = false; };
|
workers = { enabled = false; };
|
||||||
ticket = { enabled = true; preset = "intake"; };
|
ticket = { enabled = true; authoring = true; thread = true; intake = true; };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ import "./default.dcdl" // {
|
||||||
memory = { enabled = true; };
|
memory = { enabled = true; };
|
||||||
web = { enabled = true; };
|
web = { enabled = true; };
|
||||||
workers = { enabled = true; };
|
workers = { enabled = true; };
|
||||||
ticket = { enabled = true; preset = "orchestration_control"; };
|
ticket = { enabled = true; thread = true; orchestration_control = true; };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ import "./default.dcdl" // {
|
||||||
memory = { enabled = true; };
|
memory = { enabled = true; };
|
||||||
web = { enabled = true; };
|
web = { enabled = true; };
|
||||||
workers = { enabled = false; };
|
workers = { enabled = false; };
|
||||||
ticket = { enabled = true; preset = "review"; };
|
ticket = { enabled = true; thread = true; };
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user