merge: integrate orchestration

# Conflicts:
#	crates/flow/src/builtin.rs
#	crates/manifest/src/profile.rs
#	crates/worker/src/prompt/catalog.rs
#	crates/worker/src/prompt/system.rs
#	resources/flows/coder-review.dcdl
#	resources/prompts/role/coder.md
#	resources/prompts/role/orchestrator.md
#	web/workspace/src/lib/workspace/console/worker-console.ui.test.ts
#	web/workspace/src/lib/workspace/styles/tickets.css
#	web/workspace/src/routes/w/[workspaceId]/tickets/+page.svelte
#	web/workspace/src/routes/w/[workspaceId]/tickets/+page.ts
This commit is contained in:
2026-08-18 09:55:27 +09:00
37 changed files with 2964 additions and 727 deletions
+100 -3
View File
@@ -18,9 +18,10 @@ use crate::model::{AuthRef, ModelManifest, ReasoningControl};
use crate::plugin::PluginConfig;
use crate::{
CompactionConfig, EngineManifest, FeatureConfig, FeatureFlagConfig, FileUploadLimits,
McpConfig, McpEnvValue, McpStdioCwdPolicy, MemoryConfig, MemoryFeatureConfig, ScopeConfig,
SessionConfig, SkillsConfig, TicketFeatureConfig, ToolOutputLimits, ToolPermissionConfig,
ToolPermissionRule, WebConfig, WorkerFeatureConfig, WorkerManifest, WorkerMeta,
McpConfig, McpEnvValue, McpStdioCwdPolicy, MemoryConfig, MemoryFeatureConfig,
MergeRequestFeatureConfig, ScopeConfig, SessionConfig, SkillsConfig, TicketFeatureConfig,
ToolOutputLimits, ToolPermissionConfig, ToolPermissionRule, WebConfig, WorkerFeatureConfig,
WorkerManifest, WorkerMeta,
};
/// Partial-form Worker manifest. Every field is optional; one or more
@@ -97,6 +98,8 @@ pub struct FeatureConfigPartial {
#[serde(default)]
pub ticket: Option<TicketFeatureConfigPartial>,
#[serde(default)]
pub merge_request: Option<MergeRequestFeatureConfigPartial>,
#[serde(default)]
pub orchestration: Option<FeatureFlagConfigPartial>,
#[serde(default)]
pub plugins: Option<FeatureFlagConfigPartial>,
@@ -127,6 +130,11 @@ impl FeatureConfigPartial {
FeatureFlagConfigPartial::merge,
),
ticket: merge_option(self.ticket, other.ticket, TicketFeatureConfigPartial::merge),
merge_request: merge_option(
self.merge_request,
other.merge_request,
MergeRequestFeatureConfigPartial::merge,
),
orchestration: merge_option(
self.orchestration,
other.orchestration,
@@ -216,6 +224,28 @@ impl TicketFeatureConfigPartial {
}
}
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
#[serde(default, deny_unknown_fields)]
pub struct MergeRequestFeatureConfigPartial {
pub show: Option<bool>,
pub open: Option<bool>,
pub review: Option<bool>,
pub readiness_check: Option<bool>,
pub complete: Option<bool>,
}
impl MergeRequestFeatureConfigPartial {
fn merge(self, other: Self) -> Self {
Self {
show: other.show.or(self.show),
open: other.open.or(self.open),
review: other.review.or(self.review),
readiness_check: other.readiness_check.or(self.readiness_check),
complete: other.complete.or(self.complete),
}
}
}
impl From<FeatureConfigPartial> for FeatureConfig {
fn from(value: FeatureConfigPartial) -> Self {
Self {
@@ -247,6 +277,10 @@ impl From<FeatureConfigPartial> for FeatureConfig {
.ticket
.map(TicketFeatureConfig::from)
.unwrap_or_default(),
merge_request: value
.merge_request
.map(MergeRequestFeatureConfig::from)
.unwrap_or_default(),
orchestration: value
.orchestration
.map(FeatureFlagConfig::from)
@@ -326,6 +360,30 @@ impl From<TicketFeatureConfig> for TicketFeatureConfigPartial {
}
}
impl From<MergeRequestFeatureConfigPartial> for MergeRequestFeatureConfig {
fn from(value: MergeRequestFeatureConfigPartial) -> Self {
Self {
show: value.show.unwrap_or_default(),
open: value.open.unwrap_or_default(),
review: value.review.unwrap_or_default(),
readiness_check: value.readiness_check.unwrap_or_default(),
complete: value.complete.unwrap_or_default(),
}
}
}
impl From<MergeRequestFeatureConfig> for MergeRequestFeatureConfigPartial {
fn from(value: MergeRequestFeatureConfig) -> Self {
Self {
show: Some(value.show),
open: Some(value.open),
review: Some(value.review),
readiness_check: Some(value.readiness_check),
complete: Some(value.complete),
}
}
}
impl From<FeatureConfig> for FeatureConfigPartial {
fn from(value: FeatureConfig) -> Self {
Self {
@@ -339,6 +397,7 @@ impl From<FeatureConfig> for FeatureConfigPartial {
objective: Some(value.objective.into()),
manage_workdir: Some(value.manage_workdir.into()),
ticket: Some(value.ticket.into()),
merge_request: Some(value.merge_request.into()),
orchestration: Some(value.orchestration.into()),
plugins: Some(value.plugins.into()),
}
@@ -1880,6 +1939,7 @@ worker_max_turns = 7
assert!(!manifest.feature.objective.enabled);
assert!(!manifest.feature.manage_workdir.enabled);
assert!(!manifest.feature.ticket.enabled);
assert!(!manifest.feature.merge_request.any());
}
#[test]
@@ -1899,6 +1959,13 @@ thread = false
intake = false
workflow = false
[feature.merge_request]
show = true
open = false
review = true
readiness_check = false
complete = false
[feature.orchestration]
enabled = false
"#,
@@ -1934,6 +2001,14 @@ enabled = false
assert!(!manifest.feature.ticket.thread);
assert!(!manifest.feature.ticket.intake);
assert!(!manifest.feature.ticket.workflow);
assert_eq!(
manifest.feature.merge_request,
MergeRequestFeatureConfig {
show: true,
review: true,
..Default::default()
}
);
assert!(!manifest.feature.orchestration.enabled);
assert!(!manifest.feature.memory.enabled);
assert!(!manifest.feature.memory.staging);
@@ -1957,6 +2032,13 @@ thread = false
intake = false
workflow = false
[feature.merge_request]
show = true
open = false
review = true
readiness_check = false
complete = false
[feature.orchestration]
enabled = false
"#,
@@ -1968,6 +2050,11 @@ enabled = false
thread = true
workflow = true
[feature.merge_request]
open = true
review = false
readiness_check = true
[feature.orchestration]
enabled = true
@@ -2017,6 +2104,16 @@ enabled = true
assert!(manifest.feature.ticket.thread);
assert!(!manifest.feature.ticket.intake);
assert!(manifest.feature.ticket.workflow);
assert_eq!(
manifest.feature.merge_request,
MergeRequestFeatureConfig {
show: true,
open: true,
review: false,
readiness_check: true,
complete: false,
}
);
assert!(manifest.feature.orchestration.enabled);
assert!(manifest.feature.objective.enabled);
assert!(manifest.feature.web.enabled);
+24
View File
@@ -125,6 +125,8 @@ pub struct FeatureConfig {
#[serde(default)]
pub ticket: TicketFeatureConfig,
#[serde(default)]
pub merge_request: MergeRequestFeatureConfig,
#[serde(default)]
pub orchestration: FeatureFlagConfig,
#[serde(default)]
pub plugins: FeatureFlagConfig,
@@ -143,6 +145,7 @@ impl Default for FeatureConfig {
objective: FeatureFlagConfig::disabled(),
manage_workdir: FeatureFlagConfig::disabled(),
ticket: TicketFeatureConfig::default(),
merge_request: MergeRequestFeatureConfig::default(),
orchestration: FeatureFlagConfig::disabled(),
plugins: FeatureFlagConfig::disabled(),
}
@@ -252,6 +255,27 @@ pub struct TicketFeatureConfig {
pub workflow: bool,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(deny_unknown_fields)]
pub struct MergeRequestFeatureConfig {
#[serde(default)]
pub show: bool,
#[serde(default)]
pub open: bool,
#[serde(default)]
pub review: bool,
#[serde(default)]
pub readiness_check: bool,
#[serde(default)]
pub complete: bool,
}
impl MergeRequestFeatureConfig {
pub fn any(self) -> bool {
self.show || self.open || self.review || self.readiness_check || self.complete
}
}
/// External Agent Skills (`SKILL.md`) ingest configuration. Skills are
/// loaded *only* from the directories listed here — there is no
/// implicit `$config_dir/skills/` or builtin probe. Profile and Manifest
+47
View File
@@ -919,6 +919,37 @@ fn apply_role_profile(
_ => serde_json::json!({ "enabled": true, "authoring": true, "thread": true }),
};
value["feature"]["ticket"] = ticket;
let merge_request = match slug {
"coder" => serde_json::json!({
"show": true,
"open": true,
"review": false,
"readiness_check": false,
"complete": false
}),
"reviewer" => serde_json::json!({
"show": true,
"open": false,
"review": true,
"readiness_check": false,
"complete": false
}),
"orchestrator" => serde_json::json!({
"show": true,
"open": false,
"review": false,
"readiness_check": true,
"complete": true
}),
_ => serde_json::json!({
"show": false,
"open": false,
"review": false,
"readiness_check": false,
"complete": false
}),
};
value["feature"]["merge_request"] = merge_request;
}
fn reject_manifest_shaped_profile(value: &serde_json::Value) -> Result<(), ProfileError> {
@@ -1448,6 +1479,14 @@ enabled = true
authoring = false
thread = false
intake = false
[feature.merge_request]
show = true
open = false
review = true
readiness_check = false
complete = false
[feature.orchestration]
enabled = false
"#,
@@ -1470,6 +1509,14 @@ enabled = false
assert!(!resolved.manifest.feature.ticket.authoring);
assert!(!resolved.manifest.feature.ticket.thread);
assert!(!resolved.manifest.feature.ticket.intake);
assert_eq!(
resolved.manifest.feature.merge_request,
crate::MergeRequestFeatureConfig {
show: true,
review: true,
..Default::default()
}
);
assert!(!resolved.manifest.feature.orchestration.enabled);
assert_eq!(
resolved.manifest.delegation_scope.allow[0].target,