Bump agent-document-renderer to 1095077 for node-located content diagnostics

Why: agent-document-renderer 1095077 (origin/main 10950774) adds node-located
content diagnostics (required_content_missing, intentionally_empty,
template_placeholder_left, mapping_fallback, signature_data_missing,
mapping_unresolved, mapping_ambiguous), a mapping Required flag, the
intentionally-empty model property, refusal of approved output with missing
required content or trusted signature data, expansion of empty
w:sdtContent so content lands inside content controls, and nested mapped
content controls rendered inside parent slots. Backend pinned 31fbe54.

Evidence: go generate ./internal/docgen and go run
./internal/docgen/internal/genrendereridentity -check ok; go vet ./...
exit 0; gofmt -l on changed Go files empty; go test -count=1 ./... exit 0
(42 packages ok, including renderer integration tests in internal/docgen);
renderer submodule go test -count=1 ./... ok at 10950774. New
TestRendererNodeDiagnosticsPassThroughUnchanged asserts renderer codes,
severities, NodeID and RulePath pass unchanged through the manifest and
DiagnosticError. git diff --cached --check clean.

Impact: Renderer identity and the published pin move to 10950774736b64b5d8e544ce03d45ae5a6ceca7c.
Backend only renders ordinary workspace working/preview documents; approved and
review modes still route to RDM before the renderer runs, so the approved
refusal paths are not reachable from Backend. Typical Backend documents produce
no new diagnostics and their DOCX output is unchanged in the test suite. Node
diagnostics on the failure path are still returned only as DiagnosticError
values and are not added to the operation receipt, which carries error text
only; that contract is unchanged.

Refs: unified renderer plan Tasks 10/13
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
weichen
2026-10-17 05:24:24 +00:00
co-authored by Cursor
parent 1afe164798
commit 935c82276d
3 changed files with 68 additions and 3 deletions
@@ -2,6 +2,6 @@
package docgen
const rendererRevision = "31fbe54fae8bf11be664919ce173da0d68ebfdc9"
const rendererRevision = "10950774736b64b5d8e544ce03d45ae5a6ceca7c"
const RendererIdentity = "agent-document-renderer@" + rendererRevision
+66 -1
View File
@@ -367,7 +367,7 @@ func TestPersistedControlledMetadataRoutesBeforeDefaultProfileLoad(t *testing.T)
}
func TestRendererIdentityMatchesPublishedPin(t *testing.T) {
const want = "agent-document-renderer@31fbe54fae8bf11be664919ce173da0d68ebfdc9"
const want = "agent-document-renderer@10950774736b64b5d8e544ce03d45ae5a6ceca7c"
if RendererIdentity != want {
t.Fatalf("RendererIdentity = %q, want %q", RendererIdentity, want)
}
@@ -502,6 +502,71 @@ func TestGenerateReturnsTypedDiagnosticsForUnsafeMarkdownAndAssets(t *testing.T)
}
}
// TestRendererNodeDiagnosticsPassThroughUnchanged pins the wire contract for
// node-located content diagnostics: the renderer's codes, severities, NodeID
// and RulePath reach the manifest and the typed failure without rewriting.
func TestRendererNodeDiagnosticsPassThroughUnchanged(t *testing.T) {
warnings := []render.Diagnostic{
{Code: "required_content_missing", Severity: render.SeverityWarning, NodeID: "node-7", RulePath: "mappings[2]", Message: "required node node-7 is empty", Suggestion: "add content"},
{Code: "intentionally_empty", Severity: render.SeverityInfo, NodeID: "node-8", RulePath: "mappings[2]", Message: "node node-8 is intentionally empty"},
{Code: "mapping_fallback", Severity: render.SeverityWarning, NodeID: "node-9", Message: "style not defined", Suggestion: "define the style"},
}
deps := defaultGeneratorDependencies()
deps.newRenderer = func(*profile.AssetReader) render.Renderer {
return diagnosticsRenderer{diagnostics: warnings}
}
delivery, err := generate(context.Background(), workspaceRequest([]byte(representativeMarkdown)), deps)
if err != nil {
t.Fatalf("generate with warnings: %v", err)
}
var manifest Manifest
if err := json.Unmarshal(delivery.Manifest.Bytes, &manifest); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(manifest.Diagnostics, warnings) {
t.Fatalf("manifest diagnostics = %#v, want renderer diagnostics %#v", manifest.Diagnostics, warnings)
}
for _, fragment := range []string{`"nodeId": "node-7"`, `"rulePath": "mappings[2]"`, `"code": "required_content_missing"`} {
if !strings.Contains(string(delivery.Manifest.Bytes), fragment) {
t.Errorf("manifest JSON omits %s", fragment)
}
}
refusal := []render.Diagnostic{{Code: "mapping_unresolved", Severity: render.SeverityError, NodeID: "node-3", Message: "node node-3 cannot be mapped"}}
deps.newRenderer = func(*profile.AssetReader) render.Renderer {
return diagnosticsRenderer{diagnostics: refusal, err: errors.New("word: mapping unresolved for node node-3")}
}
_, err = generate(context.Background(), workspaceRequest([]byte(representativeMarkdown)), deps)
var diagnostic *DiagnosticError
if !errors.As(err, &diagnostic) {
t.Fatalf("refusal error %T %v is not a DiagnosticError", err, err)
}
if !reflect.DeepEqual(diagnostic.Diagnostics, refusal) {
t.Fatalf("refusal diagnostics = %#v, want %#v", diagnostic.Diagnostics, refusal)
}
}
// diagnosticsRenderer returns fixed renderer diagnostics without a template, so
// the Backend's carriage of the renderer's diagnostics is tested on its own.
type diagnosticsRenderer struct {
diagnostics []render.Diagnostic
err error
}
func (renderer diagnosticsRenderer) Validate(context.Context, render.Request) ([]render.Diagnostic, error) {
return renderer.diagnostics, renderer.err
}
func (renderer diagnosticsRenderer) Render(context.Context, render.Request) (render.Result, error) {
if renderer.err != nil {
return render.Result{Diagnostics: renderer.diagnostics}, renderer.err
}
return render.Result{
Artifact: []byte("docx"), MIME: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
Diagnostics: renderer.diagnostics,
}, nil
}
type failingRenderer struct{ err error }
func (renderer failingRenderer) Validate(context.Context, render.Request) ([]render.Diagnostic, error) {