chore: integrate current develop for review

This commit is contained in:
2026-08-26 16:12:48 +09:00
20 changed files with 558 additions and 109 deletions
@@ -50,11 +50,11 @@ struct MergeRequestTool {
kind: Kind,
}
#[derive(Debug, Deserialize, JsonSchema)]
struct ShowInput {
struct TicketInput {
ticket: String,
}
#[derive(Debug, Deserialize, JsonSchema)]
struct OpenInput {
struct OpenMergeRequestInput {
ticket: String,
repository_id: String,
selector_from: String,
@@ -63,7 +63,7 @@ struct OpenInput {
summary: String,
}
#[derive(Debug, Deserialize, JsonSchema)]
struct CompleteInput {
struct CompleteMergeRequestInput {
ticket: String,
operation_id: String,
approval_event_id: String,
@@ -86,7 +86,7 @@ enum MergeResolutionInput {
ConflictsResolved,
}
#[derive(Debug, Deserialize, JsonSchema)]
struct ReviewInput {
struct ReviewMergeRequestInput {
decision: ReviewDecisionInput,
#[serde(default)]
body: String,
@@ -133,19 +133,19 @@ impl Kind {
fn name(self) -> &'static str {
match self {
Self::Show => "MergeRequestShow",
Self::Readiness => "MergeRequestReadinessCheck",
Self::Open => "MergeRequestOpen",
Self::Complete => "MergeRequestComplete",
Self::Review => "MergeRequestReview",
Self::Show => "ShowMergeRequest",
Self::Readiness => "CheckMergeRequestReadiness",
Self::Open => "OpenMergeRequest",
Self::Complete => "CompleteMergeRequest",
Self::Review => "ReviewMergeRequest",
}
}
fn schema(self) -> serde_json::Value {
match self {
Self::Show | Self::Readiness => json!(schemars::schema_for!(ShowInput)),
Self::Open => json!(schemars::schema_for!(OpenInput)),
Self::Complete => json!(schemars::schema_for!(CompleteInput)),
Self::Review => json!(schemars::schema_for!(ReviewInput)),
Self::Show | Self::Readiness => json!(schemars::schema_for!(TicketInput)),
Self::Open => json!(schemars::schema_for!(OpenMergeRequestInput)),
Self::Complete => json!(schemars::schema_for!(CompleteMergeRequestInput)),
Self::Review => json!(schemars::schema_for!(ReviewMergeRequestInput)),
}
}
}
@@ -156,13 +156,13 @@ impl Tool for MergeRequestTool {
ToolError::ExecutionFailed("Merge Request tools require Workspace identity".into())
})?;
if matches!(self.kind, Kind::Show) {
let value: ShowInput = parse(input)?;
let value: TicketInput = parse(input)?;
nonempty(&value.ticket)?;
return self.show_current_merge_request(ws, &value.ticket);
}
let (method, path, body) = match self.kind {
Kind::Readiness => {
let v: ShowInput = parse(input)?;
let v: TicketInput = parse(input)?;
nonempty(&v.ticket)?;
(
WorkspaceRequestMethod::Get,
@@ -170,9 +170,9 @@ impl Tool for MergeRequestTool {
None,
)
}
Kind::Show => unreachable!("MergeRequestShow is handled above"),
Kind::Show => unreachable!("ShowMergeRequest is handled above"),
Kind::Open => {
let v: OpenInput = parse(input)?;
let v: OpenMergeRequestInput = parse(input)?;
nonempty(&v.ticket)?;
(
WorkspaceRequestMethod::Post,
@@ -183,7 +183,7 @@ impl Tool for MergeRequestTool {
)
}
Kind::Complete => {
let v: CompleteInput = parse(input)?;
let v: CompleteMergeRequestInput = parse(input)?;
nonempty(&v.ticket)?;
(
WorkspaceRequestMethod::Post,
@@ -194,7 +194,7 @@ impl Tool for MergeRequestTool {
)
}
Kind::Review => {
let v: ReviewInput = parse(input)?;
let v: ReviewMergeRequestInput = parse(input)?;
let ctx = self.client.reviewer_context().ok_or_else(|| {
ToolError::ExecutionFailed(
"Review submit requires injected Reviewer capability".into(),
@@ -397,19 +397,21 @@ impl FeatureModule for MergeRequestFeature {
pub fn description(n: &str) -> Option<&'static str> {
match n {
"MergeRequestShow" => Some("Read the selector-based Merge Request and append-only thread."),
"MergeRequestReadinessCheck" => {
Some("Resolve current provider refs and derive readiness from valid review events.")
}
"MergeRequestOpen" => {
Some("Open a Merge Request with immutable source and target selectors.")
}
"MergeRequestComplete" => {
Some("Complete using an approved review event and final target-ref evidence.")
}
"MergeRequestReview" => {
Some("Submit the injected Reviewer capability result for its captured subject ref.")
}
"ShowMergeRequest" => Some(
"Read the selector-based Merge Request, append-only thread, source-review freshness, and target-integration evidence before review, fix, or handoff decisions.",
),
"CheckMergeRequestReadiness" => Some(
"Resolve current provider refs and derive readiness from exact-source review evidence; source movement requires fresh review while target-only movement preserves unchanged-source approval.",
),
"OpenMergeRequest" => Some(
"Open the Ticket's one Merge Request with immutable source and target selectors; reuse it and advance only selector_from with a normal non-force push for later fixes.",
),
"CompleteMergeRequest" => Some(
"Record Orchestrator-owned integration using unchanged-source approval and refreshed final target-ref evidence.",
),
"ReviewMergeRequest" => Some(
"Submit the injected Reviewer capability result for its captured exact source ref; source movement cancels it, while target-only movement does not.",
),
_ => None,
}
}
@@ -469,6 +471,31 @@ mod tests {
}
}
#[test]
fn model_facing_operations_use_only_verb_first_names() {
for name in [
"ShowMergeRequest",
"CheckMergeRequestReadiness",
"OpenMergeRequest",
"CompleteMergeRequest",
"ReviewMergeRequest",
] {
assert!(description(name).is_some(), "missing operation {name}");
}
for legacy in [
"MergeRequestShow",
"MergeRequestReadinessCheck",
"MergeRequestOpen",
"MergeRequestComplete",
"MergeRequestReview",
] {
assert!(
description(legacy).is_none(),
"legacy alias {legacy} must not remain registered"
);
}
}
#[tokio::test]
async fn show_resolves_ticket_projection_then_reads_canonical_resource() {
let client = Arc::new(RecordingWorkspaceClient::new(vec![
@@ -546,7 +573,7 @@ mod tests {
open: true,
..Default::default()
};
assert_eq!(tool_names(coder), ["MergeRequestShow", "MergeRequestOpen"]);
assert_eq!(tool_names(coder), ["ShowMergeRequest", "OpenMergeRequest"]);
let reviewer = MergeRequestFeatureConfig {
show: true,
@@ -555,7 +582,7 @@ mod tests {
};
assert_eq!(
tool_names(reviewer),
["MergeRequestShow", "MergeRequestReview"]
["ShowMergeRequest", "ReviewMergeRequest"]
);
let orchestrator = MergeRequestFeatureConfig {
@@ -567,9 +594,9 @@ mod tests {
assert_eq!(
tool_names(orchestrator),
[
"MergeRequestShow",
"MergeRequestReadinessCheck",
"MergeRequestComplete"
"ShowMergeRequest",
"CheckMergeRequestReadiness",
"CompleteMergeRequest"
]
);
assert_eq!(install(coder).1, [FEATURE_PROMPT_REF]);
@@ -581,8 +608,8 @@ mod tests {
#[test]
fn schemas_hide_revision_and_commit_authority() {
let schemas = [
schemars::schema_for!(OpenInput),
schemars::schema_for!(CompleteInput),
schemars::schema_for!(OpenMergeRequestInput),
schemars::schema_for!(CompleteMergeRequestInput),
];
for s in schemas {
let j = serde_json::to_string(&s).unwrap();
+41
View File
@@ -609,6 +609,47 @@ mod tests {
assert!(!prompt.contains("use the Ticket repository `origin` transport"));
}
#[test]
fn merge_request_prompts_use_selector_refs_and_verb_first_operations() {
let catalog = PromptCatalog::builtins_only().unwrap();
let templates = &catalog.projection.templates;
let common = &templates["common.merge_request"];
let coder = &templates["role.coder"];
let orchestrator = &templates["role.orchestrator"];
let reviewer = &templates["role.reviewer"];
let combined = format!("{common}\n{coder}\n{orchestrator}\n{reviewer}");
for operation in [
"OpenMergeRequest",
"ShowMergeRequest",
"ReviewMergeRequest",
"CheckMergeRequestReadiness",
"CompleteMergeRequest",
] {
assert!(combined.contains(operation), "missing {operation}");
}
for stale_operation in [
"MergeRequestOpen",
"MergeRequestShow",
"MergeRequestReview",
"MergeRequestReadinessCheck",
"MergeRequestComplete",
"MergeRequestAddRevision",
] {
assert!(
!combined.contains(stale_operation),
"stale operation {stale_operation} remains in prompt authority"
);
}
assert!(common.contains("selector_from"));
assert!(common.contains("normal non-force push"));
assert!(common.contains("Moving only the target ref does not invalidate approval"));
assert!(common.contains("take precedence over stale Memory"));
assert!(coder.contains("Never invent an add-revision operation"));
assert!(orchestrator.contains("Target-only movement preserves source approval"));
assert!(reviewer.contains("target-only movement does not invalidate approval"));
}
#[test]
fn graph_rejects_dynamic_legacy_missing_and_cycles() {
let invalid = BTreeMap::from([