RC-4 nach umfangreichen Updates
release-tag / release-image (push) Successful in 6m37s

This commit is contained in:
2026-07-21 16:28:24 +02:00
parent 52c1c204ff
commit 862f86dc3e
18 changed files with 558 additions and 51 deletions
+20 -13
View File
@@ -71,10 +71,14 @@ type ParseOptions struct {
}
var Presets = map[string]Preset{
"no-ai": {ID: "no-ai", Extent: "none", Review: "editorial"},
"research": {ID: "research", Extent: "assisted", Activities: []string{"research"}, Review: "editorial"},
"summary": {ID: "summary", Extent: "assisted", Activities: []string{"summarisation"}, Review: "editorial"},
"full": {ID: "full", Extent: "full", Activities: []string{"generation"}, Review: "editorial"},
// Human review is deliberately not presumed by presets. Under Article 50(4)
// AI Act, substantive human review/editorial control can affect whether
// certain public-interest text must be labelled. The factual review process
// must therefore be selected explicitly by the publisher.
"no-ai": {ID: "no-ai", Extent: "none", Review: "none"},
"research": {ID: "research", Extent: "assisted", Activities: []string{"research"}, Review: "none"},
"summary": {ID: "summary", Extent: "assisted", Activities: []string{"summarisation"}, Review: "none"},
"full": {ID: "full", Extent: "full", Activities: []string{"generation"}, Review: "none"},
}
var validExtents = map[string]bool{"none": true, "assisted": true, "partial": true, "mostly": true, "full": true}
@@ -134,7 +138,8 @@ func NewFromQueryWithOptions(values url.Values, contextURL string, options Parse
extent = "assisted"
}
if review == "" {
review = "editorial"
// Do not infer a legally relevant human-review process from AI usage alone.
review = "none"
}
assurance := clean(values.Get("assurance"), 32)
if assurance == "" {
@@ -150,11 +155,8 @@ func NewFromQueryWithOptions(values url.Values, contextURL string, options Parse
}
componentReview := clean(values.Get(name+"Review"), 24)
if componentReview == "" {
if componentExtent == "none" {
componentReview = "none"
} else {
componentReview = "editorial"
}
// Human review must be stated explicitly; never infer editorial control.
componentReview = "none"
}
componentActivities := splitCSV(values.Get(name + "Activities"))
if len(componentActivities) == 0 {
@@ -252,9 +254,14 @@ func Validate(d Declaration) error {
problems = append(problems, "declaredAt must be RFC3339")
}
}
if d.EditorialResponsibility != nil && d.EditorialResponsibility.URL != "" {
if u, err := url.ParseRequestURI(d.EditorialResponsibility.URL); err != nil || u.Scheme == "" || u.Host == "" {
problems = append(problems, "editorialResponsibility.url must be an absolute URL")
if d.EditorialResponsibility != nil {
if strings.TrimSpace(d.EditorialResponsibility.Name) == "" {
problems = append(problems, "editorialResponsibility.name is required when editorialResponsibility is present")
}
if d.EditorialResponsibility.URL != "" {
if u, err := url.ParseRequestURI(d.EditorialResponsibility.URL); err != nil || u.Scheme == "" || u.Host == "" {
problems = append(problems, "editorialResponsibility.url must be an absolute URL")
}
}
}
for name, c := range d.Components {
+16 -2
View File
@@ -3,6 +3,7 @@ package declaration
import (
"errors"
"net/url"
"strings"
"testing"
)
@@ -19,6 +20,9 @@ func TestPresetQuery(t *testing.T) {
if d.Language != "fr" {
t.Fatalf("unexpected language: %s", d.Language)
}
if c.HumanReview != "none" {
t.Fatalf("preset inferred human review=%q", c.HumanReview)
}
}
func TestNoAIRejectsActivities(t *testing.T) {
@@ -72,8 +76,8 @@ func TestArticleDeclarationFromQuery(t *testing.T) {
if d.Components["text"].HumanReview != "none" {
t.Fatalf("no-AI component review=%q, want none", d.Components["text"].HumanReview)
}
if d.Components["research"].HumanReview != "editorial" {
t.Fatalf("AI-assisted component review=%q, want editorial default", d.Components["research"].HumanReview)
if d.Components["research"].HumanReview != "none" {
t.Fatalf("AI-assisted component review=%q, want no inferred review", d.Components["research"].HumanReview)
}
if got := d.Components["research"].Activities; len(got) != 1 || got[0] != "research" {
t.Fatalf("research activities=%v", got)
@@ -112,3 +116,13 @@ func TestAssuranceFromQuery(t *testing.T) {
}
}
}
func TestEditorialResponsibilityRequiresName(t *testing.T) {
_, err := NewFromQuery(url.Values{
"preset": {"research"},
"responsibleUrl": {"https://example.org/impressum"},
}, "https://example.org/context")
if err == nil || !strings.Contains(err.Error(), "editorialResponsibility.name is required") {
t.Fatalf("expected editorial responsibility name validation error, got %v", err)
}
}