chore: merge develop into hare/develop
This commit is contained in:
@@ -108,6 +108,13 @@ pub const BUILTIN_PROFILE_RESOURCES: &[BuiltinProfileResource] = &[
|
||||
description: "Internal Memory consolidation service.",
|
||||
imports: BASE_IMPORT,
|
||||
},
|
||||
BuiltinProfileResource {
|
||||
selector: Some("builtin:subjektiv-memory-consolidation"),
|
||||
path: "profiles/subjektiv-memory-consolidation.dcdl",
|
||||
source: include_str!("../../../resources/profiles/subjektiv-memory-consolidation.dcdl"),
|
||||
description: "Delegated subject Memory consolidation service.",
|
||||
imports: BASE_IMPORT,
|
||||
},
|
||||
];
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
||||
@@ -19,9 +19,10 @@ use crate::{
|
||||
CompactionConfig, EngineManifest, FeatureConfig, FeatureFlagConfig, FileUploadLimits,
|
||||
McpConfig, McpEnvValue, McpStdioCwdPolicy, MemoryConsolidationProfileConfig,
|
||||
MemoryExtractionProfileConfig, MemoryFeatureProfileConfig, MemoryResidentProfileConfig,
|
||||
MergeRequestFeatureConfig, ResolvedMemoryFeatureConfig, ScopeConfig, SessionConfig,
|
||||
SkillsConfig, TicketFeatureConfig, ToolOutputLimits, ToolPermissionConfig, ToolPermissionRule,
|
||||
WebConfig, WorkerFeatureConfig, WorkerManifest, WorkerMeta,
|
||||
MergeRequestFeatureConfig, ResolvedMemoryFeatureConfig, ResolvedSubjektivFeatureConfig,
|
||||
ScopeConfig, SessionConfig, SkillsConfig, SubjektivFeatureProfileConfig, TicketFeatureConfig,
|
||||
ToolOutputLimits, ToolPermissionConfig, ToolPermissionRule, WebConfig, WorkerFeatureConfig,
|
||||
WorkerManifest, WorkerMeta,
|
||||
};
|
||||
|
||||
/// Partial-form Worker manifest. Every field is optional; one or more
|
||||
@@ -76,6 +77,8 @@ pub struct FeatureConfigPartial {
|
||||
#[serde(default)]
|
||||
pub memory: Option<MemoryFeatureConfigPartial>,
|
||||
#[serde(default)]
|
||||
pub subjektiv: Option<SubjektivFeatureConfigPartial>,
|
||||
#[serde(default)]
|
||||
pub web: Option<FeatureFlagConfigPartial>,
|
||||
#[serde(default)]
|
||||
pub image: Option<FeatureFlagConfigPartial>,
|
||||
@@ -104,6 +107,11 @@ impl FeatureConfigPartial {
|
||||
Self {
|
||||
task: merge_option(self.task, other.task, FeatureFlagConfigPartial::merge),
|
||||
memory: merge_option(self.memory, other.memory, MemoryFeatureConfigPartial::merge),
|
||||
subjektiv: merge_option(
|
||||
self.subjektiv,
|
||||
other.subjektiv,
|
||||
SubjektivFeatureConfigPartial::merge,
|
||||
),
|
||||
web: merge_option(self.web, other.web, FeatureFlagConfigPartial::merge),
|
||||
image: merge_option(self.image, other.image, FeatureFlagConfigPartial::merge),
|
||||
sub_worker: merge_option(
|
||||
@@ -227,6 +235,28 @@ pub struct MemoryConsolidationProfileConfigPartial {
|
||||
pub request_enabled: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct SubjektivFeatureConfigPartial {
|
||||
#[serde(default)]
|
||||
pub enabled: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub extraction: Option<MemoryExtractionProfileConfigPartial>,
|
||||
}
|
||||
|
||||
impl SubjektivFeatureConfigPartial {
|
||||
fn merge(self, other: Self) -> Self {
|
||||
Self {
|
||||
enabled: other.enabled.or(self.enabled),
|
||||
extraction: merge_option(
|
||||
self.extraction,
|
||||
other.extraction,
|
||||
MemoryExtractionProfileConfigPartial::merge,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MemoryFeatureConfigPartial {
|
||||
fn merge(self, other: Self) -> Self {
|
||||
Self {
|
||||
@@ -331,6 +361,10 @@ impl From<FeatureConfigPartial> for FeatureConfig {
|
||||
.memory
|
||||
.map(ResolvedMemoryFeatureConfig::from)
|
||||
.unwrap_or_default(),
|
||||
subjektiv: value
|
||||
.subjektiv
|
||||
.map(ResolvedSubjektivFeatureConfig::from)
|
||||
.unwrap_or_default(),
|
||||
web: value.web.map(FeatureFlagConfig::from).unwrap_or_default(),
|
||||
image: value.image.map(FeatureFlagConfig::from).unwrap_or_default(),
|
||||
sub_worker: value
|
||||
@@ -447,6 +481,42 @@ impl From<ResolvedMemoryFeatureConfig> for MemoryFeatureConfigPartial {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SubjektivFeatureConfigPartial> for ResolvedSubjektivFeatureConfig {
|
||||
fn from(value: SubjektivFeatureConfigPartial) -> Self {
|
||||
let extraction = value.extraction.unwrap_or_default();
|
||||
Self {
|
||||
profile: SubjektivFeatureProfileConfig {
|
||||
enabled: value.enabled.unwrap_or_default(),
|
||||
extraction: MemoryExtractionProfileConfig {
|
||||
enabled: extraction.enabled.unwrap_or(true),
|
||||
model: extraction.model,
|
||||
reasoning: extraction.reasoning,
|
||||
threshold: extraction.threshold.or(Some(50_000)),
|
||||
worker_max_turns: extraction
|
||||
.worker_max_turns
|
||||
.or(defaults::MEMORY_EXTRACT_WORKER_MAX_TURNS),
|
||||
},
|
||||
},
|
||||
workspace_settings: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ResolvedSubjektivFeatureConfig> for SubjektivFeatureConfigPartial {
|
||||
fn from(value: ResolvedSubjektivFeatureConfig) -> Self {
|
||||
Self {
|
||||
enabled: Some(value.profile.enabled),
|
||||
extraction: Some(MemoryExtractionProfileConfigPartial {
|
||||
enabled: Some(value.profile.extraction.enabled),
|
||||
model: value.profile.extraction.model,
|
||||
reasoning: value.profile.extraction.reasoning,
|
||||
threshold: value.profile.extraction.threshold,
|
||||
worker_max_turns: value.profile.extraction.worker_max_turns,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TicketFeatureConfigPartial> for TicketFeatureConfig {
|
||||
fn from(value: TicketFeatureConfigPartial) -> Self {
|
||||
Self {
|
||||
@@ -500,6 +570,7 @@ impl From<FeatureConfig> for FeatureConfigPartial {
|
||||
Self {
|
||||
task: Some(value.task.into()),
|
||||
memory: Some(value.memory.into()),
|
||||
subjektiv: Some(value.subjektiv.into()),
|
||||
web: Some(value.web.into()),
|
||||
image: Some(value.image.into()),
|
||||
sub_worker: Some(value.sub_worker.into()),
|
||||
@@ -2490,6 +2561,36 @@ directories = [".claude/skills", ".cursor/skills"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subjektiv_config_is_explicit_and_roundtrips_without_subject_identity() {
|
||||
let config = WorkerManifestConfig::from_toml(
|
||||
r#"
|
||||
[worker]
|
||||
name = "subject-worker"
|
||||
|
||||
[feature.subjektiv]
|
||||
enabled = true
|
||||
|
||||
[feature.subjektiv.extraction]
|
||||
enabled = true
|
||||
threshold = 321
|
||||
worker_max_turns = 7
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let partial = config.feature.subjektiv.unwrap();
|
||||
assert_eq!(partial.enabled, Some(true));
|
||||
assert_eq!(partial.extraction.as_ref().unwrap().threshold, Some(321));
|
||||
|
||||
let resolved = ResolvedSubjektivFeatureConfig::from(partial);
|
||||
assert!(resolved.profile.enabled);
|
||||
assert_eq!(resolved.profile.extraction.threshold, Some(321));
|
||||
assert_eq!(resolved.profile.extraction.worker_max_turns, Some(7));
|
||||
let encoded = serde_json::to_value(SubjektivFeatureConfigPartial::from(resolved)).unwrap();
|
||||
assert!(encoded.get("subject_id").is_none());
|
||||
assert_eq!(encoded["enabled"], true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_preserves_ref() {
|
||||
let lower = WorkerManifestConfig {
|
||||
|
||||
+109
-1
@@ -108,6 +108,11 @@ pub struct FeatureConfig {
|
||||
pub task: FeatureFlagConfig,
|
||||
#[serde(default)]
|
||||
pub memory: ResolvedMemoryFeatureConfig,
|
||||
/// Subject-scoped Memory extraction. Subject identity is resolved by the
|
||||
/// Workspace host from the Worker's keyed singleton lease, never from this
|
||||
/// model-visible manifest.
|
||||
#[serde(default)]
|
||||
pub subjektiv: ResolvedSubjektivFeatureConfig,
|
||||
#[serde(default)]
|
||||
pub web: FeatureFlagConfig,
|
||||
#[serde(default)]
|
||||
@@ -139,6 +144,7 @@ impl Default for FeatureConfig {
|
||||
Self {
|
||||
task: FeatureFlagConfig::disabled(),
|
||||
memory: ResolvedMemoryFeatureConfig::default(),
|
||||
subjektiv: ResolvedSubjektivFeatureConfig::default(),
|
||||
web: FeatureFlagConfig::disabled(),
|
||||
image: FeatureFlagConfig::disabled(),
|
||||
sub_worker: FeatureFlagConfig::disabled(),
|
||||
@@ -350,6 +356,85 @@ impl ResolvedMemoryFeatureConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Profile-owned subjektiv activation and extraction policy. The subject id is
|
||||
/// deliberately absent: the Workspace host binds it from `subjektiv:<id>`
|
||||
/// singleton ownership for each operation.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct SubjektivFeatureProfileConfig {
|
||||
pub enabled: bool,
|
||||
pub extraction: MemoryExtractionProfileConfig,
|
||||
}
|
||||
|
||||
impl Default for SubjektivFeatureProfileConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
extraction: MemoryExtractionProfileConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Immutable subjektiv execution configuration persisted in a resolved Worker
|
||||
/// Manifest. Workspace settings provide only trusted extraction language and
|
||||
/// are not subject identity.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct ResolvedSubjektivFeatureConfig {
|
||||
pub profile: SubjektivFeatureProfileConfig,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub workspace_settings: Option<WorkspaceMemorySettingsSnapshot>,
|
||||
}
|
||||
|
||||
impl ResolvedSubjektivFeatureConfig {
|
||||
pub fn enabled(&self) -> bool {
|
||||
self.profile.enabled
|
||||
}
|
||||
|
||||
/// Returns whether the host attached this profile policy to trusted subject
|
||||
/// authority. Profile enablement without a settings snapshot is inert.
|
||||
pub fn execution_enabled(&self) -> bool {
|
||||
self.profile.enabled && self.workspace_settings.is_some()
|
||||
}
|
||||
|
||||
pub fn bind_workspace_settings(
|
||||
&mut self,
|
||||
settings: WorkspaceMemorySettingsSnapshot,
|
||||
) -> Result<(), &'static str> {
|
||||
if !self.profile.enabled {
|
||||
if self.workspace_settings.is_some() {
|
||||
return Err("disabled subjektiv feature must not carry Workspace settings");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if self.workspace_settings.is_some() {
|
||||
return Err("subjektiv Workspace settings are already bound");
|
||||
}
|
||||
self.workspace_settings = Some(settings);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn workspace_settings(&self) -> Option<WorkspaceMemorySettingsSnapshot> {
|
||||
self.workspace_settings.clone()
|
||||
}
|
||||
|
||||
pub fn validate_execution(&self) -> Result<(), &'static str> {
|
||||
// `profile.enabled` is reusable policy. Only a host-bound settings
|
||||
// snapshot activates subject execution; an ordinary Worker or Internal
|
||||
// SubWorker with the same Profile remains inert.
|
||||
if !self.profile.enabled && self.workspace_settings.is_some() {
|
||||
return Err("disabled subjektiv feature must not carry Workspace settings");
|
||||
}
|
||||
if let Some(settings) = &self.workspace_settings
|
||||
&& (settings.settings_revision == 0
|
||||
|| !is_normalized_workspace_memory_language(&settings.language))
|
||||
{
|
||||
return Err("subjektiv Workspace settings snapshot metadata is invalid");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
|
||||
pub struct TicketFeatureConfig {
|
||||
#[serde(default)]
|
||||
@@ -963,7 +1048,9 @@ impl Default for CompactionConfig {
|
||||
|
||||
impl WorkerManifest {
|
||||
pub fn requires_persisted_execution_snapshot(&self) -> bool {
|
||||
self.profile.is_some() || self.feature.memory.workspace_settings.is_some()
|
||||
self.profile.is_some()
|
||||
|| self.feature.memory.workspace_settings.is_some()
|
||||
|| self.feature.subjektiv.workspace_settings.is_some()
|
||||
}
|
||||
|
||||
/// Parse a manifest from a TOML string.
|
||||
@@ -1638,6 +1725,27 @@ model_id = "claude-sonnet-4-20250514"
|
||||
assert!(disabled.bind_workspace_settings(snapshot).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolved_subjektiv_policy_is_inert_until_trusted_attachment() {
|
||||
let snapshot = WorkspaceMemorySettingsSnapshot {
|
||||
workspace_id: "workspace-1".to_string(),
|
||||
settings_revision: 1,
|
||||
language: "English".to_string(),
|
||||
};
|
||||
let mut enabled = ResolvedSubjektivFeatureConfig::default();
|
||||
enabled.profile.enabled = true;
|
||||
assert!(enabled.validate_execution().is_ok());
|
||||
assert!(!enabled.execution_enabled());
|
||||
enabled.bind_workspace_settings(snapshot.clone()).unwrap();
|
||||
assert!(enabled.validate_execution().is_ok());
|
||||
assert!(enabled.execution_enabled());
|
||||
|
||||
let mut disabled = ResolvedSubjektivFeatureConfig::default();
|
||||
disabled.workspace_settings = Some(snapshot.clone());
|
||||
assert!(disabled.validate_execution().is_err());
|
||||
assert!(disabled.bind_workspace_settings(snapshot).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn current_manifest_rejects_legacy_top_level_memory_authority() {
|
||||
let toml = format!("{MINIMAL_REQUIRED}\n[memory]\nlanguage = \"Japanese\"\n");
|
||||
|
||||
@@ -144,6 +144,7 @@ pub enum WorkspaceAuthorityRequirement {
|
||||
Flow,
|
||||
ManageWorkdir,
|
||||
Memory,
|
||||
Subjektiv,
|
||||
MergeRequest,
|
||||
Objective,
|
||||
Orchestration,
|
||||
@@ -157,6 +158,7 @@ impl fmt::Display for WorkspaceAuthorityRequirement {
|
||||
Self::Flow => formatter.write_str("feature.flow"),
|
||||
Self::ManageWorkdir => formatter.write_str("feature.manage_workdir"),
|
||||
Self::Memory => formatter.write_str("feature.memory"),
|
||||
Self::Subjektiv => formatter.write_str("feature.subjektiv"),
|
||||
Self::MergeRequest => formatter.write_str("feature.merge_request"),
|
||||
Self::Objective => formatter.write_str("feature.objective"),
|
||||
Self::Orchestration => formatter.write_str("feature.orchestration"),
|
||||
@@ -185,6 +187,9 @@ pub fn validate_profile_execution_target(
|
||||
if feature.memory.profile.enabled || feature.memory.profile.staging_tools {
|
||||
requirements.insert(WorkspaceAuthorityRequirement::Memory);
|
||||
}
|
||||
if feature.subjektiv.profile.enabled {
|
||||
requirements.insert(WorkspaceAuthorityRequirement::Subjektiv);
|
||||
}
|
||||
if feature.merge_request.show
|
||||
|| feature.merge_request.open
|
||||
|| feature.merge_request.review
|
||||
@@ -1252,6 +1257,64 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_workspace_profiles_cut_over_to_subjektiv_without_legacy_memory() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let resolve = |name: &str| {
|
||||
ProfileResolver::new()
|
||||
.with_workspace_base(tmp.path())
|
||||
.resolve(
|
||||
&ProfileSelector::source_named(ProfileRegistrySource::Builtin, name),
|
||||
ProfileResolveOptions::with_worker_name(format!("{name}-worker")),
|
||||
)
|
||||
.unwrap()
|
||||
.manifest
|
||||
};
|
||||
|
||||
for name in ["coder", "companion", "intake", "reviewer", "orchestrator"] {
|
||||
let manifest = resolve(name);
|
||||
assert!(
|
||||
manifest.feature.subjektiv.profile.enabled,
|
||||
"{name} must enable subjektiv"
|
||||
);
|
||||
assert!(
|
||||
manifest.feature.subjektiv.profile.extraction.enabled,
|
||||
"{name} must enable subject extraction"
|
||||
);
|
||||
assert!(
|
||||
!manifest.feature.memory.profile.enabled,
|
||||
"{name} must not enable legacy Workspace Memory"
|
||||
);
|
||||
}
|
||||
|
||||
for name in ["default", "standalone", "backend-job"] {
|
||||
let manifest = resolve(name);
|
||||
assert!(!manifest.feature.memory.profile.enabled, "{name}");
|
||||
assert!(!manifest.feature.subjektiv.profile.enabled, "{name}");
|
||||
}
|
||||
|
||||
let legacy_consolidator = resolve("memory-consolidation");
|
||||
assert!(legacy_consolidator.feature.memory.profile.enabled);
|
||||
assert!(legacy_consolidator.feature.memory.profile.staging_tools);
|
||||
assert!(
|
||||
legacy_consolidator
|
||||
.feature
|
||||
.memory
|
||||
.profile
|
||||
.extraction
|
||||
.enabled
|
||||
);
|
||||
assert!(
|
||||
legacy_consolidator
|
||||
.feature
|
||||
.memory
|
||||
.profile
|
||||
.consolidation
|
||||
.request_enabled
|
||||
);
|
||||
assert!(!legacy_consolidator.feature.subjektiv.profile.enabled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builtin_profiles_pin_role_models_and_reasoning() {
|
||||
use crate::model::{ReasoningControl, ReasoningEffort};
|
||||
@@ -1276,6 +1339,7 @@ mod tests {
|
||||
"coder",
|
||||
"reviewer",
|
||||
"memory-consolidation",
|
||||
"subjektiv-memory-consolidation",
|
||||
] {
|
||||
let role = resolve(name);
|
||||
assert_eq!(
|
||||
@@ -1308,7 +1372,7 @@ mod tests {
|
||||
role.manifest.engine.reasoning,
|
||||
Some(ReasoningControl::Effort(ReasoningEffort::High))
|
||||
);
|
||||
let extraction = &role.manifest.feature.memory.profile.extraction;
|
||||
let extraction = &role.manifest.feature.subjektiv.profile.extraction;
|
||||
assert_eq!(
|
||||
extraction
|
||||
.model
|
||||
@@ -1322,14 +1386,63 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
let consolidation = resolve("memory-consolidation");
|
||||
for name in ["memory-consolidation", "subjektiv-memory-consolidation"] {
|
||||
let consolidation = resolve(name);
|
||||
assert_eq!(
|
||||
consolidation.manifest.model.ref_.as_deref(),
|
||||
Some("codex-oauth/gpt-5.6-luna")
|
||||
);
|
||||
assert_eq!(
|
||||
consolidation.manifest.engine.reasoning,
|
||||
Some(ReasoningControl::Effort(ReasoningEffort::Medium))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subjektiv_memory_consolidation_profile_is_lifecycle_and_capability_isolated() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let resolved = ProfileResolver::new()
|
||||
.with_workspace_base(tmp.path())
|
||||
.resolve(
|
||||
&ProfileSelector::source_named(
|
||||
ProfileRegistrySource::Builtin,
|
||||
"subjektiv-memory-consolidation",
|
||||
),
|
||||
ProfileResolveOptions::with_worker_name("subject-consolidator"),
|
||||
)
|
||||
.unwrap();
|
||||
let manifest = resolved.manifest;
|
||||
let feature = &manifest.feature;
|
||||
|
||||
assert!(!feature.memory.profile.enabled);
|
||||
assert!(!feature.memory.profile.staging_tools);
|
||||
assert!(feature.subjektiv.profile.enabled);
|
||||
assert!(!feature.subjektiv.profile.extraction.enabled);
|
||||
assert!(!feature.task.enabled);
|
||||
assert!(!feature.web.enabled);
|
||||
assert!(!feature.image.enabled);
|
||||
assert!(!feature.sub_worker.enabled);
|
||||
assert!(!feature.flow.enabled);
|
||||
assert!(!feature.worker.enabled);
|
||||
assert!(!feature.worker.direct_spawn);
|
||||
assert!(!feature.workspace_worker_discovery.enabled);
|
||||
assert!(!feature.objective.enabled);
|
||||
assert!(!feature.manage_workdir.enabled);
|
||||
assert!(!feature.ticket.enabled);
|
||||
assert!(!feature.ticket.authoring);
|
||||
assert!(!feature.ticket.thread);
|
||||
assert!(!feature.ticket.intake);
|
||||
assert!(!feature.ticket.workflow);
|
||||
assert!(!feature.merge_request.show);
|
||||
assert!(!feature.merge_request.open);
|
||||
assert!(!feature.merge_request.review);
|
||||
assert!(!feature.merge_request.readiness_check);
|
||||
assert!(!feature.merge_request.complete);
|
||||
assert!(!feature.orchestration.enabled);
|
||||
assert_eq!(
|
||||
consolidation.manifest.model.ref_.as_deref(),
|
||||
Some("codex-oauth/gpt-5.6-luna")
|
||||
);
|
||||
assert_eq!(
|
||||
consolidation.manifest.engine.reasoning,
|
||||
Some(ReasoningControl::Effort(ReasoningEffort::Medium))
|
||||
manifest.web.as_ref().and_then(|web| web.enabled),
|
||||
Some(false)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1477,7 +1590,8 @@ mod tests {
|
||||
panic!("unexpected error: {error}");
|
||||
};
|
||||
assert_eq!(target, ProfileExecutionTarget::Standalone);
|
||||
assert!(requirements.contains(&WorkspaceAuthorityRequirement::Memory));
|
||||
assert!(requirements.contains(&WorkspaceAuthorityRequirement::Subjektiv));
|
||||
assert!(!requirements.contains(&WorkspaceAuthorityRequirement::Memory));
|
||||
assert!(requirements.contains(&WorkspaceAuthorityRequirement::MergeRequest));
|
||||
assert!(requirements.contains(&WorkspaceAuthorityRequirement::Ticket));
|
||||
assert!(!diagnostic.contains(tmp.path().to_string_lossy().as_ref()));
|
||||
|
||||
Reference in New Issue
Block a user