Why: - Independent review found trusted-boundary, semantic parity, replay-budget, comparison, persistence, cancellation, provenance, and routing gaps. - Storage migration must include the new durable delivery table. Evidence: - go test -count=1 ./... - go test -race ./internal/docgen ./internal/tools ./internal/runmgr - go test ./... (deps/agent-document-renderer) - go vet ./... - go build ./... - CGO_ENABLED=0 go build -trimpath ./cmd/agent-system - Python house-style unittest suite and git diff --check Impact: - Word delivery now uses host-owned freezes, Markdown-first resumable persistence, closed registration boundaries, semantic shared rendering, and real comparison. - Controlled/project documents still fail closed to RDM and non-Word flows remain unchanged. Refs: - Unified Document Renderer implementation plan Task 10 - /tmp/task10-backend-review-last.md - Renderer d5cd8c126045a27d5a5cc5da23f64d3c737e116a
175 lines
7.0 KiB
Go
175 lines
7.0 KiB
Go
package runmgr
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
|
|
"agent-system-backend/internal/docgen"
|
|
"agent-system-backend/internal/store"
|
|
core "agent-system-core"
|
|
)
|
|
|
|
func (t *toolCtx) ExecuteDocumentDelivery(ctx context.Context, request docgen.OperationRequest) (docgen.OperationReceipt, error) {
|
|
receipt, err := t.rc.executeDocumentDelivery(ctx, request)
|
|
if receipt.Outcome == "complete" || receipt.Outcome == "unknown" {
|
|
for _, artifact := range receipt.Artifacts {
|
|
if artifact.Role != string(store.DocumentArtifactArchive) {
|
|
continue
|
|
}
|
|
t.arts = append(t.arts, core.Artifact{
|
|
ID: core.ID(artifact.ID), ProjectID: t.rc.run.ProjectID, Owner: t.rc.run.Owner,
|
|
Name: artifact.Name, Kind: "document", MIME: artifact.MIME, Size: artifact.Size,
|
|
})
|
|
break
|
|
}
|
|
}
|
|
return receipt, err
|
|
}
|
|
|
|
func (c *runCtx) executeDocumentDelivery(ctx context.Context, operation docgen.OperationRequest) (docgen.OperationReceipt, error) {
|
|
if ctx == nil {
|
|
return docgen.OperationReceipt{}, errors.New("document delivery: nil context")
|
|
}
|
|
if err := c.mgr.authorizeRunWork(ctx, c.run.Owner, c.run.ProjectID); err != nil {
|
|
return docgen.OperationReceipt{}, err
|
|
}
|
|
requestHash, err := documentOperationHash(operation)
|
|
if err != nil {
|
|
return docgen.OperationReceipt{}, err
|
|
}
|
|
record, err := c.mgr.st.BeginDocumentDeliveryAccountChecked(c.run.Owner, store.DocumentDeliveryBeginInput{
|
|
OperationID: operation.OperationID, RequestSHA256: requestHash,
|
|
ProjectID: string(c.run.ProjectID), ConversationID: string(c.run.ConversationID), SourceRun: string(c.run.ID),
|
|
DocumentID: operation.DocumentID, Mode: string(operation.Mode), RendererIdentity: docgen.RendererIdentity,
|
|
}, c.mgr.accountAllowImplicitLocal)
|
|
if err != nil {
|
|
return receiptFromDelivery(record, "partial", false, err), err
|
|
}
|
|
if record.State == store.DocumentDeliveryComplete {
|
|
return receiptFromDelivery(record, "complete", true, nil), nil
|
|
}
|
|
|
|
request := docgen.Request{
|
|
DocumentID: operation.DocumentID, Title: operation.Title, DocumentType: operation.DocumentType, Language: operation.Language,
|
|
Markdown: operation.Markdown, Mode: operation.Mode, FrozenAt: record.FrozenAt, RequestedBy: c.run.Owner,
|
|
Controlled: operation.Controlled, ProjectDocument: operation.ProjectDocument,
|
|
}
|
|
if record.MarkdownArtifactID != "" {
|
|
persisted, readErr := c.mgr.st.DocumentDeliveryMarkdown(c.run.Owner, operation.OperationID)
|
|
if readErr != nil {
|
|
return receiptFromDelivery(record, "unknown", false, readErr), readErr
|
|
}
|
|
request.Markdown = persisted
|
|
}
|
|
prepared, err := docgen.Prepare(ctx, request)
|
|
if err != nil {
|
|
return receiptFromDelivery(record, "partial", false, err), err
|
|
}
|
|
if record.MarkdownArtifactID == "" {
|
|
record, err = c.mgr.st.PersistDocumentDeliveryMarkdownAccountChecked(c.run.Owner, operation.OperationID,
|
|
deliveryArtifactInput(store.DocumentArtifactMarkdown, prepared.Markdown), prepared.CanonicalHash,
|
|
c.mgr.accountAllowImplicitLocal)
|
|
if err != nil {
|
|
outcome := "partial"
|
|
if errors.Is(err, store.ErrDocumentDeliveryOutcomeUnknown) {
|
|
outcome = "unknown"
|
|
}
|
|
return receiptFromDelivery(record, outcome, false, err), err
|
|
}
|
|
}
|
|
request.LegacySource = append([]byte(nil), operation.Markdown...)
|
|
request.Markdown = prepared.Markdown.Bytes
|
|
if err := c.mgr.authorizeRunWork(ctx, c.run.Owner, c.run.ProjectID); err != nil {
|
|
return receiptFromDelivery(record, "partial", false, err), err
|
|
}
|
|
delivery, err := docgen.GenerateWithOptions(ctx, request, docgen.RenderOptions{MigrationComparison: c.mgr.documentMigrationComparison})
|
|
if err != nil {
|
|
record, markErr := c.mgr.st.MarkDocumentDeliveryFailedAccountChecked(c.run.Owner, operation.OperationID, err.Error(), c.mgr.accountAllowImplicitLocal)
|
|
if markErr != nil {
|
|
return receiptFromDelivery(record, "unknown", false, markErr), errors.Join(err, markErr)
|
|
}
|
|
return receiptFromDelivery(record, "partial", false, err), err
|
|
}
|
|
if err := c.mgr.authorizeRunWork(ctx, c.run.Owner, c.run.ProjectID); err != nil {
|
|
return receiptFromDelivery(record, "partial", false, err), err
|
|
}
|
|
deliveryKey := documentDeliveryKey(delivery)
|
|
record, err = c.mgr.st.CommitDocumentDeliveryDerivedAccountChecked(c.run.Owner, operation.OperationID,
|
|
deliveryKey, delivery.Profile.SnapshotHash, delivery.Renderer.ReviewHash,
|
|
[]store.DocumentDeliveryArtifactInput{
|
|
deliveryArtifactInput(store.DocumentArtifactDOCX, delivery.DOCX),
|
|
deliveryArtifactInput(store.DocumentArtifactManifest, delivery.Manifest),
|
|
deliveryArtifactInput(store.DocumentArtifactArchive, delivery.Archive),
|
|
}, c.mgr.accountAllowImplicitLocal)
|
|
if err != nil {
|
|
outcome := "partial"
|
|
if errors.Is(err, store.ErrDocumentDeliveryOutcomeUnknown) {
|
|
outcome = "unknown"
|
|
}
|
|
return receiptFromDelivery(record, outcome, false, err), err
|
|
}
|
|
return receiptFromDelivery(record, "complete", false, nil), nil
|
|
}
|
|
|
|
func documentOperationHash(operation docgen.OperationRequest) (string, error) {
|
|
payload := struct {
|
|
DocumentID string `json:"documentId"`
|
|
Title string `json:"title"`
|
|
DocumentType string `json:"documentType"`
|
|
Language string `json:"language"`
|
|
Markdown string `json:"markdown"`
|
|
Mode string `json:"mode"`
|
|
Controlled bool `json:"controlled"`
|
|
ProjectDocument bool `json:"projectDocument"`
|
|
}{
|
|
DocumentID: operation.DocumentID, Title: operation.Title, DocumentType: operation.DocumentType,
|
|
Language: operation.Language, Markdown: string(operation.Markdown), Mode: string(operation.Mode),
|
|
Controlled: operation.Controlled, ProjectDocument: operation.ProjectDocument,
|
|
}
|
|
raw, err := json.Marshal(payload)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
sum := sha256.Sum256(raw)
|
|
return hex.EncodeToString(sum[:]), nil
|
|
}
|
|
|
|
func documentDeliveryKey(delivery docgen.Delivery) string {
|
|
manifest := delivery.Renderer
|
|
value := strings.Join([]string{manifest.DocumentHash, manifest.ProfileHash, manifest.ReviewHash, string(manifest.Mode), docgen.RendererIdentity}, "\x00")
|
|
sum := sha256.Sum256([]byte(value))
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
func deliveryArtifactInput(role store.DocumentDeliveryArtifactRole, artifact docgen.Artifact) store.DocumentDeliveryArtifactInput {
|
|
kind := "document"
|
|
if artifact.MIME == "application/json" {
|
|
kind = "data"
|
|
}
|
|
return store.DocumentDeliveryArtifactInput{
|
|
Role: role, Name: artifact.Name, Kind: kind, MIME: artifact.MIME,
|
|
SHA256: artifact.SHA256, Content: artifact.Bytes,
|
|
}
|
|
}
|
|
|
|
func receiptFromDelivery(record store.DocumentDeliveryRecord, outcome string, cacheHit bool, cause error) docgen.OperationReceipt {
|
|
receipt := docgen.OperationReceipt{
|
|
OperationID: record.OperationID, Outcome: outcome, State: string(record.State), FrozenAt: record.FrozenAt,
|
|
DeliveryKey: record.DeliveryKey, Renderer: record.RendererIdentity, CacheHit: cacheHit,
|
|
}
|
|
if cause != nil {
|
|
receipt.Error = cause.Error()
|
|
}
|
|
for _, item := range record.Artifacts {
|
|
receipt.Artifacts = append(receipt.Artifacts, docgen.ReceiptArtifact{
|
|
Role: string(item.Role), ID: string(item.Artifact.ID), Name: item.Artifact.Name,
|
|
MIME: item.Artifact.MIME, Size: item.Artifact.Size, SHA256: item.SHA256,
|
|
})
|
|
}
|
|
return receipt
|
|
}
|