Skip to content

Opt-in Canvas publish via external canvas-pp-cli (PRD §17.5) - #11

Open
johnnyrobot wants to merge 2 commits into
mainfrom
feat/canvas-pushback
Open

Opt-in Canvas publish via external canvas-pp-cli (PRD §17.5)#11
johnnyrobot wants to merge 2 commits into
mainfrom
feat/canvas-pushback

Conversation

@johnnyrobot

@johnnyrobot johnnyrobot commented Jul 16, 2026

Copy link
Copy Markdown
Owner

Implements the approved product decision (2026-07-16): Canvas stays read-only by default; publishing a remediated page back is explicit opt-in, delegated to the separately installed canvas-pp-cli.

Guardrails (all must hold, else the path is invisible)

  1. External canvas-pp-cli detected on PATH (probe: agent-context) — the binary is not bundled, deliberately
  2. Persisted Allow publishing to Canvas toggle (default off; on the Canvas connect screen)
  3. Page was imported from Canvas this session (pasted HTML has no publish target)
  4. Server-side gate re-check at publish time — the runtime re-runs the accessibility gate on the exact HTML; a withheld badge refuses the publish (test-asserted)
  5. Two-step per-page confirm on the review panel, with the before/after diff on screen
  6. Host-match preflight (doctorbase_url must equal the import host) so a stale setting can never push to the wrong Canvas

Audit trail

New canvas_publishes table (schema v4): course, page, SHA-256 of the pushed HTML, timestamp. Receipt returned to the UI.

Discipline

  • src/canvas/publish.ts mirrors the catalog client: arg-array spawn (no shell), HTML over stdin (never argv), validated ids, typed errors, timeouts
  • src/canvas/http.ts remains GET-only by construction — untouched, tests still assert it
  • AppApi +3 methods, implemented in all five implementers + IPC transport per the growth law

Verification

  • npm run verify: 657 tests, 600 pass / 0 fail / 57 skip, tsc clean
  • Scripted Electron UI matrix: 16/16 pass
  • pages update --stdin --agent --dry-run verified against the real CLI (request shape + auth + host)

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Added an opt-in “Publish to Canvas” flow for repaired pages, with per-page confirmation and accessibility gate checks.
    • Added a persistent enablement toggle that’s available only when canvas-pp-cli is installed.
    • Recorded successful publish receipts to an on-device audit log for traceability.
  • Documentation
    • Clarified that ingestion runs entirely on-device, with only Canvas read access during remediation and opt-in publishing via canvas-pp-cli using a stored token.

…s-pp-cli

Implements the approved PRD §17.5 decision: Canvas stays read-only by
default and the in-app client stays GET-only by construction; publishing
a remediated page is an explicit opt-in that shells out to the
separately installed, separately authenticated canvas-pp-cli.

Guardrails, all enforced:
- publish path is invisible unless the CLI binary is detected AND the
  persisted 'Allow publishing to Canvas' toggle (default off) is on
- only Canvas-imported pages can publish (pasted HTML has no target)
- the runtime re-runs the accessibility gate on the exact HTML at
  publish time - a withheld badge refuses the publish (test-asserted)
- host-match preflight via 'canvas-pp-cli doctor': the CLI's configured
  Canvas must equal the host the page was imported from
- two-step per-page confirm on the review panel, diff on screen
- every publish is recorded in the new canvas_publishes audit table
  (course, page, SHA-256 of pushed HTML, timestamp) - schema v4

Transport discipline mirrors src/catalog/client.ts: arg-array spawn
(never a shell), page HTML over stdin (never argv), validated ids,
typed PublishError, timeout-bounded. AppApi grows by three methods
(canvasPublishStatus / setCanvasPublishEnabled / publishCanvasPage),
implemented across all five implementers + IPC transport per the
growth law.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an opt-in Canvas publishing flow using canvas-pp-cli, with accessibility-gate validation, per-page confirmation, IPC/API wiring, renderer controls, publish receipts, and CLI error handling.

Changes

Canvas publishing

Layer / File(s) Summary
Canvas CLI publisher
src/canvas/publish.ts, src/canvas/publish.test.ts
Adds validated, timeout-bounded CLI execution with host matching, STDIN HTML transport, typed errors, and adapter tests.
Runtime publish contract and audit
src/contracts/index.ts, src/runtime/app-api.ts, src/runtime/app-api.test.ts, src/storage/schema.ts
Adds publish contracts, persisted enablement, gate re-checking, SHA-256 receipts, and the canvas_publishes audit table.
App API implementations
src/app/e2e-api.ts, src/app/stub-api.ts, src/app/unavailable-api.ts
Extends test, stub, and unavailable APIs with publish status, enablement, and page publishing behavior.
IPC and renderer bridge wiring
src/app/channels.ts, src/app/ipc.ts, src/app/bridge.ts, src/app/*test.ts
Adds three Canvas publish channels, handlers, bridge methods, and coverage for arguments and envelopes.
Renderer publish controls
src/app/renderer/renderer.ts, src/app/renderer/remediation.ts, README.md
Adds opt-in settings, CLI availability handling, eligibility checks, two-step confirmation, publish state management, and updated workflow documentation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Reviewer
  participant Renderer
  participant IPC
  participant RuntimeAppApi
  participant CanvasPublisher
  participant CanvasCLI
  Reviewer->>Renderer: Enable Canvas publishing
  Renderer->>IPC: setCanvasPublishEnabled(true)
  IPC->>RuntimeAppApi: Persist enablement
  Reviewer->>Renderer: Confirm page publish
  Renderer->>IPC: publishCanvasPage(baseUrl, courseId, pageId, html)
  IPC->>RuntimeAppApi: Re-audit HTML and publish
  RuntimeAppApi->>CanvasPublisher: publishPage(...)
  CanvasPublisher->>CanvasCLI: doctor and pages update
  CanvasCLI-->>CanvasPublisher: Updated page response
  CanvasPublisher-->>RuntimeAppApi: Canvas URL
  RuntimeAppApi-->>Renderer: Publish receipt
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: opt-in Canvas publishing through the external canvas-pp-cli.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/canvas-pushback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Around line 19-21: Update the README text describing the opt-in canvas-pp-cli
publishing workflow to state that it communicates directly with the configured
Canvas host, while clarifying that course data is not sent to an app-operated
cloud service. Preserve the existing per-page confirmation and
accessibility-gate requirements.

In `@src/app/e2e-api.ts`:
- Around line 435-446: The publish receipts in publishCanvasPage within
src/app/e2e-api.ts lines 435-446 and src/app/stub-api.ts lines 377-389 must use
the SHA-256 hexadecimal digest of the exact html instead of an HTML-length
marker. Update both implementations to compute and return the digest in
contentHash while preserving the remaining receipt fields and behavior.

In `@src/app/renderer/renderer.ts`:
- Around line 2107-2115: Replace currentPublishTarget’s reconstruction from
mutable Canvas state with an immutable publish-target snapshot captured when the
Canvas import/remediation succeeds. Clear that snapshot for non-Canvas runs, and
update the publish flow to use only the snapshot so later changes to
canvasBaseUrl, canvasCourseId, or selectedCanvasPageId cannot retarget the
repaired HTML.
- Around line 2096-2104: Update setPublishEnabled to serialize rapid
publishing-setting changes, preventing concurrent setCanvasPublishEnabled calls
from resolving out of order. Disable the related checkbox while the save is
pending or sequence requests so only the latest operation updates
state.publishStatus and state.error, while preserving the final render()
behavior.

In `@src/canvas/publish.ts`:
- Around line 197-204: Update available() to inspect the resolved result from
exec and return true only when its exitCode indicates success; continue
returning false for thrown errors and non-zero exits. Add a regression test
covering a resolved exec result with exitCode: 1 and verify the availability
result is false.
- Around line 56-94: Update defaultExec to listen for child.stdin errors and
settle the promise through the existing guarded cleanup path, preventing
unhandled errors when the CLI closes early. Update available() to consider the
probe’s exitCode and return false for non-zero exits, rather than treating every
resolved execution as available.

In `@src/runtime/app-api.ts`:
- Around line 806-818: Update the runtime around importCanvas and
publishCanvasPage to track each successfully imported (baseUrl, courseId,
pageId) tuple in current-session memory. Before calling publisher.publishPage,
require an exact tuple match and reject unimported pages; preserve the existing
publishing and accessibility checks. Add a direct API test verifying an
unimported page is rejected and publisher.publishPage is not invoked.
- Around line 818-831: Update the publish flow around publisher.publishPage and
the canvas_publishes insert to persist a pending audit intent before invoking
the CLI, then finalize that intent after Canvas succeeds. If finalization fails,
retain an explicit indeterminate audit state and do not report the operation as
a normal failed publish or encourage an unsafe retry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8c7cf15c-5f74-43dd-9fe0-3884bbbb9869

📥 Commits

Reviewing files that changed from the base of the PR and between 69c5a77 and 36d7fa0.

📒 Files selected for processing (18)
  • README.md
  • src/app/bridge.test.ts
  • src/app/bridge.ts
  • src/app/channels.test.ts
  • src/app/channels.ts
  • src/app/e2e-api.ts
  • src/app/ipc.test.ts
  • src/app/ipc.ts
  • src/app/renderer/remediation.ts
  • src/app/renderer/renderer.ts
  • src/app/stub-api.ts
  • src/app/unavailable-api.ts
  • src/canvas/publish.test.ts
  • src/canvas/publish.ts
  • src/contracts/index.ts
  • src/runtime/app-api.test.ts
  • src/runtime/app-api.ts
  • src/storage/schema.ts

Comment thread README.md
Comment thread src/app/e2e-api.ts
Comment on lines +435 to +446
async publishCanvasPage(_baseUrl, courseId, pageId, html): Promise<CanvasPublishReceipt> {
failIfDown();
if (!e2ePublishEnabled) {
throw new Error('Publishing to Canvas is disabled. Turn on "Allow publishing to Canvas" first.');
}
return {
courseId,
pageId,
contentHash: `e2e-${html.length.toString(16)}`,
publishedAt: new Date().toISOString(),
canvasUrl: `https://e2e.instructure.test/courses/${courseId}/pages/${pageId}`,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Make test-double publish receipts honor the SHA-256 contract.

Both implementations substitute HTML length for the required SHA-256 hexadecimal digest, allowing integration tests and stub workflows to accept invalid receipt semantics.

  • src/app/e2e-api.ts#L435-L446: compute the SHA-256 digest of the exact html.
  • src/app/stub-api.ts#L377-L389: return the same contract-valid digest rather than a length marker.
📍 Affects 2 files
  • src/app/e2e-api.ts#L435-L446 (this comment)
  • src/app/stub-api.ts#L377-L389
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/e2e-api.ts` around lines 435 - 446, The publish receipts in
publishCanvasPage within src/app/e2e-api.ts lines 435-446 and
src/app/stub-api.ts lines 377-389 must use the SHA-256 hexadecimal digest of the
exact html instead of an HTML-length marker. Update both implementations to
compute and return the digest in contentHash while preserving the remaining
receipt fields and behavior.

Comment thread src/app/renderer/renderer.ts
Comment on lines +2107 to +2115
/** The Canvas identity of the current remediation, when it was a live import. */
function currentPublishTarget(): { baseUrl: string; courseId: string; pageId: string } | undefined {
if (state.sourceMode !== 'canvas') return undefined;
const baseUrl = state.canvasBaseUrl.trim();
const courseId = state.canvasCourseId.trim();
const pageId = state.selectedCanvasPageId;
if (!baseUrl || !courseId || !pageId) return undefined;
return { baseUrl, courseId, pageId };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Bind the publish target to the completed Canvas import.

This reconstructs the target from mutable canvasBaseUrl, canvasCourseId, and selectedCanvasPageId state rather than the identity that produced remediateView. Changing the selection after remediation can therefore send one page’s repaired HTML to another imported page.

Capture an immutable target when import/remediation succeeds, clear it for non-Canvas runs, and publish only against that snapshot.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/renderer/renderer.ts` around lines 2107 - 2115, Replace
currentPublishTarget’s reconstruction from mutable Canvas state with an
immutable publish-target snapshot captured when the Canvas import/remediation
succeeds. Clear that snapshot for non-Canvas runs, and update the publish flow
to use only the snapshot so later changes to canvasBaseUrl, canvasCourseId, or
selectedCanvasPageId cannot retarget the repaired HTML.

Comment thread src/canvas/publish.ts
Comment thread src/canvas/publish.ts
Comment thread src/runtime/app-api.ts
Comment on lines +806 to +818
async publishCanvasPage(baseUrl, courseId, pageId, html) {
if (!(await publishEnabledSetting())) {
throw new Error('Publishing to Canvas is disabled. Turn on "Allow publishing to Canvas" first.');
}
// Server-side re-check of the accessibility gate on the EXACT HTML that
// would go out — a withheld badge refuses the publish regardless of what
// the renderer showed. The gated (allowlist-sanitized) html is what ships.
const gate = await enforceGate(html, gateDeps);
if (gate.badgeWithheld) {
const first = gate.conformance.blockers[0]?.message ?? 'accessibility blockers remain';
throw new Error(`Refused to publish: the accessibility gate withheld the badge (${first}).`);
}
const { canvasUrl } = await publisher.publishPage({ baseUrl, courseId, pageId, html: gate.html });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Enforce current-session import provenance in the runtime.

The method accepts arbitrary baseUrl, courseId, and pageId values without proving that this exact page was imported during the current session. Renderer checks can be bypassed through the API/IPC boundary, violating the publish guardrail.

Track imported page tuples in memory when importCanvas succeeds, then reject publishing unless the target tuple exists. Add a direct API test confirming an unimported page never reaches publisher.publishPage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/app-api.ts` around lines 806 - 818, Update the runtime around
importCanvas and publishCanvasPage to track each successfully imported (baseUrl,
courseId, pageId) tuple in current-session memory. Before calling
publisher.publishPage, require an exact tuple match and reject unimported pages;
preserve the existing publishing and accessibility checks. Add a direct API test
verifying an unimported page is rejected and publisher.publishPage is not
invoked.

Comment thread src/runtime/app-api.ts
Comment on lines +818 to +831
const { canvasUrl } = await publisher.publishPage({ baseUrl, courseId, pageId, html: gate.html });
const receipt = {
courseId,
pageId,
contentHash: createHash('sha256').update(gate.html).digest('hex'),
publishedAt: new Date().toISOString(),
canvasUrl,
};
const db = await database();
await db.run(
`INSERT INTO canvas_publishes (course_id, page_id, content_hash, canvas_url, published_at)
VALUES (?, ?, ?, ?, ?)`,
[receipt.courseId, receipt.pageId, receipt.contentHash, receipt.canvasUrl, receipt.publishedAt],
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not report a failed publish after Canvas was already updated.

The external update succeeds before the audit insert. If SQLite then fails—such as from a full or locked disk—the method rejects even though Canvas changed and no receipt was recorded, encouraging an unsafe retry.

Persist a pending audit intent before invoking the CLI, finalize it after success, and retain an explicit indeterminate state if finalization fails.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/app-api.ts` around lines 818 - 831, Update the publish flow
around publisher.publishPage and the canvas_publishes insert to persist a
pending audit intent before invoking the CLI, then finalize that intent after
Canvas succeeds. If finalization fails, retain an explicit indeterminate audit
state and do not report the operation as a normal failed publish or encourage an
unsafe retry.

- available(): our spawn-based exec resolves on non-zero exit (unlike the
  catalog client's execFile), so a present-but-broken binary read as
  available. Gate on exitCode === 0. (+ regression test)
- stdin: register an error listener before writing so an early child-close
  EPIPE can't become an unhandled exception; use end(data).
- preflight: compare the FULL normalized base (scheme + host + path), not
  just the host, and build the receipt URL from the configured base — fixes
  http/https and path-hosted Canvas instances. (+ scheme-mismatch test)
- renderer: never publish an empty body (would blank the live page) — guard
  htmlAfter before the write; drop the '' fallback.
- renderer: disable the publish toggle while a save is in flight so rapid
  clicks can't persist out of order.
- ipc: reject a non-boolean setCanvasPublishEnabled payload instead of
  coercing it (this toggle gates a write capability).
- stub/e2e AppApi: return a real SHA-256 contentHash, matching the contract.
- README: correct the blanket 'nothing leaves the device' claim — AI and
  doc processing are local, but Canvas import/publish talk to the connected
  Canvas.

Skipped (verified not actionable): CodeRabbit's 'duplicate rows' critical is
a false positive (single declaration, compiles, tests green); a publish/
setEnabled mutex is disproportionate for a single-user desktop app already
guarded by publishBusy + the server-side gate re-check.

Verify 602/0, Electron matrix 16/16, tsc clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@johnnyrobot

Copy link
Copy Markdown
Owner Author

CodeRabbit review addressed (commit 28fe071d)

Ran coderabbit review --agent --base main — 12 findings, each verified against the code. 10 fixed, 2 skipped with cause.

Fixed

  • publish.ts available() — the spawn-based exec resolves on a non-zero exit (unlike the catalog client's execFile, which rejects), so a present-but-broken binary read as available. Now gates on exitCode === 0 (+ regression test).
  • publish.ts stdin — added an error listener before writing so an early child-close EPIPE can't crash the process; uses end(data).
  • publish.ts preflight — compares the full normalized base (scheme + host + path), not just host, and builds the receipt URL from the configured base. Fixes http/https and path-hosted Canvas (+ scheme-mismatch test).
  • renderer — never publishes an empty body (would blank the live page); guards htmlAfter and drops the '' fallback.
  • renderer — disables the publish toggle while a save is in flight (no out-of-order persistence).
  • ipc — rejects a non-boolean setCanvasPublishEnabled payload instead of coercing.
  • stub + e2e AppApi — return a real SHA-256 contentHash matching the contract.
  • README — corrected the blanket "nothing leaves the device" claim: AI + doc processing are local; Canvas import/publish talk to the connected Canvas.

Skipped (verified not actionable)

  • Critical: "duplicate rows declarations"false positive. Lines 863–865 hold a single const rows in that test's own scope; the file compiles and the suite passes 44/44.
  • Major: publish/setEnabled mutex — disproportionate for a single-user desktop app. The write is already guarded by publishBusy in the UI and a server-side gate re-check on the exact HTML; an already-authorized publish completing during a concurrent disable is harmless.

Verify 602 / 0 fail, Electron matrix 16/16, tsc clean.

🤖 Generated with Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/app/stub-api.ts (1)

372-390: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Keep stub publishing preconditions aligned with the runtime contract.

The runtime re-runs the accessibility gate on the exact HTML before publishing, but this stub only checks the toggle and hashes raw html. Renderer/E2E tests using this implementation can therefore pass empty or ungated content that production rejects. Reuse shared validation where practical, or add explicit negative-case tests documenting the intentional difference.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/stub-api.ts` around lines 372 - 390, Update publishCanvasPage to
enforce the same accessibility validation precondition as the runtime before
hashing or returning the publish result, using the shared validation helper if
available. Preserve the existing stubPublishEnabled check and validate the exact
html passed to publishing so empty or ungated content is rejected consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@README.md`:
- Line 11: Update the README Requirements section and the publishing
instructions to consistently describe canvas-pp-cli as an optional prerequisite
required only for publishing, while preserving the statement that no additional
installation is needed for users who do not publish.
- Around line 7-12: Update the privacy statement near the language-model and
document-ingestion description to avoid claiming Canvas is the only network
destination; either scope the claim specifically to Canvas import/publishing
traffic or explicitly include user-provided URL ingestion as an exception,
consistent with the later SSRF documentation.

---

Nitpick comments:
In `@src/app/stub-api.ts`:
- Around line 372-390: Update publishCanvasPage to enforce the same
accessibility validation precondition as the runtime before hashing or returning
the publish result, using the shared validation helper if available. Preserve
the existing stubPublishEnabled check and validate the exact html passed to
publishing so empty or ungated content is rejected consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 06f08be3-162b-404a-ac34-90d59fd3104c

📥 Commits

Reviewing files that changed from the base of the PR and between 36d7fa0 and 28fe071.

📒 Files selected for processing (8)
  • README.md
  • src/app/e2e-api.ts
  • src/app/ipc.ts
  • src/app/renderer/renderer.ts
  • src/app/stub-api.ts
  • src/canvas/publish.test.ts
  • src/canvas/publish.ts
  • src/runtime/app-api.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/app/ipc.ts
  • src/app/e2e-api.ts
  • src/runtime/app-api.test.ts
  • src/canvas/publish.test.ts
  • src/app/renderer/renderer.ts
  • src/canvas/publish.ts

Comment thread README.md
Comment on lines +7 to +12
The language model and the document-ingestion pipeline both run on your machine
as bundled sidecars — no cloud AI service and no telemetry. The only network
calls are to the Canvas instance you choose to connect: reading pages to
remediate, and — strictly opt-in — publishing a repaired page back via the
separately installed `canvas-pp-cli`. Your Canvas token stays in the macOS
Keychain and is never sent anywhere else.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Avoid claiming Canvas is the only network destination.

The privacy section later documents URL ingestion and SSRF protection, implying that user-provided URLs may also be fetched. Narrow this claim to the Canvas import/publishing path or document the URL-ingestion exception.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 7 - 12, Update the privacy statement near the
language-model and document-ingestion description to avoid claiming Canvas is
the only network destination; either scope the claim specifically to Canvas
import/publishing traffic or explicitly include user-provided URL ingestion as
an exception, consistent with the later SSRF documentation.

Comment thread README.md
as bundled sidecars — no cloud AI service and no telemetry. The only network
calls are to the Canvas instance you choose to connect: reading pages to
remediate, and — strictly opt-in — publishing a repaired page back via the
separately installed `canvas-pp-cli`. Your Canvas token stays in the macOS

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Document the CLI prerequisite consistently.

This says publishing requires a separately installed canvas-pp-cli, while the Requirements section says end users install nothing else. List the CLI as an optional publishing prerequisite or qualify that statement.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` at line 11, Update the README Requirements section and the
publishing instructions to consistently describe canvas-pp-cli as an optional
prerequisite required only for publishing, while preserving the statement that
no additional installation is needed for users who do not publish.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant