{"record":{"id":"c1d9353e4875be04","repo":"nexu-io/open-design","slug":"artifact-publication-blocked","errorCode":"ARTIFACT_PUBLICATION_BLOCKED","errorMessage":"Artifact still contains unresolved pitch-deck placeholders: ${list}. Provide the required pitch facts before publishing.","messagePattern":"Artifact still contains unresolved pitch-deck placeholders: (.+?)\\. Provide the required pitch facts before publishing\\.","errorType":"error_code","errorClass":"ArtifactPublicationBlockedError","httpStatus":null,"severity":"error","filePath":"apps/daemon/src/artifacts/publication-guard.ts","lineNumber":80,"sourceCode":"  if (!text) return [];\n  return UNRESOLVED_ARTIFACT_PLACEHOLDERS.filter((placeholder) =>\n    text.includes(placeholder),\n  );\n}\n\nexport function shouldBlockArtifactPublication(value: unknown): boolean {\n  return findUnresolvedArtifactPlaceholders(value).length > 0;\n}\n\nexport function buildArtifactPublicationBlockedMessage(placeholders: readonly string[]): string {\n  const list = placeholders.length > 0 ? placeholders.join(', ') : 'unknown placeholders';\n  return `Artifact still contains unresolved pitch-deck placeholders: ${list}. Provide the required pitch facts before publishing.`;\n}\n\nexport function assertArtifactPublicationAllowed(value: unknown): void {\n  const placeholders = findUnresolvedArtifactPlaceholders(value);\n  if (placeholders.length > 0) {\n    throw new ArtifactPublicationBlockedError(placeholders);\n  }\n}\n\nfunction stringifyArtifactContent(value: unknown): string {\n  if (typeof value === 'string') return value;\n  if (Buffer.isBuffer(value)) return value.toString('utf8');\n  if (value instanceof Uint8Array) return Buffer.from(value).toString('utf8');\n  return '';\n}\n","sourceCodeStart":62,"sourceCodeEnd":90,"githubUrl":"https://github.com/nexu-io/open-design/blob/5be4028344c2eb4c667c5a97bda8f750c5597ef7/apps/daemon/src/artifacts/publication-guard.ts#L62-L90","documentation":"Thrown as ArtifactPublicationBlockedError (code ARTIFACT_PUBLICATION_BLOCKED) by assertArtifactPublicationAllowed() when an html or deck artifact body still contains one of the pitch-deck template placeholders ('Name to confirm', '$X.XM', 'Replace this panel with', 'Replace role placeholders', 'Your form answer only said'). It is the defense-in-depth guard for the html-ppt-pitch-deck example template, catching agents that bypassed the structured od.inputs contract.","triggerScenarios":"Publishing (writing to the project as a finished artifact) an HTML or deck body produced from the html-ppt-pitch-deck example without first filling in the fundraising facts through od.inputs. The guard runs at the file-write boundary for html/deck kinds only.","commonSituations":"Agent routed the deck through od-default instead of the official plugin, skipping od.inputs; user previewed a deck and hit 'publish' before answering the pitch facts; copy of the example template whose placeholders were never replaced.","solutions":["Provide the required pitch facts through the plugin's structured od.inputs so the placeholders are substituted before publication.","Manually replace each listed placeholder in the body with real content before publishing.","If the listed strings are legitimate product copy (not unfilled slots), rename them in the source so they no longer collide with the placeholder set."],"exampleFix":"// before — body still has placeholder\n'<h2>Funding: $X.XM</h2>'\n// after — real value substituted via od.inputs\n'<h2>Funding: $4.5M</h2>'","handlingStrategy":"validation","validationCode":"import { findUnresolvedArtifactPlaceholders } from './artifacts/publication-guard.js';\nconst placeholders = findUnresolvedArtifactPlaceholders(body);\nif (placeholders.length > 0) {\n  throw new Error(`unresolved placeholders: ${placeholders.join(', ')}`);\n}\n// safe to publish","typeGuard":"import { UNRESOLVED_ARTIFACT_PLACEHOLDERS } from './artifacts/publication-guard.js';\nfunction isFreeOfPitchPlaceholders(text: string): boolean {\n  return !UNRESOLVED_ARTIFACT_PLACEHOLDERS.some((p) => text.includes(p));\n}","tryCatchPattern":"try {\n  await publishArtifact(body);\n} catch (e) {\n  if (e instanceof ArtifactPublicationBlockedError) {\n    // prompt for pitch facts, substitute, then retry\n  }\n  throw e;\n}","preventionTips":["Drive pitch-deck creation through the html-ppt-pitch-deck plugin and its od.inputs so placeholders are filled upstream.","Run findUnresolvedArtifactPlaceholders on the body before exposing a Publish action.","Treat the placeholder set as reserved strings; do not use them as literal product copy."],"tags":["artifacts","publication-guard","pitch-deck","validation","daemon"],"backgroundTag":null,"analyzedSha":"5be4028344c2eb4c667c5a97bda8f750c5597ef7","analyzedAt":"2026-08-12T12:03:58.812Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}