Files
weichenandCursor c34279ad55
governance / governance (pull_request) Successful in 16s
ci / vet-and-test (pull_request) Successful in 1m2s
Bump the renderer to 3f25999 and bind documents to published profile versions
Why: Agent-Document-Renderer main publishes company.default@1.1.0 with a
declarative TOC (spec 04, acceptance 10 §4 §7) and keeps 1.0.0 loadable by
version. The fixed defaultProfileVersion "1.0.0" no longer matched
LoadCompanyDefaultContext (1.1.0), so every delivery failed with a profile
mismatch. New documents must take the latest published version, while
documents that already bind a published version keep rendering with exactly
that version, because published rules are immutable and old versions remain
retrievable (spec 08).

Evidence: go vet ./... ok; go test -count=1 ./... ok (all packages);
go run ./internal/docgen/internal/genrendereridentity -check ok;
scripts/agent-exec validate --repo . ok; scripts/dm governance check --repo .
ok; gofmt -l clean; git diff --cached --check clean. New tests cover the
default binding (1.1.0, TOC present), pinned 1.0.0 (no TOC) and 1.1.0 for
both a request pin and a persisted-Markdown replay, and refusal of unknown
versions and other profile IDs before any profile load.

Impact: docgen resolves the profile version from the persisted Markdown
binding or the request, defaults new documents to
profiles.LatestCompanyDefaultVersion, and loads it through
LoadCompanyDefaultVersionContext. The legacy migration comparison records the
declared TOC as an intentional corporate-layout difference
(RDR-T10-CMP-002) and keeps it out of the body visible-text dimension.
RendererIdentity is regenerated for 3f259990b5d4.

Refs: unified renderer plan Task 13; renderer PR #7
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-18 06:24:36 +00:00

580 lines
21 KiB
Go

package docgen
import (
"archive/zip"
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"slices"
"sort"
"strings"
"time"
markdownrenderer "agent-document-renderer/markdown"
"agent-document-renderer/model"
"agent-document-renderer/profile"
"agent-document-renderer/profiles"
"agent-document-renderer/render"
"agent-document-renderer/word"
)
const (
WorkspaceProjectID = "workspace"
ManifestSchemaVersion = "backend-document-delivery/v1"
AdapterIdentity = RendererIdentity + "/word-adapter-v1"
defaultProfileID = "company.default"
defaultRenderCapacity = 4
)
var renderCapacity = make(chan struct{}, defaultRenderCapacity)
var (
ErrRDMRequired = errors.New("docgen: controlled or project document requires RDM")
ErrProfileMismatch = errors.New("docgen: organization-default profile mismatch")
ErrResourceLimit = errors.New("docgen: resource limit exceeded")
)
type Artifact struct {
Name string
MIME string
SHA256 string
Bytes []byte
}
type Request struct {
DocumentID string
Title string
DocumentType string
Language string
Markdown []byte
Mode render.Mode
FrozenAt time.Time
RequestedBy string
ProfileID string
ProfileVersion string
Controlled bool
ProjectDocument bool
LegacySource []byte
Limits Limits
}
type Limits struct {
MaxMarkdownBytes int
MaxNodes int
MaxDepth int
}
type DiagnosticError struct {
Stage string
Code string
Diagnostics []render.Diagnostic
Err error
}
func (failure *DiagnosticError) Error() string {
if failure == nil {
return "docgen: diagnostic failure"
}
return fmt.Sprintf("docgen: %s: %s: %v", failure.Stage, failure.Code, failure.Err)
}
func (failure *DiagnosticError) Unwrap() error {
if failure == nil {
return nil
}
return failure.Err
}
type ComparisonDimension struct {
Name string `json:"name"`
Status string `json:"status"`
LegacyHash string `json:"legacyHash,omitempty"`
SharedHash string `json:"sharedHash,omitempty"`
ExceptionID string `json:"exceptionId,omitempty"`
Detail string `json:"detail,omitempty"`
}
type ComparisonReceipt struct {
Enabled bool `json:"enabled"`
Assessment string `json:"assessment,omitempty"`
LegacySHA256 string `json:"legacySha256,omitempty"`
SharedSHA256 string `json:"sharedSha256,omitempty"`
Dimensions []ComparisonDimension `json:"dimensions,omitempty"`
IntentionalDifferences []string `json:"intentionalDifferences,omitempty"`
AccidentalDifferences []string `json:"accidentalDifferences,omitempty"`
Unassessed []string `json:"unassessed,omitempty"`
}
type RenderOptions struct {
MigrationComparison bool
}
type ManifestArtifact struct {
Name string `json:"name"`
MIME string `json:"mime"`
Size int `json:"size"`
SHA256 string `json:"sha256"`
}
type ProfileReceipt struct {
ProfileID string `json:"profileId"`
Version string `json:"version"`
SnapshotHash string `json:"snapshotHash"`
Candidate bool `json:"candidate"`
ReviewStatus string `json:"reviewStatus"`
PublicationAllowed bool `json:"publicationAllowed"`
Unverified []string `json:"unverified"`
ProfileSHA256 string `json:"profileSha256"`
AssetManifestSHA256 string `json:"assetManifestSha256"`
ReviewSHA256 string `json:"reviewSha256"`
}
type Manifest struct {
SchemaVersion string `json:"schemaVersion"`
RendererIdentity string `json:"rendererIdentity"`
AdapterIdentity string `json:"adapterIdentity"`
SelectionSource string `json:"selectionSource"`
Profile ProfileReceipt `json:"profile"`
DocumentRootID string `json:"documentRootId"`
Artifacts []ManifestArtifact `json:"artifacts"`
Renderer render.Manifest `json:"renderer"`
Diagnostics []render.Diagnostic `json:"diagnostics,omitempty"`
NodeAnchors map[string]string `json:"nodeAnchors"`
Comparison ComparisonReceipt `json:"comparison"`
}
type Delivery struct {
Document model.CanonicalDocument
Profile ProfileReceipt
Renderer render.Manifest
Markdown Artifact
DOCX Artifact
Manifest Artifact
Archive Artifact
Comparison ComparisonReceipt
}
type PreparedDocument struct {
Document model.CanonicalDocument
Markdown Artifact
CanonicalHash string
}
// Prepare canonicalizes and validates authoritative Markdown without loading a
// profile or invoking the Word adapter. Durable callers persist its Markdown
// artifact before any rendering work.
func Prepare(ctx context.Context, request Request) (PreparedDocument, error) {
if err := contextErr(ctx); err != nil {
return PreparedDocument{}, err
}
if request.Controlled || request.ProjectDocument || request.Mode == render.ModeReview || request.Mode == render.ModeApproved {
return PreparedDocument{}, ErrRDMRequired
}
if request.Mode != render.ModeWorking && request.Mode != render.ModePreview {
return PreparedDocument{}, fmt.Errorf("docgen: unsupported ordinary workspace mode %q", request.Mode)
}
limits, err := normalizeLimits(request.Limits)
if err != nil {
return PreparedDocument{}, err
}
if len(request.Markdown) > limits.MaxMarkdownBytes {
return PreparedDocument{}, fmt.Errorf("%w: Markdown is %d bytes, limit is %d", ErrResourceLimit, len(request.Markdown), limits.MaxMarkdownBytes)
}
materialized := bytes.HasPrefix(bytes.TrimSpace(request.Markdown), []byte("---"))
var parsed markdownrenderer.Result
if materialized {
parsed, err = parseMarkdownContext(ctx, request.Markdown, false, limits)
if err != nil {
return PreparedDocument{}, classifyParseError(err)
}
if err := validateWorkspaceRoute(request, parsed.Metadata); err != nil {
return PreparedDocument{}, err
}
if _, err := profileVersionFor(request, &parsed.Metadata); err != nil {
return PreparedDocument{}, err
}
} else {
profileVersion, err := profileVersionFor(request, nil)
if err != nil {
return PreparedDocument{}, err
}
source, err := workspaceMarkdown(request, defaultProfileID, profileVersion)
if err != nil {
return PreparedDocument{}, err
}
if len(source) > limits.MaxMarkdownBytes {
return PreparedDocument{}, fmt.Errorf("%w: normalized Markdown source is %d bytes, limit is %d", ErrResourceLimit, len(source), limits.MaxMarkdownBytes)
}
parsed, err = parseMarkdownContext(ctx, source, true, limits)
if err != nil {
return PreparedDocument{}, classifyParseError(err)
}
}
if len(parsed.Markdown) > limits.MaxMarkdownBytes {
return PreparedDocument{}, fmt.Errorf("%w: materialized Markdown is %d bytes, limit is %d", ErrResourceLimit, len(parsed.Markdown), limits.MaxMarkdownBytes)
}
canonicalHash, err := parsed.Document.HashContext(ctx)
if err != nil {
return PreparedDocument{}, err
}
return PreparedDocument{
Document: parsed.Document, CanonicalHash: canonicalHash,
Markdown: artifact(safeBaseName(request.Title)+".md", "text/markdown; charset=utf-8", parsed.Markdown),
}, nil
}
type generatorDependencies struct {
loadProfile func(context.Context, string) (profiles.Bundle, error)
newRenderer func(*profile.AssetReader) render.Renderer
legacyBuild func(string, string) ([]byte, error)
}
func defaultGeneratorDependencies() generatorDependencies {
return generatorDependencies{
loadProfile: profiles.LoadCompanyDefaultVersionContext,
newRenderer: func(assets *profile.AssetReader) render.Renderer { return word.NewAdapter(assets) },
legacyBuild: legacyBuild,
}
}
func Generate(ctx context.Context, request Request) (Delivery, error) {
return generate(ctx, request, defaultGeneratorDependencies())
}
func generate(ctx context.Context, request Request, dependencies generatorDependencies) (Delivery, error) {
return generateWithOptions(ctx, request, RenderOptions{}, dependencies)
}
func GenerateWithOptions(ctx context.Context, request Request, options RenderOptions) (Delivery, error) {
return generateWithOptions(ctx, request, options, defaultGeneratorDependencies())
}
func generateWithOptions(ctx context.Context, request Request, options RenderOptions, dependencies generatorDependencies) (Delivery, error) {
if err := contextErr(ctx); err != nil {
return Delivery{}, err
}
if request.Controlled || request.ProjectDocument || request.Mode == render.ModeReview || request.Mode == render.ModeApproved {
return Delivery{}, ErrRDMRequired
}
if request.Mode != render.ModeWorking && request.Mode != render.ModePreview {
return Delivery{}, fmt.Errorf("docgen: unsupported ordinary workspace mode %q", request.Mode)
}
limits, err := normalizeLimits(request.Limits)
if err != nil {
return Delivery{}, err
}
if len(request.Markdown) > limits.MaxMarkdownBytes {
return Delivery{}, fmt.Errorf("%w: Markdown is %d bytes, limit is %d", ErrResourceLimit, len(request.Markdown), limits.MaxMarkdownBytes)
}
var parsed markdownrenderer.Result
var boundMetadata *markdownrenderer.Metadata
materialized := bytes.HasPrefix(bytes.TrimSpace(request.Markdown), []byte("---"))
if materialized {
parsed, err = parseMarkdownContext(ctx, request.Markdown, false, limits)
if err != nil {
return Delivery{}, classifyParseError(err)
}
if err := validateWorkspaceRoute(request, parsed.Metadata); err != nil {
return Delivery{}, err
}
boundMetadata = &parsed.Metadata
}
boundVersion, err := profileVersionFor(request, boundMetadata)
if err != nil {
return Delivery{}, err
}
bundle, err := dependencies.loadProfile(ctx, boundVersion)
if err != nil {
return Delivery{}, fmt.Errorf("docgen: load company default %s: %w", boundVersion, err)
}
profileID, profileVersion := bundle.Snapshot.ProfileIdentity()
if profileID != defaultProfileID || profileVersion != boundVersion {
return Delivery{}, fmt.Errorf("%w: loaded %s@%s, bound %s@%s", ErrProfileMismatch,
profileID, profileVersion, defaultProfileID, boundVersion)
}
if !materialized {
source, sourceErr := workspaceMarkdown(request, profileID, profileVersion)
if sourceErr != nil {
return Delivery{}, sourceErr
}
if len(source) > limits.MaxMarkdownBytes {
return Delivery{}, fmt.Errorf("%w: normalized Markdown source is %d bytes, limit is %d", ErrResourceLimit, len(source), limits.MaxMarkdownBytes)
}
parsed, err = parseMarkdownContext(ctx, source, true, limits)
if err != nil {
return Delivery{}, classifyParseError(err)
}
}
if len(parsed.Markdown) > limits.MaxMarkdownBytes {
return Delivery{}, fmt.Errorf("%w: materialized Markdown is %d bytes, limit is %d", ErrResourceLimit, len(parsed.Markdown), limits.MaxMarkdownBytes)
}
if err := validateWorkspaceMetadata(request, parsed.Metadata, profileID, profileVersion); err != nil {
return Delivery{}, err
}
if err := contextErr(ctx); err != nil {
return Delivery{}, err
}
if err := acquireRenderCapacity(ctx); err != nil {
return Delivery{}, err
}
result, err := dependencies.newRenderer(bundle.Assets).Render(ctx, render.Request{
Document: parsed.Document,
Profile: bundle.Snapshot,
Mode: request.Mode,
FrozenAt: request.FrozenAt,
RequestedBy: request.RequestedBy,
})
releaseRenderCapacity()
if err != nil {
code := "renderer_failed"
if strings.Contains(err.Error(), "asset") || strings.Contains(err.Error(), "hash") {
code = "asset_mismatch"
}
return Delivery{}, diagnosticFailure("render", code, err, result.Diagnostics)
}
base := safeBaseName(request.Title)
delivery := Delivery{
Document: parsed.Document, Renderer: result.Manifest,
Profile: ProfileReceipt{
ProfileID: profileID, Version: profileVersion, SnapshotHash: bundle.Snapshot.SnapshotHash(),
Candidate: bundle.Snapshot.State() != profile.StatePublished, ReviewStatus: bundle.Review.Status, PublicationAllowed: bundle.Review.PublicationAllowed,
Unverified: append([]string(nil), bundle.Review.Unverified...), ProfileSHA256: bundle.Review.ProfileSHA256,
AssetManifestSHA256: bundle.Review.AssetManifestSHA256, ReviewSHA256: bundle.Review.ReviewSHA256,
},
Markdown: artifact(base+".md", "text/markdown; charset=utf-8", parsed.Markdown),
DOCX: artifact(base+".docx", result.MIME, result.Artifact),
}
if options.MigrationComparison {
comparisonSource := request.LegacySource
if len(comparisonSource) == 0 {
comparisonSource = request.Markdown
}
legacy, err := dependencies.legacyBuild(request.Title, string(comparisonSource))
if err != nil {
return Delivery{}, fmt.Errorf("docgen: legacy migration comparison: %w", err)
}
delivery.Comparison = compareLegacyAndShared(request.Title, comparisonSource, parsed.Document, legacy, delivery.DOCX.Bytes)
}
manifest := Manifest{
SchemaVersion: ManifestSchemaVersion, RendererIdentity: RendererIdentity, AdapterIdentity: AdapterIdentity,
SelectionSource: "organization-default",
Profile: delivery.Profile,
DocumentRootID: parsed.Document.Root.ID,
Artifacts: []ManifestArtifact{
manifestArtifact(delivery.Markdown), manifestArtifact(delivery.DOCX),
},
Renderer: result.Manifest, Diagnostics: append([]render.Diagnostic(nil), result.Diagnostics...),
NodeAnchors: cloneStringMap(result.NodeAnchors), Comparison: delivery.Comparison,
}
manifestRaw, err := json.MarshalIndent(manifest, "", " ")
if err != nil {
return Delivery{}, fmt.Errorf("docgen: marshal manifest: %w", err)
}
manifestRaw = append(manifestRaw, '\n')
delivery.Manifest = artifact(base+".manifest.json", "application/json", manifestRaw)
archiveRaw, err := buildArchive(delivery.Markdown, delivery.DOCX, delivery.Manifest)
if err != nil {
return Delivery{}, err
}
delivery.Archive = artifact(base+".zip", "application/zip", archiveRaw)
return delivery, nil
}
func acquireRenderCapacity(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
case renderCapacity <- struct{}{}:
return nil
}
}
func releaseRenderCapacity() { <-renderCapacity }
func parseMarkdownContext(ctx context.Context, source []byte, firstImport bool, limits Limits) (markdownrenderer.Result, error) {
return markdownrenderer.ParseContext(ctx, source, markdownrenderer.Options{
Controlled: true, FirstImport: firstImport, MaxBytes: limits.MaxMarkdownBytes,
MaxNodes: limits.MaxNodes, MaxDepth: limits.MaxDepth,
})
}
func classifyParseError(err error) error {
if errors.Is(err, markdownrenderer.ErrDocumentLimit) {
return fmt.Errorf("%w: %v", ErrResourceLimit, err)
}
code := "markdown_invalid"
switch {
case errors.Is(err, markdownrenderer.ErrUnknownMetadata):
code = "unknown_metadata"
case errors.Is(err, markdownrenderer.ErrHiddenHTML):
code = "unsafe_markdown"
case errors.Is(err, markdownrenderer.ErrUnsafeDirective) && strings.Contains(err.Error(), "unknown directive"):
code = "unknown_directive"
case errors.Is(err, markdownrenderer.ErrUnsafeDirective):
code = "unsafe_markdown"
}
return diagnosticFailure("parse", code, err, nil)
}
func diagnosticFailure(stage, code string, err error, diagnostics []render.Diagnostic) error {
cloned := append([]render.Diagnostic(nil), diagnostics...)
if len(cloned) == 0 {
cloned = []render.Diagnostic{{Code: code, Severity: render.SeverityError, Message: err.Error()}}
}
return &DiagnosticError{Stage: stage, Code: code, Diagnostics: cloned, Err: err}
}
func normalizeLimits(input Limits) (Limits, error) {
if input.MaxMarkdownBytes < 0 || input.MaxNodes < 0 || input.MaxDepth < 0 {
return Limits{}, fmt.Errorf("%w: limits cannot be negative", ErrResourceLimit)
}
if input.MaxMarkdownBytes == 0 {
input.MaxMarkdownBytes = 2 << 20
}
if input.MaxNodes == 0 {
input.MaxNodes = 10_000
}
if input.MaxDepth == 0 {
input.MaxDepth = 64
}
return input, nil
}
func workspaceMarkdown(request Request, profileID, profileVersion string) ([]byte, error) {
if strings.TrimSpace(request.DocumentID) == "" || strings.TrimSpace(request.Title) == "" ||
strings.TrimSpace(request.DocumentType) == "" || strings.TrimSpace(request.Language) == "" {
return nil, errors.New("docgen: document ID, title, document type, and language are required")
}
if request.Mode == "" || request.FrozenAt.IsZero() || strings.TrimSpace(request.RequestedBy) == "" {
return nil, errors.New("docgen: mode, frozen time, and requester are required")
}
quote := func(value string) string {
raw, _ := json.Marshal(value)
return string(raw)
}
var header strings.Builder
header.WriteString("---\n")
fmt.Fprintf(&header, "documentId: %s\n", quote(request.DocumentID))
fmt.Fprintf(&header, "projectId: %s\n", quote(WorkspaceProjectID))
fmt.Fprintf(&header, "documentType: %s\n", quote(request.DocumentType))
fmt.Fprintf(&header, "title: %s\n", quote(request.Title))
fmt.Fprintf(&header, "language: %s\n", quote(request.Language))
fmt.Fprintf(&header, "profileId: %s\n", quote(profileID))
fmt.Fprintf(&header, "profileVersion: %s\n", quote(profileVersion))
header.WriteString("controlled: false\n---\n\n")
header.Write(bytes.TrimSpace(request.Markdown))
header.WriteByte('\n')
return []byte(header.String()), nil
}
func validateWorkspaceRoute(request Request, metadata markdownrenderer.Metadata) error {
if metadata.Controlled || metadata.ProjectID != WorkspaceProjectID {
return ErrRDMRequired
}
if metadata.DocumentID != request.DocumentID || metadata.DocumentType != request.DocumentType ||
metadata.Title != request.Title || metadata.Language != request.Language {
return errors.New("docgen: persisted Markdown metadata does not match the frozen request")
}
return nil
}
// profileVersionFor returns the company-default version a document renders
// with. Published versions are immutable, so a version the persisted Markdown
// or the request already binds is kept; only a new, unbound document takes the
// latest published version. metadata is nil until Markdown is materialized.
func profileVersionFor(request Request, metadata *markdownrenderer.Metadata) (string, error) {
requested := request.ProfileID != "" || request.ProfileVersion != ""
if requested && !publishedProfileVersion(request.ProfileID, request.ProfileVersion) {
return "", fmt.Errorf("%w: requested %s@%s is not a published %s version", ErrProfileMismatch,
request.ProfileID, request.ProfileVersion, defaultProfileID)
}
if metadata == nil {
if requested {
return request.ProfileVersion, nil
}
return profiles.LatestCompanyDefaultVersion, nil
}
if !publishedProfileVersion(metadata.ProfileID, metadata.ProfileVersion) {
return "", fmt.Errorf("%w: Markdown binds %s@%s, which is not a published %s version", ErrProfileMismatch,
metadata.ProfileID, metadata.ProfileVersion, defaultProfileID)
}
if requested && request.ProfileVersion != metadata.ProfileVersion {
return "", fmt.Errorf("%w: requested %s@%s, Markdown binds %s@%s", ErrProfileMismatch,
request.ProfileID, request.ProfileVersion, metadata.ProfileID, metadata.ProfileVersion)
}
return metadata.ProfileVersion, nil
}
func publishedProfileVersion(profileID, profileVersion string) bool {
return profileID == defaultProfileID && slices.Contains(profiles.CompanyDefaultVersions(), profileVersion)
}
func validateWorkspaceMetadata(request Request, metadata markdownrenderer.Metadata, profileID, profileVersion string) error {
if err := validateWorkspaceRoute(request, metadata); err != nil {
return err
}
if metadata.ProfileID != profileID || metadata.ProfileVersion != profileVersion {
return fmt.Errorf("%w: Markdown binds %s@%s, available %s@%s", ErrProfileMismatch,
metadata.ProfileID, metadata.ProfileVersion, profileID, profileVersion)
}
return nil
}
func artifact(name, mime string, raw []byte) Artifact {
sum := sha256.Sum256(raw)
// Callers transfer ownership of freshly produced buffers to the delivery.
return Artifact{Name: name, MIME: mime, SHA256: hex.EncodeToString(sum[:]), Bytes: raw}
}
func manifestArtifact(value Artifact) ManifestArtifact {
return ManifestArtifact{Name: value.Name, MIME: value.MIME, Size: len(value.Bytes), SHA256: value.SHA256}
}
func buildArchive(artifacts ...Artifact) ([]byte, error) {
items := append([]Artifact(nil), artifacts...)
sort.Slice(items, func(i, j int) bool { return items[i].Name < items[j].Name })
var buffer bytes.Buffer
archive := zip.NewWriter(&buffer)
for _, item := range items {
header := &zip.FileHeader{Name: item.Name, Method: zip.Deflate}
header.SetModTime(time.Date(1980, 1, 1, 0, 0, 0, 0, time.UTC))
header.SetMode(0o644)
writer, err := archive.CreateHeader(header)
if err != nil {
return nil, fmt.Errorf("docgen: create archive entry %s: %w", item.Name, err)
}
if _, err := writer.Write(item.Bytes); err != nil {
return nil, fmt.Errorf("docgen: write archive entry %s: %w", item.Name, err)
}
}
if err := archive.Close(); err != nil {
return nil, fmt.Errorf("docgen: close delivery archive: %w", err)
}
return buffer.Bytes(), nil
}
func cloneStringMap(source map[string]string) map[string]string {
clone := make(map[string]string, len(source))
for key, value := range source {
clone[key] = value
}
return clone
}
func safeBaseName(title string) string {
return strings.TrimSuffix(SuggestName(title), ".docx")
}
func contextErr(ctx context.Context) error {
select {
case <-ctx.Done():
return fmt.Errorf("docgen: render canceled: %w", ctx.Err())
default:
return nil
}
}