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.js to the board page's script list, alongside the existing paned_board_reader.js.

    • templates/partials/paned_board_compose_panel.php: added data-compose-root to the panel's outer wrapper (confirmed no other attributes are needed — browser_signing.js doesn't reference data-unicode-authored-text/data-emoji-authored-text, which belong to an unrelated script).

  • Verification:

    • php -l clean on both changed files.

    • curl http://127.0.0.1:8001/forte — lazy_compose_signing.*.js present in the page; data-compose-root present on the compose panel.

    • Headless-browser test (Selenium + chromium-browser): openpgp_loader.js/browser_signing.js are absent from the page before any interaction, and load only once the reply body field is focused (window.ForumBrowserSigning becomes 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> since browser_signing.js queries it relative to data-compose-root, not the form itself).

    • public/assets/forte.css: added a small .paned-compose-status rule (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 -l clean.

    • 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 404 on /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 reused browser_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 optional returnTo; when non-empty, builds the target from it (joining the existing created_post_id/__v/#post-{id} suffix with & when returnTo already has its own query string, ? otherwise — mirroring the exact fix already applied server-side in this same codebase for resolveComposeReplyReturnTo's caller) instead of the hardcoded /threads/{threadId} path. navigateToCanonicalReply(result, returnTo) passes it through. The one call site reachable from Forte (submitSignedReplyAndNavigate) now passes composeFormFieldValue(form, "return_to") (an existing helper, reused rather than a new querySelector). The other call site (inside submitOptimisticReply, 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.js clean.

    • 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 reaches submitSignedReplyAndNavigate — it has data-compose-root but no data-inline-reply-details, same shape as Forte): its return_to field renders as value="" since it never passes a returnTo to reply_form.php; a live signed submission from that page still lands on http://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 on http://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, unlinked guest text — direct visual proof the feature distinguishes signed from anonymous authorship correctly.

    • Confirmed classic's compose_reply.php signed-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.