The four 'version upgrades are run for you' lines (black-friday, hosting-offers,
hosting-2027 body + table row, llms.txt) and their JSON-LD descriptions now say
updates and in-version upgrades are included, and name a major-version migration
as a separate, quoted service. terms.html: migration = separate service, quoted
separately; release list no longer reads stale (names Odoo 20 as current).
No offer, price, page structure or ad change.
The archived two-offer alt named a Bootcamp line the current card does not carry
(re-rendered hosting-only in e6f0b6a). The meta + og:description do carry the Bootcamp
again (the page shows it); the image alt stays true to the image.
- Hero is the homepage's (same markup/assets): slide-in city image + the server
location selector below it that slides a chosen city in; headline adapted to the offer.
- Order now: hero > location selector > deal (HOST27, $295 first year / $395 after,
one-click product-4 buy with redirect=/coupon/HOST27?r=/shop/checkout) > trust bar >
Black Friday FAQ > features + trial card > FAQ > pricing (buy section) > Bootcamp > footer.
- Bootcamp box restored from webmaster/out/2026-11-02-bootcamp-box-removed-archive.md:
heading, USD 595.00, mechanism line, deadline, CTA, its meta/og contribution, its JSON-LD
Product entry and the FAQ answer are all back and consistent.
- Excluded (no blog, no OCA modules, no Project Plan); no dangling refs.
- New /static/black-friday.js = the homepage behaviours this page needs (root-relative
image paths), with the blog/OCA/project-plan blocks removed.
- Canonical URL unchanged; competitor QC 0 on the page.
Two user-requested changes on the Black Friday landing page.
1. The one-year framing is now unmissable where the price is stated:
"$295 for your first year — then $395 a year after that." (was: "$395.00 a year —
$295.00 for your first year"). The hero, meta description, og:description and the
JSON-LD WebPage description all carry the same first-year-then-$395 wording. The
offer itself is unchanged ($295 first year, $395 after, code HOST27, 16-30 Nov 2026).
2. The Bootcamp box is removed; the page is now a hosting-only offer surface. The
meta/og, the JSON-LD Product entry and WebPage.about Thing were rewritten, the FAQ
answer no longer claims the Bootcamp is part of the offer (HTML + JSON-LD, byte-
identical), and the offer grid re-flows to one centred card (no empty cell).
The removed box is archived, with line numbers, at
webmaster/out/2026-11-02-bootcamp-box-removed-archive.md.
The OG card was re-rendered hosting-only (sub line "your first year"); og:image:alt
updated to match.
Verified: verify-html.py exit 0; competitor QC on the page exit 0; impeccable detector
0 new findings (23 -> 21); JSON-LD valid, 1 Product, FAQ 6/6 byte-identical.
The Hosting form on /odoo-black-friday/ posts product 4 ("basket") + clear=1
to the shop's custom /shop/post_basket route. Adding a third hidden field,
redirect=/coupon/HOST27?r=/shop/checkout, chains that route's redirect into
the core website_sale_loyalty GET /coupon/<code> handler, so a single click
lands the visitor on /shop/address with the -$100 first-year discount already
itemised (Total $295) - no typing, works with JavaScript off, and it survives
the cross-host POST because the target is baked into the form.
Mechanism designed, probed and proven signed-out end to end by odoo-dev
(wiki/concepts/odoo-shop-promo-autoapply.md). No module, no price change.
Rollback: delete the one <input type="hidden" name="redirect"> line.
The code was behind a reveal (click 'Open the Black Friday price'). Both offer
cards are now always open: the price and HOST27 are readable without any
interaction. The scroll animation (opacity/transform only, base state fully
opaque, behind @supports) is presentation, never a gate.
The hosting CTA is now a native form that posts the On the Rise product straight
to the shop basket, so one click from the page lands in checkout with the plan in
the cart — replacing a three-navigation detour (offers hub -> homepage trial ->
pricing).
Bootcamp box content is untouched; only its reveal gate was removed.
The two Black Friday landing pages carried 0 Buy marks, so the page we are
about to advertise reported nothing. Buy is a CLICK PROXY by contract (it marks
a click on the offer CTA, not a completed purchase); the real purchase is fired
on the checkout host (20.odoo4projects.com) by odoo-dev. Both CTAs on
/odoo-black-friday/ and the BF-block link on /odoo-hosting-offers/ now carry
plausible-event-name=Buy, each labelled in-page as the proxy it is.
No Google Ads tag added: see handoff 2026-11-01-webmaster-to-olchi-ads-tag-and-buy-proxy.md
(privacy.html discloses no advertising cookies and the site has no consent gate).
- de|es/index.html: order widget #orderPlanTax '+ MwSt.'/'+ impuestos'
-> 'inkl. MwSt.'/'impuestos incluidos' (static span, no JS writes it)
- de|es/support.html: add the AI-agent urgent + 24h email support sentence
- de|es/odoo-erp-automation-bootcamp/index.html: add the seat terms
(transferable any time, cancellable up to two weeks before 21 Jan 2027)
- terms.html has no DE/ES page (legal pages held back) -> nothing to mirror
- terms.html: cancel any time + refund unused time (pro rata); delete the
no-refund sentence and the 'no refund promise beyond' clause
- terms.html: prices are tax included (was 'plus tax where it applies')
- index.html: order widget #orderPlanTax text '+ tax' -> 'tax included'
(id/class kept; static text, no JS reads or writes it)
- support.html: add the AI-agent-urgent + 24h email support promise,
bot still scoped to quick fixes
- odoo-erp-automation-bootcamp/index.html: seat transferable any time,
cancellable up to two weeks before the 21 Jan 2027 start
Mirror of 9715a3b into the two translated bootcamp pages. Each page's three
surfaces now read 595.00: the three buy buttons, the JSON-LD Offer `price`
field, and the `.bc-price` sentence. Matches the shop product, so the DE/ES
page can no longer lose the sale at the cart.
Price-only: the term (Q1 2027), the dates, the six-seat cap and the "one year
of hosted Odoo Community Edition" wording are untouched. Currency stays USD,
numeral format unchanged per page.
Verified (see translator/work/verify-price-change.py):
- HEAD vs worktree per page identical on tag sequence, attribute list,
<style> bytes, non-JSON-LD <script> bytes and JSON-LD shape -> text-only.
- 595.00 x5, 465.00 x0, no stray `465` token on either page.
- the three buy forms label-normalised byte-identical to EN.
No DE/ES workshops, black-friday or what-can-i-use-odoo-for translations
exist; llms.txt already carries 595.00.
The ODOO ERP Automation Bootcamp price is now USD 595.00 (shop product
already set). Every printed EN bootcamp price now reads 595.00 so the page
no longer loses the sale at the cart (page said 465.00, cart charged 595.00).
EN surfaces changed (price strings only — term, dates, six-seat cap and the
one-year hosted Odoo Community wording untouched):
- odoo-erp-automation-bootcamp/index.html: 3 buy buttons, JSON-LD Offer
price, .bc-price sentence
- odoo-black-friday/index.html: Bootcamp box JSON-LD Offer price, bf-teaser,
bf-price
- workshops/index.html: both meta descriptions, JSON-LD Offer price,
.bc-card-price, comparison-table cell
- what-can-i-use-odoo-for/index.html: the line naming the bootcamp
- llms.txt: workshops line
verify-html.py 0 problems, node --check script.js clean; competitor checker
unchanged (74 pre-existing Odoo Online hits, 0 added by this diff).
All three bootcamp pages (EN/DE/ES), every buy form: the basket line now
carries info="Q1 2027, English" alongside product_id 12 and qty 1, the
same field shape australia.html uses for location. Value verbatim on all
three pages (the programme is delivered in English in every language).
clear=1 / action / method / target / button labels unchanged.
Verified end to end against the live shop: POST /shop/post_basket returns
303 -> /shop/checkout and /shop/cart shows product 12 at qty 1 with the
info text rendered on the order line.
Deleted odooityourself.html ('delete the site we did for odooityourself').
The URL was already out of sitemap.xml and llms.txt, so no dead URL
stays advertised. The file is removed from the working tree; git history
retains it in full (recoverable from any earlier commit). The two SVGs
it used (images/andrew/ai-agent-on-server.svg and
ai-agent-on-server-mobile.svg) are kept — candidate art for
/odoo-community-ai/ — not part of this removal.
The code may not be named where a visitor or crawler can read it before
opening a reveal panel. Three off-panel leaks removed:
- /odoo-black-friday/ FAQ (visible answer + its FAQPage JSON-LD twin,
byte-identical): 'the code stops working' -> 'the offer stops working'.
- /odoo-hosting-offers/ og:image:alt: the trailing ', and the code
HOST27' dropped; the offer and the 16-30 November window stay.
- /odoo-hosting-offers/ date-scoped Black Friday block: the line no
longer names the code; it states the offer and links to
/odoo-black-friday/ (the only page with a reveal panel), marked with a
dated comment for removal after 30 November 2026.
HOST27 now survives only inside the .bf-panel on /odoo-black-friday/;
/odoo-hosting-offers/ carries zero.
New page "Run Odoo Community with your own AI agent", built from the copywriter's
build-ready copy (copywriter/out/2026-11-01-odoo-community-ai-copy.md) on the newest
page pattern (what-can-i-use-odoo-for/ , odoo-staging-environment/): page-scoped top
bar + language switcher, hero, alternating bands, shared footer, page-scoped CSS only.
Draft contract (all five): <meta name="robots" content="noindex, nofollow">; not added
to sitemap.xml; not added to llms.txt; no inbound link from any page (footer untouched);
self-canonical kept, no og:image. No file outside odoo-community-ai/ is touched.
Copy carried verbatim: title (53), meta (148), one H1, the seven H2s, the FAQ pairs
byte-identical between the visible FAQ and the FAQPage JSON-LD, the two MCP settings
shown apart (a table), the "for your developer" <details> after the offer, and the
fence sentence "does not move data". The copy's DRAFT(R1) marker and its [OPEN-SLOT:…]
marks ship as HTML comments, never as visible text.
The copy's 'Into this page' table, applied as single additive sentences:
- support.html: the 'error 500 after a git check in' FAQ answer links
'test it safely first'.
- blog/posts/speed-run-backup.html: end of the post links
'what a staging environment is, and why you probably do not need one'.
- odoo-hosting-2027.html: one contextual note by the backup/module rows
links 'do you need a staging environment?'.
- index.html: one FAQ card 'Do you need an Odoo staging environment?'.
No footer change. No new impeccable finding category.
New standalone page answering 'odoo staging environment': what staging is,
how odoo.sh's staging branches work (sourced to odoo.sh's own docs and
pricing page), why most small businesses do not need one, and the
back-up-install-test route we offer instead.
- Built on the newest page pattern (top bar, band rhythm, hero, FAQ,
language switcher, footer), page-scoped CSS only; no new framework.
- ICP review R1-R7 applied: backup plan-gate and slot limit stated,
the 'ask us' claim resolved against the Side Hustle limit, the restore
stated as a rollback of live work, the restore routed through support,
the meta de-claims 'one-click ... safely', 'module' defined at first
use, and a scope/price boundary added to the escalation.
- Title 51 chars, meta 148; one H1; six FAQ pairs byte-identical to the
FAQPage JSON-LD; @graph WebPage+Article+FAQPage+BreadcrumbList parses.
- CTA -> support.html#contact (existing route). og:image reuses the
established hero share image.
- sitemap.xml + one llms.txt line; blog/index.json untouched.
Remove the odooityourself.html <url> block from sitemap.xml (42 -> 41 <loc>)
and its line from llms.txt. The page stays published and crawlable: no
noindex, no canonical change, no internal links. It is a deliberate orphan
for a single affiliate, not a search target.
The Bootcamp note comment and its inline [OPEN-SLOT: …] marker named the code
HOST27 in the served HTML, so view-source leaked it before anyone opened the
reveal. Both mark the same still-open decision (BOOTCAMP-CONFIRM / bootcamp BF
price line), so the markers are kept and rewritten without the code string.
The reveal content that names the code on open is untouched.
- delete the whole 'What the code does' <section> (heading, both .bf-lead paragraphs, markup)
- remove HOST27 from meta/og/twitter descriptions, og:image:alt and the WebPage JSON-LD description
- rewrite the two FAQ strings (question + 'On the Rise cost' answer) and their JSON-LD twins so no code is named
- drop the now-dead .bf-lead CSS block and its dangling comment
- reveal mechanic untouched: the code stays inside each native <details> .bf-panel
/odoo-black-friday/ gains og:image + og:image:width/height/alt + twitter:image
pointing at the 1200x630 OG card; /odoo-hosting-offers/ reuses the same card.
Both pages declared no og:image before.
Same base filenames (bf2026-hosting-og, bf2026-hosting-linkedin,
bf2026-bootcamp-lastcall-linkedin), so the pipeline records' image URLs stay
valid. PNGs byte-identical to thumbnail-designer's rendered files:
og 161923B, linkedin 99673B, bootcamp 169572B. WebP siblings regenerated.
The page carried only the Hosting offer; it now carries both Black Friday
offers. Bootcamp box first, then Hosting, in the same <section class="bf-box">,
each in its own .bf-box-inner card, each with the same native
<details class="bf-reveal"> reveal (closed teaser, panel in the DOM in both
states, keyboard-operable, no display:none, JS-off safe, aria-expanded synced
for both). Copy is the copywriter's v3 verbatim: USD 465.00, ten live Thursday
sessions 18:00-19:00 Central Time from Thursday 21 January 2027, the Black
Friday second hosted year, six seats, deadline 30 November 2026. No discounted
Bootcamp figure is printed (HOST27 applicability unconfirmed): the value slot
stays a build placeholder in the one greppable bf-mechanism line. Hero value
statement, meta/og description, WebPage description, FAQ (6 pairs) and the
@graph (second Product+Offer, 465.00 USD) follow the same copy file.
Both pages shipped (355c5d0) with a three-item footer; the site footer is a
four-item commitment. The /odoo-erp-automation-bootcamp/ entry is restored on
both pages, matching index.html / odoo-hosting-2027.html.
/odoo-hosting-offers/: the code HOST27 is added inside the date-scoped Black
Friday block only (BF-BLOCK-START..END), so it disappears with the block after
30 Nov. Title, meta description and H1 are untouched — the intent split holds.
verify-html.py: PROBLEMS 0. node --check script.js: OK.
Three stills from the thumbnail-designer render pipeline, copied byte-identical
(md5 verified against the handoff) into images/black-friday/ so the two BF
pipeline records can reference stable public URLs. WebP siblings added to match
how the site serves images. Not referenced from any page yet — hosting only.
bf2026-hosting-og.png 1200x630
bf2026-hosting-linkedin.png 1200x627
bf2026-bootcamp-lastcall-linkedin.png 1200x627
Page A /odoo-black-friday/ owns "odoo black friday": hero, one On the Rise
box with a CSS-only reveal (native <details>) of the HOST27 offer, "what the
code does", FAQ. Page B /odoo-hosting-offers/ is the evergreen hub owning
"odoo hosting offers/deals/discount", with a date-scoped Black Friday block
(BF-BLOCK-START/END) linking to Page A. Both carry the copy's title/meta/H1,
@graph JSON-LD (Organization, WebPage, BreadcrumbList, Product+Offer, FAQPage).
sitemap.xml and llms.txt updated. Copy source: copywriter 2026-10-30.
Mirror the EN cap from 2940d33 in native register, cap only — the Black
Friday call-out (window, second hosted year, BF2026) is not added; it was
dropped from EN in fd3f1dc and stays out of every language.
- de|es/odoo-erp-automation-bootcamp/index.html: Class-size fact row
(Begrenzt auf 6 Teilnehmer / Limitado a 6 participantes), the .bc-price
cap line, and JSON-LD CourseInstance maximumAttendeeCapacity: 6.
- No workshops DE/ES copies exist, so no card / comparison row applies.
- why-odoo4projects.html left untracked.
The bootcamp has no Black Friday offer. Remove the .bc-cards call-out block
(16-30 Nov 2026 dates, second hosted year, BF2026 code, six-seats line) and
the OPEN-SLOT(olchi) landing-page comment. No BF string existed in llms.txt
or workshops/index.html. The 6-seat capacity stays: Class size fact row, the
bc-price cap line, CourseInstance maximumAttendeeCapacity: 6, the workshops
card cap + comparison row, and the llms.txt six-participants sentence.
- bootcamp page: Class size fact row (Limited to 6 participants), a cap line by
the price, JSON-LD CourseInstance maximumAttendeeCapacity: 6, and a compact
Black Friday call-out (16-30 Nov 2026, second hosted year + $100 off BF2026,
six seats) with the landing-page slot left open for olchi
- workshops page: the cap on the bootcamp card and a Class size comparison row
- llms.txt: durable 'limited to six participants per cohort' on the bootcamp line
(evergreen - no dated Black Friday offer in llms.txt)
Mirror the EN term change (01c5dd3) into the published German and Spanish
bootcamp pages: hero kicker, hero first-session stat, the four schedule
facts, the DST note and both JSON-LD dates. Text/date splice only — the
verifier reports the tag sequence, attribute list, <style>/<script> bytes
and JSON-LD shape identical before and after, english_leftovers: none.
Splice the bootcamp term on the EN page to the Q1 2027 January term:
hero kicker, hero stat, schedule <dd>s (Term/First/Last/Dates), DST note
(8 March -> 14 March), and JSON-LD CourseInstance startDate/endDate
(2026-01-22/2026-03-26 -> 2027-01-21/2027-03-25). llms.txt: 'Q1 2026,
starting 8 January 2026' -> 'Q1 2027, starting 21 January 2027' (retires
the 8-Jan vs 22-Jan conflict). sitemap.xml: bump lastmod on the three
bootcamp <loc> entries to 2026-10-30. No structural/CSS change.
The blog was an island: zero post→page links to hosting/upgrade/support/courses,
and only one page→post link in the whole site. Posts ranked for nothing while the
old blog host ranked for our long-tail queries.
Posts → pages (26 new in-body contextual links across 17 posts):
each post now carries one or two links to the page that answers its reader's next
question — odoo-hosting-2027, odoo-community-upgrade, support, the workshops hub,
the n8n masterclass, and the plan pricing on index.html#pricing.
Pages → posts (7 pages, one contextual link each):
index.html (OCA section), odoo-hosting-2027, odoo-community-upgrade, support,
workshops/, odoo-n8n-masterclass/, odoo-erp-automation-bootcamp/ now link into the
post that fits their own subject. The clearest miss is fixed — the
community-vs-enterprise-cost post and the odoo-community-upgrade page now link
to each other.
Anchors wrap existing sentences where the text already said the thing; four short
linking sentences were added (index OCA section, support first-steps FAQ, masterclass
lessons, bootcamp included section). No new components, no related-posts widget,
in-body anchors only. English pages only; DE/ES untouched (translator follow-up).
New post blog/posts/oca-paraguay-internationalization-hackathon.html (News,
2026-10-29), body verbatim from the supplied source (sha256 cca84c45...),
chrome copied from how-to-install-odoo-modules-using-git.html with the inline
style trimmed to the figureless variant. Adds the newest-first entry to
blog/index.json (16 -> 17), one 0.6 <url> to sitemap.xml (39 -> 40) and the
top line of the llms.txt blog list (16 -> 17).
Modelled on odoo-community-upgrade.html (top bar, band rhythm, FAQ details).
Copy published verbatim from the copywriter's 2026-10-28 file: 58-char title,
157-char meta, the six outbound internal links and the WebPage+Article+FAQPage+
BreadcrumbList @graph. Nine FAQ questions marked plausible-event-name=FAQ (no
event fires on page load). Sitemap + llms.txt list the new URL. Inbound links:
one from the homepage FAQ card, one from the support FAQ answer. Nothing else
moves; no footer change.
Plausible's click handler walks up from the clicked element and bails out the
moment it meets a <form>, so a plausible-event-name=Buy class on a
<button type="submit"> inside a plain form never fires Buy — the tracker sends
its automatic "Form: Submission" event instead and the goal records nothing.
Measured live on /odoo-erp-automation-bootcamp/ before this change:
{"n":"Form: Submission"}.
- odoo-erp-automation-bootcamp/index.html (3 CTAs): class moved from the
<button type="submit"> to the enclosing <form>.
- australia.html (2 CTAs): same move.
- de|es/odoo-erp-automation-bootcamp/index.html (3 forms each): the buy forms
carried no Buy marking at all, so the DE and ES bootcamp sales were invisible
to the goal. Class added to the <form>.
- odoo/upsell.html: the three .plan-btn buttons drive the same JS buy path and
were unmarked. A class on a plain <button> (not a form submit) does fire, so
it was added there and browser-verified. The disabled Trial button is left
unmarked.
Class attribute only: every button label, every form action and every basket
payload is byte-identical. No CSS or JS rule references the class.
Verified in a real browser (fetch shimmed, tracker's localhost/automation gates
bypassed): EN bootcamp 3/3, australia 2/2, DE 3/3, ES 3/3, upsell 1/1 clicks
each send exactly one {"n":"Buy"} to /api/event and never "Form: Submission".
Browser measurement on the live page: with plausible-event-name=Buy only on the
<button type="submit">, clicking it sends Plausible's automatic "Form: Submission"
event and no "Buy". With the class on the enclosing <form> the click sends exactly
one {"n":"Buy"}. The button keeps its class as specified; the form now carries it as
well, so the Buy goal fires once per click.
Same behaviour measured on the live bootcamp page (its three CTAs also emit only
"Form: Submission") - left untouched, reported as a site-wide follow-up.
Two directory URLs Google still has indexed were 404ing after the old site was
torn down. Both are rebuilt as trailing-slash directories (same model as
/odoo-erp-automation-bootcamp/, so the non-slash form 301s to the slash form).
odoo-n8n-masterclass/index.html
- copy from copywriter, structure and page-scoped CSS reused from the bootcamp page
- next session: four Thursdays of February 2027, 17:00 Central Time (hero eyebrow,
price and booking, JSON-LD startDate 2027-02-04)
- booking: native POST form to 20.odoo4projects.com/shop/post_basket, basket
product_id 44, zero JavaScript, three CTAs
- n8n environment corrected: we do not hand out n8n workspaces; the participant
uses n8n's own free plan. Body, hero fact, FAQ and JSON-LD say so plainly.
- JSON-LD Course + FAQPage; Plausible snippet; FAQ and Buy markings
workshops/index.html
- hub page, two cards (masterclass USD 120.00 / bootcamp USD 465.00), pull quote,
routing section, FAQ, comparison table
- the bootcamp card carries no term or date; the "ERP Automation Quick Wins" free
offer is gone entirely (no card, no 1-hour format, no /free-webinar link)
- JSON-LD ItemList; Plausible snippet; FAQ markings
SEO: canonical to the slash URL, OG tags, the bootcamp's font/style.css loading
pattern, sitemap.xml (+2 urls, lastmod 2026-10-28, bootcamp lastmod bumped for the
two links added there), llms.txt (+2 lines). Cross-linked both ways and both to the
bootcamp; the bootcamp page gains the inbound links.
English only. No DE/ES twins yet - translator follow-up.
Checks: verify-html.py 0 problems, check-links.py clean, check-links-2026-10-28.py
56 PASS / 0 FAIL, verify-plausible-events 71 PASS / 0 FAIL, JSON-LD parses.
The snippet was on 10 of 40 pages, so the Trial / FAQ / Buy / Custom Quote /
search posts / Project Plan goals were inert everywhere else. It now sits
verbatim, in the same <head> position as index.html, on all 39 visitor-facing
pages. email/trial-confirmation.html is deliberately left out (email template).
FAQ: the five <details class="bc-faq"> <summary> elements on the bootcamp page
and its DE/ES twins now carry plausible-event-name=FAQ, and the 15
.faq-question buttons on de/es/support.html get the marking they were believed
to already have. No goal renamed: one FAQ goal across all languages.
No markup, copy or behaviour change, and no third-party request beyond the
snippet itself. verify-html.py PROBLEMS 0; check-links.py OK; event checker
59 PASS / 0 FAIL; impeccable detector +0/-0 findings on the changed files.
Mark the six conversion goals on the self-hosted Plausible instance so the
events match the goals to be registered in Plausible. Event names verbatim:
Trial, FAQ, Buy, Custom Quote, search posts, Project Plan.
CSS-class markings (work without JS logic, on the clickable element):
- FAQ -> index/de/es index .faq-card; odoo-hosting-2027 + odoo-community-upgrade
(de/es twins) and australia <summary>; support.html .faq-question
- Buy -> index/de/es index .btn-buy anchors; australia + bootcamp native
post_basket submit buttons (forms stay native POST)
JS call markings in script.js (loaded by index, de/index, es/index):
- Trial on the successful trial-form POST (.then branch)
- Custom Quote on the successful custom-quote POST (.then branch)
- Project Plan on the successful business-wizard submit
- search posts on the FIRST input of #blogSearch only (guard flag, one per
pageview, not on every keystroke, not on #ocaSearch)
Nothing fires on page load; the 'Start the free trial' anchors that only
scroll to #trialCard are deliberately not tagged. One goal per topic across
all languages, no language suffix. No visual, behavioural or request change.
New post at blog/posts/how-to-install-odoo-modules-using-git.html
(2026-10-28, area "deep dive"), body pasted verbatim from the boss's
supplied block (7 h2, two figures in place). Chrome copied from the
newest post (odoo-community-is-going-online-on-odoo4projects.html:
main.blog-post / span.badge / div.post-meta / img.post-image /
div.post-body / a.back-link), style block trimmed to the rules this
page uses plus the ol/figure/figcaption rules its body needs.
Additive wiring: og:image + twitter:card, post-image after post-meta
(alt = h1), BlogPosting JSON-LD with dateModified.
blog/index.json gets the entry first (newest-first, key order
id,date,area,headline,teaser,link,image) by text splice - 15 -> 16
posts; sitemap.xml one new 0.6 blog url - 35 -> 36 <loc>; llms.txt one
new line at the top of the post list.
The visible FAQ answer used arrows while the structured-data copy spelled the
chain out with 'to', so acceptedAnswer.text was not verbatim-present on the
page. FAQ parity check now reports 8/8 on EN/DE/ES.
build-i18n.py's rebase_paths() only re-pointed a root-absolute page link when it
contained ".html", so extensionless directory links stayed on the EN route even
when the language had its own copy (`/odoo-erp-automation-bootcamp/` on all four
DE and ES pages, and `/` on the DE/ES bootcamp pages). Same guard as the .html
branch: localise only when <lang>/<path>/index.html exists.
Re-applied the rebasing to the 8 already-built DE/ES pages (work/i18n/
relocalise-links.py) - a full re-transform is not safe for them because the
switcher-slot/hero rework means repo pages are not raw transform output. 18 refs
in 8 files. The DE/ES homepages were the stale case: their body links were built
before support.html/odoo-hosting-2027.html existed in that language and were
never revisited.
check-links.py now flags an EN-route page link whose language has its own copy:
0 such links on all 10 DE/ES pages after the rebuild. test-build.py 107 passed;
verify-html.py 0 problems.
Copy-only mirrors of odoo-community-upgrade.html built by translator against the
post-R1-R5 English (EN af27098a, commit 2fe0c31), rendered through work/i18n/build-i18n.py:
<html lang>, canonical, 4-way hreflang cluster (en-US/de/es/x-default), og:url,
/static/lang.js and 15 rebased refs. No EN page change.
- sitemap.xml: 33 -> 35 <loc>, both new pages at priority 0.9
- llms.txt ## Languages: DE + ES lists now name /<lang>/odoo-community-upgrade.html
- checks: check-links.py 0 broken, verify-html.py 0 problems (tag balance, JSON-LD,
sitemap XML), JSON-LD FAQPage answers verbatim in the visible FAQ 8/8 per page
Retrospective ICP 1 review (kanban t_03b62410) found the EN page made a
non-technical owner-operator hesitate: the offer sat behind four blocks of
shell commands, the hero ended on "a developer", one hero line carried an
untraced claim, there was no route to a number, the FAQ printed the
OpenUpgrade data-loss warning against our own fresh-database service, and
the "restaurant meal" simile was unitless.
R1 move "The ODOO4projects way" above the process and the commands; swap the
two band classes so the page still alternates; keep the offer's code cards
legible on the dark band; reword the §5 intro and §6 opening sentence.
R2 hero subhead reworded, dropping the untraced "costs you your history".
R3 "See the plans" route added inside §6, with the honest "not priced on that
page" clause. No price, no plan hero, no Offer node.
R4 FAQ "What happens to my history…" answer rewritten (JSON-LD acceptedAnswer
kept verbatim-equal to the visible text).
R5 "restaurant meal" clause deleted.
Band-aware text colours added for the elements the class swap re-parented
(process list on light, technical section and its notes on dark, the §6 link).
No other content changed: H1, CTAs, code blocks (all 4 byte-identical),
Sweat Equity, the 8 FAQs and the JSON-LD shape are untouched.
Four follow-up defects found beyond the 2026-10-27 SEO audit (no copy changes):
1. legal.html canonical was https://ODOO4projects.com/legal — uppercase host AND a
path that 404s. Now https://odoo4projects.com/legal.html. The file's og:url keeps
its path /legal but its host is lowercased (path fix awaiting a decision).
2. The no-"4" domain typo in reader-visible <pre><code> body text of
blog/posts/{guide-migrate-enterprise,speed-run-deploy,my-first-post}.html:
odooprojects.com -> odoo4projects.com. Domain string only, no other wording.
3. Lowercase host in every machine-read value: the robots.txt Sitemap line, the
homepage + de/ + es/ Organization JSON-LD url / contactPoint.url / offers[].url,
legal.html og:url, and the four absolute self-URL hrefs in blog posts,
odoo/support.html and email/trial-confirmation.html. Display text untouched
(support@ODOO4projects.com stays as written).
4. One BlogPosting per blog post (15), every value taken from the page itself:
headline = its <h1>, datePublished = its visible post-meta date, url = its own
canonical, author/publisher = the Organization the site already publishes,
image = its og:image where one exists (omitted on the 10 posts that have none).
Empire ">950.00", FAQPage on support.html, the blog-chrome change and the bootcamp
date conflict are untouched — decisions pending with the user.
From seo/out/site-seo-ai-search-audit-2026-10-27.md (§6 items 1, 2, 3a, 5).
- canonical: 10 blog posts declared https://odooprojects.com/... — a live parked
third-party domain — as their canonical. Now self-referencing on
https://odoo4projects.com/blog/posts/<slug>.html (1 line each, nothing else).
- sitemap.xml: 27 -> 33 URLs. Added the 6 live pages that were missing
(support.html, legal.html, community-vs-enterprise-cost, 6-tips,
odoocommunity-agents, odoo-20-is-live); corrected 13 lastmod values to the
real last content commit (homepage was 4 months stale); /de/ and /es/
priority 1.0 -> 0.7 so a translation does not outrank its original.
- blog/index.json: 7 -> 15 posts. The 8 orphan posts are now listed, so every
post is reachable from the homepage grid.
- llms.txt: removed the link to /blog/ (that URL returns 403) and added a
"## Blog posts" section listing all 15 posts with date and summary.
Not changed on purpose: FAQPage on support.html, the homepage "price": ">950.00",
the bootcamp start-date conflict, and the blog post chrome (15 files).