diff --git a/collab-notebook-reproducibility-guard/README.md b/collab-notebook-reproducibility-guard/README.md
new file mode 100644
index 00000000..5ed758d4
--- /dev/null
+++ b/collab-notebook-reproducibility-guard/README.md
@@ -0,0 +1,32 @@
+# Collaborative Notebook Reproducibility Guard
+
+This module adds a dependency-free pre-acceptance guard for embedded Jupyter-style outputs in the real-time collaborative research editor.
+
+It checks whether notebook outputs are reproducible enough to be accepted into a shared manuscript section. The guard reviews execution order, kernel/runtime fingerprints, dependency lock hashes, random seed capture, input artifact fingerprints, stale output timestamps, unsafe rich HTML, private local paths, and section-version drift.
+
+## Commands
+
+```bash
+npm test
+npm run demo
+npm run video
+npm run check
+```
+
+## Outputs
+
+The demo writes deterministic artifacts to `reports/`:
+
+- `unsafe-notebook-packet.json`
+- `warning-notebook-packet.json`
+- `clean-notebook-packet.json`
+- `notebook-reproducibility-report.md`
+- `maintainer-verification-packet.md`
+- `summary.svg`
+- `demo.mp4`
+
+## Status Lanes
+
+- `hold_notebook_outputs`: block shared manuscript insertion and require a clean rerun.
+- `stage_for_reproducibility_review`: allow review only after metadata is completed.
+- `accept_notebook_outputs`: output packet is ready for collaborative acceptance.
diff --git a/collab-notebook-reproducibility-guard/acceptance-notes.md b/collab-notebook-reproducibility-guard/acceptance-notes.md
new file mode 100644
index 00000000..5a0e08a1
--- /dev/null
+++ b/collab-notebook-reproducibility-guard/acceptance-notes.md
@@ -0,0 +1,13 @@
+# Acceptance Notes
+
+Validation performed locally:
+
+- `node test.js`
+- `npm test`
+- `npm run demo`
+- `npm run video`
+- `npm run check` (syntax, tests, report generation, and demo video generation)
+- `ffprobe` on `reports/demo.mp4`
+- `git diff --check`
+
+The data is synthetic. No external services, credentials, private manuscripts, live collaborator data, or payment details are used.
diff --git a/collab-notebook-reproducibility-guard/demo.js b/collab-notebook-reproducibility-guard/demo.js
new file mode 100644
index 00000000..91c89872
--- /dev/null
+++ b/collab-notebook-reproducibility-guard/demo.js
@@ -0,0 +1,154 @@
+const fs = require('fs');
+const path = require('path');
+
+const { assessNotebookOutputPacket } = require('./index');
+const { cleanPacket, unsafePacket, warningPacket } = require('./sample-data');
+
+const reportsDir = path.join(__dirname, 'reports');
+fs.mkdirSync(reportsDir, { recursive: true });
+
+const unsafeReview = assessNotebookOutputPacket(unsafePacket);
+const warningReview = assessNotebookOutputPacket(warningPacket);
+const cleanReview = assessNotebookOutputPacket(cleanPacket);
+
+writeJson('unsafe-notebook-packet.json', unsafeReview);
+writeJson('warning-notebook-packet.json', warningReview);
+writeJson('clean-notebook-packet.json', cleanReview);
+writeMarkdownReport(unsafeReview, warningReview, cleanReview);
+writeMaintainerVerificationPacket(unsafeReview, warningReview, cleanReview);
+writeSummarySvg(unsafeReview, warningReview, cleanReview);
+
+console.log('Generated notebook reproducibility artifacts:');
+console.log(`- ${path.join(reportsDir, 'unsafe-notebook-packet.json')}`);
+console.log(`- ${path.join(reportsDir, 'warning-notebook-packet.json')}`);
+console.log(`- ${path.join(reportsDir, 'clean-notebook-packet.json')}`);
+console.log(`- ${path.join(reportsDir, 'notebook-reproducibility-report.md')}`);
+console.log(`- ${path.join(reportsDir, 'maintainer-verification-packet.md')}`);
+console.log(`- ${path.join(reportsDir, 'summary.svg')}`);
+
+function writeJson(filename, value) {
+ fs.writeFileSync(path.join(reportsDir, filename), `${JSON.stringify(value, null, 2)}\n`);
+}
+
+function writeMarkdownReport(unsafeReview, warningReview, cleanReview) {
+ const lines = [
+ '# Collaborative Notebook Reproducibility Guard',
+ '',
+ 'Synthetic demo packet review for issue #12.',
+ '',
+ '| Packet | Status | Cells | Blockers | Warnings | Action |',
+ '| --- | --- | ---: | ---: | ---: | --- |',
+ row('Unsafe packet', unsafeReview),
+ row('Warning packet', warningReview),
+ row('Clean packet', cleanReview),
+ '',
+ '## Reviewer Notes',
+ '',
+ '- Unsafe packets are held before shared manuscript insertion.',
+ '- Rich HTML outputs are sanitized and local/private paths are redacted.',
+ '- Clean continuous reruns with runtime, seed, dependency lock, and input fingerprints are accepted.'
+ ];
+
+ fs.writeFileSync(path.join(reportsDir, 'notebook-reproducibility-report.md'), `${lines.join('\n')}\n`);
+}
+
+function row(label, packet) {
+ const summary = packet.reproducibilitySummary;
+ return `| ${label} | ${packet.status} | ${summary.cells} | ${summary.blockers} | ${summary.warnings} | ${primaryAction(packet)} |`;
+}
+
+function writeMaintainerVerificationPacket(unsafeReview, warningReview, cleanReview) {
+ const blockerCodes = unsafeReview.findings
+ .filter((finding) => finding.severity === 'blocker')
+ .map((finding) => finding.code);
+ const sanitizedLoadCell = unsafeReview.cells.find((cell) => cell.id === 'cell-load-data');
+ const sanitizedModelCell = unsafeReview.cells.find((cell) => cell.id === 'cell-fit-model');
+ const lines = [
+ '# Maintainer Verification Packet',
+ '',
+ 'This packet gives reviewers a compact checklist for issue #12 acceptance.',
+ '',
+ '## Acceptance Evidence',
+ '',
+ `- Unsafe packet lane: ${unsafeReview.status}`,
+ `- Warning packet lane: ${warningReview.status}`,
+ `- Clean packet lane: ${cleanReview.status}`,
+ `- Unsafe blocker count: ${unsafeReview.reproducibilitySummary.blockers}`,
+ `- Warning count: ${warningReview.reproducibilitySummary.warnings}`,
+ `- Clean accepted cells: ${cleanReview.reproducibilitySummary.cells}`,
+ '',
+ '## Guard Coverage',
+ '',
+ '| Requirement | Evidence |',
+ '| --- | --- |',
+ `| Execution-order continuity | ${blockerCodes.includes('EXECUTION_ORDER_GAP') ? 'covered by unsafe packet' : 'missing'} |`,
+ `| Dependency/version integrity | ${blockerCodes.includes('DEPENDENCY_LOCK_MISMATCH') ? 'covered by unsafe packet' : 'missing'} |`,
+ `| Random-seed capture | ${blockerCodes.includes('MISSING_RANDOM_SEED') ? 'covered by unsafe packet' : 'missing'} |`,
+ `| Input artifact fingerprints | ${blockerCodes.includes('MISSING_INPUT_FINGERPRINT') ? 'covered by unsafe packet' : 'missing'} |`,
+ `| Stale output detection | ${blockerCodes.includes('STALE_NOTEBOOK_OUTPUT') ? 'covered by unsafe packet' : 'missing'} |`,
+ `| Section-version drift | ${blockerCodes.includes('SECTION_VERSION_MISMATCH') ? 'covered by unsafe packet' : 'missing'} |`,
+ `| Rich HTML sanitization | ${sanitizedModelCell?.outputs?.[0]?.trusted === false ? 'sanitized and marked untrusted' : 'missing'} |`,
+ `| Private path redaction | ${/\[redacted-local-path]/.test(sanitizedLoadCell?.outputs?.[0]?.content || '') ? 'redacted in output packet' : 'missing'} |`,
+ '',
+ '## Deterministic Digests',
+ '',
+ `- Unsafe packet digest: ${unsafeReview.auditDigest}`,
+ `- Warning packet digest: ${warningReview.auditDigest}`,
+ `- Clean packet digest: ${cleanReview.auditDigest}`,
+ '',
+ '## Local Command',
+ '',
+ '```bash',
+ 'npm run check',
+ '```'
+ ];
+
+ fs.writeFileSync(path.join(reportsDir, 'maintainer-verification-packet.md'), `${lines.join('\n')}\n`);
+}
+
+function primaryAction(packet) {
+ return (
+ packet.actions.find((action) => action.startsWith('hold_notebook_outputs')) ||
+ packet.actions.find((action) => action.startsWith('accept_notebook_outputs')) ||
+ packet.actions[0]
+ );
+}
+
+function writeSummarySvg(unsafeReview, warningReview, cleanReview) {
+ const cards = [
+ { label: 'Unsafe', packet: unsafeReview, color: '#b91c1c', x: 28 },
+ { label: 'Warning', packet: warningReview, color: '#b45309', x: 318 },
+ { label: 'Clean', packet: cleanReview, color: '#047857', x: 608 }
+ ];
+
+ const cardSvg = cards.map(({ label, packet, color, x }) => {
+ const summary = packet.reproducibilitySummary;
+ return [
+ ``,
+ `${label}`,
+ `${escapeXml(packet.status)}`,
+ `Cells: ${summary.cells}`,
+ `Blockers: ${summary.blockers}`,
+ `Warnings: ${summary.warnings}`
+ ].join('\n');
+ }).join('\n');
+
+ const svg = [
+ ''
+ ].join('\n');
+
+ fs.writeFileSync(path.join(reportsDir, 'summary.svg'), `${svg}\n`);
+}
+
+function escapeXml(value) {
+ return String(value)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"');
+}
diff --git a/collab-notebook-reproducibility-guard/index.js b/collab-notebook-reproducibility-guard/index.js
new file mode 100644
index 00000000..d6079e99
--- /dev/null
+++ b/collab-notebook-reproducibility-guard/index.js
@@ -0,0 +1,350 @@
+const crypto = require('crypto');
+
+function assessNotebookOutputPacket(packet) {
+ const cells = packet.cells || [];
+ const orderFindings = assessExecutionOrder(cells);
+ const assessedCells = cells.map((cell) => assessCell(cell, packet));
+ const findings = [
+ ...orderFindings,
+ ...assessedCells.flatMap((item) => item.findings)
+ ].sort(compareFindings);
+
+ const status = chooseStatus(findings);
+ const reviewPacket = {
+ packetId: packet.packetId,
+ workspaceId: packet.workspaceId,
+ manuscriptId: packet.manuscriptId,
+ status,
+ acceptanceLanes: chooseAcceptanceLanes(status),
+ assessedAt: packet.receivedAt,
+ cells: assessedCells.map((item) => item.sanitizedCell),
+ findings,
+ actions: buildActions(packet, findings),
+ reproducibilitySummary: buildSummary(cells, findings)
+ };
+
+ reviewPacket.auditDigest = digestPacket(reviewPacket);
+ return reviewPacket;
+}
+
+function assessCell(cell, packet) {
+ const findings = [];
+ const sanitizedCell = sanitizeCellBase(cell);
+
+ if (!cell.kernel || !cell.kernel.name || !cell.kernel.version) {
+ findings.push(finding({
+ code: 'MISSING_KERNEL_VERSION',
+ severity: 'blocker',
+ cellId: cell.id,
+ message: 'Notebook output is missing the kernel name or version used to produce it.'
+ }));
+ } else if (!cell.kernel.runtimeDigest) {
+ findings.push(finding({
+ code: 'MISSING_RUNTIME_DIGEST',
+ severity: 'warning',
+ cellId: cell.id,
+ message: 'Kernel version is present but the runtime digest was not captured.'
+ }));
+ }
+
+ const expectedLock = expectedDependencyLock(packet, cell);
+ if (expectedLock && cell.dependencyLock?.digest !== expectedLock.digest) {
+ findings.push(finding({
+ code: 'DEPENDENCY_LOCK_MISMATCH',
+ severity: 'blocker',
+ cellId: cell.id,
+ message: `Notebook output was created with dependency lock ${cell.dependencyLock?.digest || 'missing'}, expected ${expectedLock.digest}.`
+ }));
+ }
+
+ if (cell.stochastic && !cell.randomSeed) {
+ findings.push(finding({
+ code: 'MISSING_RANDOM_SEED',
+ severity: 'blocker',
+ cellId: cell.id,
+ message: 'Stochastic notebook cell output cannot be accepted without a captured random seed.'
+ }));
+ }
+
+ const missingInputs = (cell.inputs || []).filter((input) => !input.digest);
+ if (missingInputs.length) {
+ findings.push(finding({
+ code: 'MISSING_INPUT_FINGERPRINT',
+ severity: 'blocker',
+ cellId: cell.id,
+ message: `${missingInputs.length} notebook input artifact(s) lack a content fingerprint.`
+ }));
+ }
+
+ if (isStaleOutput(cell, packet, expectedLock)) {
+ findings.push(finding({
+ code: 'STALE_NOTEBOOK_OUTPUT',
+ severity: 'blocker',
+ cellId: cell.id,
+ message: 'Notebook output predates the current data, dependency, or manuscript section revision.'
+ }));
+ }
+
+ if (hasUntrustedRichHtml(cell.outputs)) {
+ findings.push(finding({
+ code: 'UNTRUSTED_RICH_HTML_OUTPUT',
+ severity: 'blocker',
+ cellId: cell.id,
+ message: 'Notebook rich output contains untrusted HTML or scriptable attributes.'
+ }));
+ sanitizedCell.outputs = sanitizeOutputs(cell.outputs || []);
+ }
+
+ if (containsPrivatePathInOutputs(cell.outputs)) {
+ findings.push(finding({
+ code: 'PRIVATE_OUTPUT_PATH',
+ severity: 'blocker',
+ cellId: cell.id,
+ message: 'Notebook output references a local or private filesystem path.'
+ }));
+ sanitizedCell.outputs = sanitizeOutputs(sanitizedCell.outputs || cell.outputs || []);
+ }
+
+ const currentSectionVersion = packet.currentSectionVersions?.[cell.sectionId];
+ if (currentSectionVersion && cell.sectionVersion !== currentSectionVersion) {
+ findings.push(finding({
+ code: 'SECTION_VERSION_MISMATCH',
+ severity: 'blocker',
+ cellId: cell.id,
+ message: `Output targets section version ${cell.sectionVersion || 'missing'}, expected ${currentSectionVersion}.`
+ }));
+ }
+
+ sanitizedCell.reviewState = findings.length ? 'reproducibility_review_required' : 'ready_for_collaborative_acceptance';
+ return { sanitizedCell, findings };
+}
+
+function assessExecutionOrder(cells) {
+ const findings = [];
+ const byNotebook = new Map();
+
+ for (const cell of cells) {
+ const key = cell.notebookId || 'unknown-notebook';
+ if (!byNotebook.has(key)) byNotebook.set(key, []);
+ byNotebook.get(key).push(cell);
+ }
+
+ for (const [notebookId, notebookCells] of byNotebook.entries()) {
+ const counts = new Map();
+ for (const cell of notebookCells) {
+ if (!Number.isInteger(cell.executionCount) || cell.executionCount <= 0) {
+ findings.push(finding({
+ code: 'INVALID_EXECUTION_COUNT',
+ severity: 'blocker',
+ cellId: cell.id,
+ message: `Notebook ${notebookId} cell has no positive execution counter.`
+ }));
+ continue;
+ }
+ counts.set(cell.executionCount, (counts.get(cell.executionCount) || 0) + 1);
+ }
+
+ for (const cell of notebookCells) {
+ if (counts.get(cell.executionCount) > 1) {
+ findings.push(finding({
+ code: 'DUPLICATE_EXECUTION_COUNT',
+ severity: 'blocker',
+ cellId: cell.id,
+ message: `Execution count ${cell.executionCount} appears more than once in ${notebookId}.`
+ }));
+ }
+ }
+
+ const ordered = notebookCells
+ .filter((cell) => Number.isInteger(cell.executionCount))
+ .sort((a, b) => a.executionCount - b.executionCount);
+ for (let index = 1; index < ordered.length; index += 1) {
+ if (ordered[index].executionCount !== ordered[index - 1].executionCount + 1) {
+ findings.push(finding({
+ code: 'EXECUTION_ORDER_GAP',
+ severity: 'blocker',
+ cellId: ordered[index].id,
+ message: `Notebook ${notebookId} output sequence is not a clean rerun from a continuous kernel.`
+ }));
+ break;
+ }
+ }
+ }
+
+ return findings;
+}
+
+function sanitizeCellBase(cell) {
+ const sanitized = clone(cell);
+ sanitized.outputs = sanitizeOutputs(cell.outputs || []);
+ return sanitized;
+}
+
+function sanitizeOutputs(outputs) {
+ return outputs.map((output) => {
+ const sanitized = { ...output };
+ if (typeof sanitized.content === 'string') {
+ sanitized.content = stripUnsafeHtml(redactPrivatePaths(sanitized.content));
+ }
+ if (sanitized.mime === 'text/html') {
+ sanitized.trusted = false;
+ }
+ return sanitized;
+ });
+}
+
+function hasUntrustedRichHtml(outputs = []) {
+ return outputs.some((output) => (
+ output.mime === 'text/html' &&
+ (output.trusted !== true || /',
+ trusted: false
+ }
+ ]
+ },
+ {
+ id: 'cell-render-figure',
+ notebookId: 'nb-response-model',
+ sectionId: 'results',
+ sectionVersion: 'sec-results-v9',
+ executionCount: 14,
+ lastExecutedAt: '2026-05-28T10:04:00Z',
+ kernel: {
+ name: 'python',
+ version: '3.11.8',
+ runtimeDigest: 'sha256:runtime-current'
+ },
+ dependencyLock: {
+ manager: 'pip-tools',
+ digest: 'sha256:lockfile-current-77a'
+ },
+ stochastic: false,
+ inputs: [
+ { id: 'model-summary', digest: 'sha256:model-summary' }
+ ],
+ outputs: [
+ {
+ mime: 'image/svg+xml',
+ content: '',
+ trusted: true
+ }
+ ]
+ }
+ ]
+};
+
+const warningPacket = {
+ packetId: 'notebook-output-packet-warning',
+ workspaceId: 'workspace-neuro-42',
+ manuscriptId: 'ms-neuro-response',
+ receivedAt: '2026-05-28T10:25:00Z',
+ currentDataRevisionAt: '2026-05-28T09:40:00Z',
+ currentNotebookRevisionAt: '2026-05-28T09:45:00Z',
+ currentSectionVersions: {
+ methods: 'sec-methods-v3'
+ },
+ acceptedDependencyLocks: {
+ 'nb-methods-audit': {
+ digest: 'sha256:lockfile-methods',
+ updatedAt: '2026-05-28T09:41:00Z'
+ }
+ },
+ cells: [
+ {
+ id: 'cell-methods-summary',
+ notebookId: 'nb-methods-audit',
+ sectionId: 'methods',
+ sectionVersion: 'sec-methods-v3',
+ executionCount: 1,
+ lastExecutedAt: '2026-05-28T10:20:00Z',
+ kernel: {
+ name: 'python',
+ version: '3.11.8'
+ },
+ dependencyLock: {
+ manager: 'pip-tools',
+ digest: 'sha256:lockfile-methods'
+ },
+ stochastic: false,
+ inputs: [
+ { id: 'protocol-yaml', digest: 'sha256:protocol-v3' }
+ ],
+ outputs: [
+ {
+ mime: 'text/markdown',
+ content: 'Protocol audit matched the locked methods section.',
+ trusted: true
+ }
+ ]
+ }
+ ]
+};
+
+const cleanPacket = {
+ packetId: 'notebook-output-packet-clean',
+ workspaceId: 'workspace-neuro-42',
+ manuscriptId: 'ms-neuro-response',
+ receivedAt: '2026-05-28T10:35:00Z',
+ currentDataRevisionAt: '2026-05-28T09:40:00Z',
+ currentNotebookRevisionAt: '2026-05-28T09:45:00Z',
+ currentSectionVersions: {
+ results: 'sec-results-v9'
+ },
+ acceptedDependencyLocks: {
+ 'nb-response-model': {
+ digest: 'sha256:lockfile-current-77a',
+ updatedAt: '2026-05-28T09:50:00Z'
+ }
+ },
+ cells: [
+ {
+ id: 'cell-clean-load',
+ notebookId: 'nb-response-model',
+ sectionId: 'results',
+ sectionVersion: 'sec-results-v9',
+ executionCount: 1,
+ lastExecutedAt: '2026-05-28T10:05:00Z',
+ kernel: {
+ name: 'python',
+ version: '3.11.8',
+ runtimeDigest: 'sha256:runtime-current'
+ },
+ dependencyLock: {
+ manager: 'pip-tools',
+ digest: 'sha256:lockfile-current-77a'
+ },
+ stochastic: false,
+ inputs: [
+ { id: 'cohort-export', digest: 'sha256:cohort-stable' }
+ ],
+ outputs: [
+ {
+ mime: 'text/plain',
+ content: 'Loaded 128 anonymized rows from cohort artifact sha256:cohort-stable.',
+ trusted: true
+ }
+ ]
+ },
+ {
+ id: 'cell-clean-fit',
+ notebookId: 'nb-response-model',
+ sectionId: 'results',
+ sectionVersion: 'sec-results-v9',
+ executionCount: 2,
+ lastExecutedAt: '2026-05-28T10:08:00Z',
+ kernel: {
+ name: 'python',
+ version: '3.11.8',
+ runtimeDigest: 'sha256:runtime-current'
+ },
+ dependencyLock: {
+ manager: 'pip-tools',
+ digest: 'sha256:lockfile-current-77a'
+ },
+ stochastic: true,
+ randomSeed: {
+ value: 20260528,
+ capturedAt: '2026-05-28T10:07:59Z'
+ },
+ inputs: [
+ { id: 'cohort-export', digest: 'sha256:cohort-stable' }
+ ],
+ outputs: [
+ {
+ mime: 'text/markdown',
+ content: 'Model AUC: 0.82. Bootstrap CI generated with seed 20260528.',
+ trusted: true
+ }
+ ]
+ }
+ ]
+};
+
+module.exports = {
+ unsafePacket,
+ warningPacket,
+ cleanPacket
+};
diff --git a/collab-notebook-reproducibility-guard/test.js b/collab-notebook-reproducibility-guard/test.js
new file mode 100644
index 00000000..0132736e
--- /dev/null
+++ b/collab-notebook-reproducibility-guard/test.js
@@ -0,0 +1,81 @@
+const assert = require('assert');
+
+const { assessNotebookOutputPacket } = require('./index');
+const { cleanPacket, unsafePacket, warningPacket } = require('./sample-data');
+
+function findingCodes(packet) {
+ return packet.findings.map((finding) => finding.code).sort();
+}
+
+function testHoldsUnsafeNotebookOutputsBeforeManuscriptAcceptance() {
+ const packet = assessNotebookOutputPacket(unsafePacket);
+
+ assert.equal(packet.status, 'hold_notebook_outputs');
+ assert.equal(packet.acceptanceLanes.manuscriptInsertion, 'blocked');
+ assert.equal(packet.acceptanceLanes.reviewerPreview, 'sanitized');
+ assert.equal(packet.acceptanceLanes.auditRetention, 'reproducibility_hold');
+ assert.deepEqual(findingCodes(packet), [
+ 'DEPENDENCY_LOCK_MISMATCH',
+ 'DUPLICATE_EXECUTION_COUNT',
+ 'DUPLICATE_EXECUTION_COUNT',
+ 'EXECUTION_ORDER_GAP',
+ 'MISSING_INPUT_FINGERPRINT',
+ 'MISSING_RANDOM_SEED',
+ 'MISSING_RUNTIME_DIGEST',
+ 'PRIVATE_OUTPUT_PATH',
+ 'SECTION_VERSION_MISMATCH',
+ 'STALE_NOTEBOOK_OUTPUT',
+ 'UNTRUSTED_RICH_HTML_OUTPUT'
+ ]);
+
+ assert.ok(packet.actions.includes('hold_notebook_outputs:notebook-output-packet-unsafe'));
+ assert.ok(packet.actions.includes('rerun_notebook_from_clean_kernel'));
+ assert.ok(packet.actions.includes('reset_kernel_and_run_all_cells'));
+ assert.ok(packet.actions.includes('sanitize_rich_output:cell-fit-model'));
+ assert.ok(packet.actions.includes('redact_private_output_paths:cell-load-data'));
+ assert.match(packet.auditDigest, /^[a-f0-9]{64}$/);
+
+ const loadCell = packet.cells.find((cell) => cell.id === 'cell-load-data');
+ const modelCell = packet.cells.find((cell) => cell.id === 'cell-fit-model');
+ assert.match(loadCell.outputs[0].content, /\[redacted-local-path]/);
+ assert.equal(modelCell.outputs[0].trusted, false);
+ assert.doesNotMatch(modelCell.outputs[0].content, /