Repository source: docs/plans/archive/forte_identity_signing_step4_implementation_summary.md
Forte Identity Signing — Step 4: Implementation Summary
Stage 1 - Load lazy signing on the board view
Changes:
Application.php::renderForteBoard(): added/assets/lazy_compose_signing.jsto the board page's script list, alongside the existingpaned_board_reader.js.templates/partials/paned_board_compose_panel.php: addeddata-compose-rootto the panel's outer wrapper (confirmed no other attributes are needed —browser_signing.jsdoesn't referencedata-unicode-authored-text/data-emoji-authored-text, which belong to an unrelated script).
Verification:
php -lclean on both changed files.curl http://127.0.0.1:8001/forte—lazy_compose_signing.*.jspresent in the page;data-compose-rootpresent on the compose panel.Headless-browser test (Selenium +
chromium-browser):openpgp_loader.js/browser_signing.jsare absent from the page before any interaction, and load only once the reply body field is focused (window.ForumBrowserSigningbecomes defined at that point, not before). Zero console errors.
Notes:
Matches classic's own board-page loading strategy exactly, as decided in Step 1.
Stage 2 - Identity-status feedback element
Changes:
templates/partials/paned_board_compose_panel.php: added<p class="paned-compose-status" data-role="compose-identity-status" hidden>between the panel head and the form (same DOM hook classic uses; placed outside the<form>sincebrowser_signing.jsqueries it relative todata-compose-root, not the form itself).public/assets/forte.css: added a small.paned-compose-statusrule (secondary-text size/color matching the rest of the panel, bottom border matching the head) — a new Forte-scoped class rather than reusing classic's.meta, consistent with the same congruence approach used for.paned-compose-form.
Verification:
php -lclean.Headless-browser test (Selenium +
chromium-browser) driving a real signed-submission attempt (handling the native username-setup prompts along the way): the status element becomes visible with real progress text ("Publishing your public key in the background..."), styled with--paned-ink-soft(confirmed via computed style, matching the CSS rule).Bonus finding from the same test: the flow completed a real signed reply end-to-end (actual OpenPGP-style key generation ran in headless Chromium), which let me directly confirm the exact bug Stage 3 exists to fix — see below.
Notes:
Observed one
404on/api/get_profile?profile_slug=openpgp-...during the flow — this is an existing "does a profile already exist for this new key" probe inside the reusedbrowser_signing.js/backend, expected to 404 for a brand-new identity; not something introduced by this feature's changes.Confirmed the exact pre-fix bug live: the signed reply was created successfully, but the browser was hard-navigated to
http://127.0.0.1:8001/threads/root-001?created_post_id=...#post-...(classic's thread page) — completely bypassing Forte, exactly as Step 1/2 predicted. This is direct, reproduced evidence for Stage 3's fix, not just a theoretical concern.
Stage 3 - Respect return_to in the signed-reply navigation
Changes:
public/assets/browser_signing.js:canonicalReplyUrl(result, returnTo)now accepts an optionalreturnTo; when non-empty, builds the target from it (joining the existingcreated_post_id/__v/#post-{id}suffix with&whenreturnToalready has its own query string,?otherwise — mirroring the exact fix already applied server-side in this same codebase forresolveComposeReplyReturnTo's caller) instead of the hardcoded/threads/{threadId}path.navigateToCanonicalReply(result, returnTo)passes it through. The one call site reachable from Forte (submitSignedReplyAndNavigate) now passescomposeFormFieldValue(form, "return_to")(an existing helper, reused rather than a new querySelector). The other call site (insidesubmitOptimisticReply, only reachable via classic's inline-reply-composer markup, which Forte never has) was deliberately left untouched — out of scope, not reachable from Forte.
Verification:
node --check public/assets/browser_signing.jsclean.Re-ran the exact live signed-submission test from Stage 2: final URL is now
http://127.0.0.1:8001/forte?selected=root-001&created_post_id=...#post-...— Forte's own board view, not classic's thread page.Full restore verified for the signed submission: the replied-to thread is selected, the new reply is highlighted, and Reply is enabled — identical to the anonymous flow. The new reply's author label reads "guest (unapproved)" (a real, newly-created signed identity), confirming actual signed authorship, not just a redirect-target fix.
Regression check on classic's
compose_reply.php(which also reachessubmitSignedReplyAndNavigate— it hasdata-compose-rootbut nodata-inline-reply-details, same shape as Forte): itsreturn_tofield renders asvalue=""since it never passes areturnTotoreply_form.php; a live signed submission from that page still lands onhttp://127.0.0.1:8001/threads/root-001?created_post_id=...#post-..., byte-for-byte unchanged from before this patch.
Notes:
The live test's OpenPGP-style key generation ran successfully in headless Chromium, so this stage's verification is a real end-to-end signed flow, not a code-path-only check — the fallback plan noted as a risk in Step 3 wasn't needed.
Stage 4 - Full regression check
Changes: none (verification only, as planned).
Verification:
Re-ran the pre-existing anonymous board-reply round-trip test and discovered it now exercises the signed path instead — expected and correct: once signing is loaded, the plain "Post reply" button attempts signed posting (prompting for identity creation), matching classic's own long-standing behavior. Only the explicit "Post anonymous reply" button stays anonymous, same as classic.
Verified the explicit anonymous button directly: submits via the unchanged native
form.submit()path, lands onhttp://127.0.0.1:8001/forte?selected=root-001&created_post_id=..., zero console errors, zero signing prompts.Cross-checked authorship on the rendered board page: signed replies from Stages 2/3's tests render as
<a href="/profiles/openpgp-...">guest</a> (unapproved)(linked to a real, newly-created identity); the Stage 4 anonymous reply renders as plain, unlinkedguesttext — direct visual proof the feature distinguishes signed from anonymous authorship correctly.Confirmed classic's
compose_reply.phpsigned-reply flow is still completely unaffected (verified in Stage 3).
Notes:
This completes all 4 planned stages for
forte_identity_signing. Forte's board view now supports real signed authorship, with signed submissions landing back in Forte (tag filter, thread selection, and new-reply highlight all preserved) exactly like anonymous submissions already did.