diff --git a/Cargo.lock b/Cargo.lock
index 228c722..dda8f6a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -626,7 +626,7 @@ dependencies = [
[[package]]
name = "now-policy-api"
-version = "0.6.0"
+version = "0.7.0"
dependencies = [
"chrono",
"derive_more",
@@ -641,7 +641,7 @@ dependencies = [
[[package]]
name = "now-policy-server-template"
-version = "0.6.0"
+version = "0.7.0"
dependencies = [
"aide",
"async-trait",
diff --git a/policies/dotnet/Devolutions.Now.Policy.Api/Enums.cs b/policies/dotnet/Devolutions.Now.Policy.Api/Enums.cs
index 5e5525f..6eb29ef 100644
--- a/policies/dotnet/Devolutions.Now.Policy.Api/Enums.cs
+++ b/policies/dotnet/Devolutions.Now.Policy.Api/Enums.cs
@@ -150,7 +150,6 @@ public enum ErrorCode
UnsupportedEndpoint,
MalformedDraft,
InvalidPolicy,
- WarningConfirmationRequired,
Unauthenticated,
AdministratorRequired,
UnsafePolicyPath,
diff --git a/policies/dotnet/Devolutions.Now.Policy.Api/PolicyManagementModels.cs b/policies/dotnet/Devolutions.Now.Policy.Api/PolicyManagementModels.cs
index 61626b1..c1e57b0 100644
--- a/policies/dotnet/Devolutions.Now.Policy.Api/PolicyManagementModels.cs
+++ b/policies/dotnet/Devolutions.Now.Policy.Api/PolicyManagementModels.cs
@@ -359,10 +359,6 @@ public string RequestKind
[JsonRequired]
public PolicyConflictHandling ConflictHandling { get; set; }
- [JsonPropertyName("WarningsAcknowledged")]
- [JsonRequired]
- public bool WarningsAcknowledged { get; set; }
-
/// Raw draft JSON retained for transaction-time reparsing and revalidation.
[JsonPropertyName("Draft")]
[JsonRequired]
diff --git a/policies/dotnet/Devolutions.Now.Policy.Api/README.md b/policies/dotnet/Devolutions.Now.Policy.Api/README.md
index 4ba702d..135dd3b 100644
--- a/policies/dotnet/Devolutions.Now.Policy.Api/README.md
+++ b/policies/dotnet/Devolutions.Now.Policy.Api/README.md
@@ -42,6 +42,9 @@ Opaque policy store tokens and validation receipts are restricted to safe printa
HTTP body of policy validation and replacement requests. It is separate from the package-operation
limit advertised by broker capabilities.
+Policy replacement retains the validation receipt, expected store token, operation, and explicit overwrite-conflict handling.
+Validation warnings are advisory findings for inline client UX and do not require a wire-level acknowledgement to save a valid draft.
+
Because policy documents are JSON-only, configured `.yaml`, `.yml`, extensionless, and other
non-JSON paths use `PolicyReadOnlyReason.UnsupportedFormat` in management snapshots and
`ErrorCode.UnsupportedPolicyFormat` for structured HTTP 422 errors.
diff --git a/policies/dotnet/Devolutions.Now.Policy.Client.Tests/PolicyManagementClientTests.cs b/policies/dotnet/Devolutions.Now.Policy.Client.Tests/PolicyManagementClientTests.cs
index fdf2e06..92d721c 100644
--- a/policies/dotnet/Devolutions.Now.Policy.Client.Tests/PolicyManagementClientTests.cs
+++ b/policies/dotnet/Devolutions.Now.Policy.Client.Tests/PolicyManagementClientTests.cs
@@ -82,6 +82,17 @@ public async Task ReplacePolicy_sends_every_operation_intent(
Assert.Equal("store:active:8", response.Management.StoreToken);
}
+ [Fact]
+ public async Task PolicyReplacement_rejects_legacy_warning_acknowledgement()
+ {
+ var request = JsonNode.Parse(await ReadFixture("requests", "policy-replacement.update.request.json"))!;
+ request["WarningsAcknowledged"] = true;
+
+ var json = request.ToJsonString();
+ Assert.Throws(() => BrokerSerializer.DeserializeStrict(json));
+ Assert.NotEmpty((await TestData.SchemaAsync("PolicyReplacementRequest")).Validate(json));
+ }
+
[Fact]
public async Task ReplacePolicy_preserves_structured_stale_token_findings()
{
diff --git a/policies/rust/now-policy-api/Cargo.toml b/policies/rust/now-policy-api/Cargo.toml
index 23b06bc..2e7034f 100644
--- a/policies/rust/now-policy-api/Cargo.toml
+++ b/policies/rust/now-policy-api/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "now-policy-api"
-version = "0.6.0"
+version = "0.7.0"
edition = "2024"
license.workspace = true
homepage.workspace = true
diff --git a/policies/rust/now-policy-api/README.md b/policies/rust/now-policy-api/README.md
index 66734f9..dd69782 100644
--- a/policies/rust/now-policy-api/README.md
+++ b/policies/rust/now-policy-api/README.md
@@ -61,6 +61,9 @@ The generated document contains the unchanged policy inspection route, the manag
Opaque store tokens and validation receipts use safe printable ASCII (`A-Z`, `a-z`, `0-9`, `.`, `_`, `~`, `:`, `-`) and begin with an ASCII alphanumeric character. This keeps length and validation behavior identical across Rust UTF-8 and .NET UTF-16 implementations.
+Replacement requests retain their validation receipt, expected store token, operation, and explicit overwrite-conflict handling.
+Validation warnings are advisory findings that clients present inline and never require a protocol-level acknowledgement to save a valid draft.
+
Validation
----------
diff --git a/policies/rust/now-policy-api/openapi/now-policy-api.yaml b/policies/rust/now-policy-api/openapi/now-policy-api.yaml
index 74d8cef..217c3e5 100644
--- a/policies/rust/now-policy-api/openapi/now-policy-api.yaml
+++ b/policies/rust/now-policy-api/openapi/now-policy-api.yaml
@@ -602,7 +602,6 @@ components:
- UnsupportedEndpoint
- MalformedDraft
- InvalidPolicy
- - WarningConfirmationRequired
- Unauthenticated
- AdministratorRequired
- UnsafePolicyPath
@@ -1419,9 +1418,6 @@ components:
$ref: '#/components/schemas/ApiVersion'
ValidationReceipt:
$ref: '#/components/schemas/PolicyValidationReceipt'
- WarningsAcknowledged:
- description: Explicit acknowledgement of every warning bound into the validation receipt.
- type: boolean
additionalProperties: false
required:
- RequestKind
@@ -1429,7 +1425,6 @@ components:
- ExpectedStoreToken
- Operation
- ConflictHandling
- - WarningsAcknowledged
- Draft
- ValidationReceipt
PolicyReplacementRequestKind:
diff --git a/policies/rust/now-policy-api/src/enums.rs b/policies/rust/now-policy-api/src/enums.rs
index ea41109..170c630 100644
--- a/policies/rust/now-policy-api/src/enums.rs
+++ b/policies/rust/now-policy-api/src/enums.rs
@@ -128,7 +128,6 @@ pub enum ErrorCode {
UnsupportedEndpoint,
MalformedDraft,
InvalidPolicy,
- WarningConfirmationRequired,
Unauthenticated,
AdministratorRequired,
UnsafePolicyPath,
diff --git a/policies/rust/now-policy-api/src/management.rs b/policies/rust/now-policy-api/src/management.rs
index 6311d3c..fa17468 100644
--- a/policies/rust/now-policy-api/src/management.rs
+++ b/policies/rust/now-policy-api/src/management.rs
@@ -698,9 +698,6 @@ pub struct PolicyReplacementRequest {
pub operation: PolicyReplacementOperation,
pub conflict_handling: PolicyConflictHandling,
- /// Explicit acknowledgement of every warning bound into the validation receipt.
- pub warnings_acknowledged: bool,
-
/// Raw draft JSON retained for transaction-time reparsing and revalidation.
pub draft: serde_json::Value,
diff --git a/policies/rust/now-policy-server-template/Cargo.toml b/policies/rust/now-policy-server-template/Cargo.toml
index d7f754b..e8383df 100644
--- a/policies/rust/now-policy-server-template/Cargo.toml
+++ b/policies/rust/now-policy-server-template/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "now-policy-server-template"
-version = "0.6.0"
+version = "0.7.0"
edition = "2024"
license.workspace = true
homepage.workspace = true
@@ -17,7 +17,7 @@ workspace = true
aide = { version = "0.15", features = ["axum", "axum-json"] }
async-trait = "0.1"
axum = { version = "0.8", default-features = false, features = ["json"] }
-now-policy-api = { version = "0.6", path = "../now-policy-api" }
+now-policy-api = { version = "0.7", path = "../now-policy-api" }
now-policy = { version = "0.5", path = "../now-policy" }
schemars = "0.9"
serde = { version = "1", features = ["derive"] }
diff --git a/policies/rust/now-policy-server-template/src/server.rs b/policies/rust/now-policy-server-template/src/server.rs
index e8d9f0d..59cd92c 100644
--- a/policies/rust/now-policy-server-template/src/server.rs
+++ b/policies/rust/now-policy-server-template/src/server.rs
@@ -406,10 +406,7 @@ fn error_status(code: ErrorCode) -> StatusCode {
ErrorCode::Unauthorized | ErrorCode::Unauthenticated => StatusCode::UNAUTHORIZED,
ErrorCode::Forbidden | ErrorCode::AdministratorRequired => StatusCode::FORBIDDEN,
ErrorCode::NotFound => StatusCode::NOT_FOUND,
- ErrorCode::Conflict
- | ErrorCode::WarningConfirmationRequired
- | ErrorCode::UnsafePolicyPath
- | ErrorCode::StalePolicyStoreToken => StatusCode::CONFLICT,
+ ErrorCode::Conflict | ErrorCode::UnsafePolicyPath | ErrorCode::StalePolicyStoreToken => StatusCode::CONFLICT,
ErrorCode::PayloadTooLarge => StatusCode::PAYLOAD_TOO_LARGE,
ErrorCode::UnsupportedMediaType => StatusCode::UNSUPPORTED_MEDIA_TYPE,
ErrorCode::ValidationFailed
diff --git a/policies/rust/now-policy-server-template/tests/sample_documents.rs b/policies/rust/now-policy-server-template/tests/sample_documents.rs
index d685834..a4b5404 100644
--- a/policies/rust/now-policy-server-template/tests/sample_documents.rs
+++ b/policies/rust/now-policy-server-template/tests/sample_documents.rs
@@ -136,6 +136,14 @@ fn all_sample_requests_deserialize() {
}
}
+#[test]
+fn policy_replacement_rejects_legacy_warning_acknowledgement() {
+ let mut request = load_json_file(&samples_dir().join("requests/policy-replacement.update.request.json"));
+ request["WarningsAcknowledged"] = true.into();
+
+ assert!(serde_json::from_value::(request).is_err());
+}
+
#[test]
fn all_sample_responses_deserialize() {
for path in json_files(&samples_dir().join("responses")) {
@@ -587,7 +595,6 @@ async fn policy_management_error_codes_use_stable_http_statuses() {
(ErrorCode::UnsupportedEndpoint, StatusCode::NOT_IMPLEMENTED),
(ErrorCode::MalformedDraft, StatusCode::BAD_REQUEST),
(ErrorCode::InvalidPolicy, StatusCode::UNPROCESSABLE_ENTITY),
- (ErrorCode::WarningConfirmationRequired, StatusCode::CONFLICT),
(ErrorCode::Unauthenticated, StatusCode::UNAUTHORIZED),
(ErrorCode::AdministratorRequired, StatusCode::FORBIDDEN),
(ErrorCode::UnsafePolicyPath, StatusCode::CONFLICT),
@@ -745,7 +752,7 @@ async fn policy_management_routes_accept_exact_limit_and_reject_one_byte_over()
(
"PUT",
"/v1/policy",
- r#"{"RequestKind":"PolicyReplacementRequest","RequestVersion":"1.0","ExpectedStoreToken":"store:active:7","Operation":"Update","ConflictHandling":"Reject","WarningsAcknowledged":true,"Draft":{"Padding":""#,
+ r#"{"RequestKind":"PolicyReplacementRequest","RequestVersion":"1.0","ExpectedStoreToken":"store:active:7","Operation":"Update","ConflictHandling":"Reject","Draft":{"Padding":""#,
r#""},"ValidationReceipt":"receipt:sha256:test"}"#,
),
];
diff --git a/policies/test-data/package-broker/requests/policy-replacement.create.request.json b/policies/test-data/package-broker/requests/policy-replacement.create.request.json
index d1b59d8..f90ce5e 100644
--- a/policies/test-data/package-broker/requests/policy-replacement.create.request.json
+++ b/policies/test-data/package-broker/requests/policy-replacement.create.request.json
@@ -4,7 +4,6 @@
"ExpectedStoreToken": "store:missing:0",
"Operation": "Create",
"ConflictHandling": "Reject",
- "WarningsAcknowledged": false,
"Draft": {
"PolicyFormatVersion": "1.0.0",
"Metadata": { "Id": "contoso.package-policy", "Publisher": "Contoso IT" },
diff --git a/policies/test-data/package-broker/requests/policy-replacement.overwrite.request.json b/policies/test-data/package-broker/requests/policy-replacement.overwrite.request.json
index 2ee71f9..f932559 100644
--- a/policies/test-data/package-broker/requests/policy-replacement.overwrite.request.json
+++ b/policies/test-data/package-broker/requests/policy-replacement.overwrite.request.json
@@ -4,7 +4,6 @@
"ExpectedStoreToken": "store:active:newly-observed-8",
"Operation": "Update",
"ConflictHandling": "ConfirmOverwrite",
- "WarningsAcknowledged": true,
"Draft": {
"PolicyFormatVersion": "1.0.0",
"Metadata": { "Id": "contoso.package-policy", "Publisher": "Contoso IT" },
diff --git a/policies/test-data/package-broker/requests/policy-replacement.repair.request.json b/policies/test-data/package-broker/requests/policy-replacement.repair.request.json
index bc5c6bd..4df8202 100644
--- a/policies/test-data/package-broker/requests/policy-replacement.repair.request.json
+++ b/policies/test-data/package-broker/requests/policy-replacement.repair.request.json
@@ -4,7 +4,6 @@
"ExpectedStoreToken": "store:invalid:4",
"Operation": "Repair",
"ConflictHandling": "Reject",
- "WarningsAcknowledged": false,
"Draft": {
"PolicyFormatVersion": "1.0.0",
"Metadata": { "Id": "contoso.package-policy", "Publisher": "Contoso IT" },
diff --git a/policies/test-data/package-broker/requests/policy-replacement.replace-identity.request.json b/policies/test-data/package-broker/requests/policy-replacement.replace-identity.request.json
index a3e636e..721606e 100644
--- a/policies/test-data/package-broker/requests/policy-replacement.replace-identity.request.json
+++ b/policies/test-data/package-broker/requests/policy-replacement.replace-identity.request.json
@@ -4,7 +4,6 @@
"ExpectedStoreToken": "store:active:7",
"Operation": "ReplaceIdentity",
"ConflictHandling": "Reject",
- "WarningsAcknowledged": false,
"Draft": {
"PolicyFormatVersion": "1.0.0",
"Metadata": { "Id": "fabrikam.package-policy", "Publisher": "Fabrikam IT" },
diff --git a/policies/test-data/package-broker/requests/policy-replacement.update.request.json b/policies/test-data/package-broker/requests/policy-replacement.update.request.json
index 42f4310..4febb09 100644
--- a/policies/test-data/package-broker/requests/policy-replacement.update.request.json
+++ b/policies/test-data/package-broker/requests/policy-replacement.update.request.json
@@ -4,7 +4,6 @@
"ExpectedStoreToken": "store:active:7",
"Operation": "Update",
"ConflictHandling": "Reject",
- "WarningsAcknowledged": true,
"Draft": {
"PolicyFormatVersion": "1.0.0",
"Metadata": { "Id": "contoso.package-policy", "Publisher": "Contoso IT" },