The edit form's legend read "Skill areas" and carried no directive, while the
application form read "Skill profile — 15 points to distribute" and said what to
do. Same rule, worded in two places — and the edit form lost the sentence.
- New static/focus-rule.js exposes window.O4PFocus: LEGEND, DIRECTIVE, TOTAL, MAX
and message(sum). Both forms render the legend, the directive and the
wrong-total message from it, so the two cannot drift apart again.
- Edit form: legend from the constant, the application form's directive under it,
and the blocked-save message now names the direction.
- Application form: legend set from the constant, live warn and sum gate use the
same message(); the all-zero gate keeps the directive.
- Message wording: over -> "remove N", under -> "add N", zero -> "place all 15
points"; the total, aria-live, data-bad, the blocked save and the
fieldset/legend structure are unchanged.
The rule text now appears once: "You have 17 of 15 — remove 2."
Card (odoo-community-developers/):
- "Ask question anonymously" moves into its own row ABOVE the links row, in the
runtime renderer and in build-directory.py (the build-time template).
- A cog icon button sits next to it: a real <button> with an accessible name
(aria-label="Edit or delete your profile"), same quiet weight as its neighbour.
- Pressing the cog opens a modal that explains the flow and asks for the email
address, then POSTs {"email":"..."} to odoo_experts_edit (body shape in one
constant, EDIT_EMAIL_KEY). The reply is never read: one message on any 2xx, so
the box cannot tell a visitor whether an address is on file. A non-2xx fails
honestly and shows no receipt. Both controls ship dark (added only while their
route constant is set), so neither is ever a dead control.
New private page (freelancer-edit/, noindex+nofollow, unlinked, absent from
sitemap.xml and llms.txt):
- Emailed link is https://odoo4projects.com/freelancer-edit/?has=<hash> — the
same shape as /freelancer-answer/; reads `has` first, falls back to `hash`.
- GETs the edit webhook with the hash and pre-fills the form: Name, Focus (<=70),
description (<=230), the five axis numbers (must total 15), the three language
numbers (0-5), Country, OCA_link. The GET reply is read through one alias map;
a shape it does not recognise names the fields it received instead of guessing.
404 "is not registered" is a CONFIGURATION error, never "your link is invalid";
404/500 "No item to return was found" is an invalid/expired link; a network
failure keeps the hash for a retry.
- Save = POST with the record id (one constant, SAVE_METHOD). Delete is a
deliberate second action (a confirm tick gates the button) and never reports
success on a non-2xx.
- The image is read-only here, stated in the UI: replacing a photo is a different
problem from editing text.
- The hash never reaches analytics: the query is stripped before the Plausible
snippet loads. referrer policy no-referrer; JS off says so plainly.