From f40c65235243c8974b377717649921e805b48811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?je=CC=81ro=CC=82me=20boileux?= Date: Mon, 19 Oct 2026 15:27:18 +0200 Subject: [PATCH 1/3] docs(adr): record enjeu pages as a content type Six enjeu pages become an `enjeu` content type with structured frontmatter, experts declared on team members, bow tie perimeter in content, and a website redeploy on merge. --- docs/adr/0002-enjeu-pages-as-content.md | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 docs/adr/0002-enjeu-pages-as-content.md diff --git a/docs/adr/0002-enjeu-pages-as-content.md b/docs/adr/0002-enjeu-pages-as-content.md new file mode 100644 index 0000000..244584c --- /dev/null +++ b/docs/adr/0002-enjeu-pages-as-content.md @@ -0,0 +1,36 @@ +# Enjeu pages as a content type, owned by their lead expert + +The website redesign adds six enjeu pages (Revenue Performance, Sales Experience, Sales Compensation, Customer Experience, Revenue Systems, Revenue AI). We store them in this repo as a new content type, `enjeu`, in `enjeux//.md`. We rejected the alternative of six fixed pages in the website's `locales/*.json`, which is where single marketing pages (home, method, offer) live today. The six pages share one eight-section template in the mockup, and each lead expert edits their own page after launch. With fixed pages, every wording change would be a code PR on the website. + +## Decisions + +**Structured frontmatter, no body.** The page is lists and short blocks, not prose, so every section is a frontmatter field: hero, audience, workstreams, AI bridge line, client case (a Story slug plus a one-line summary), diagnostic question. Two pages carry a section of their own: the stack diagram on Revenue Systems, and the thesis on Revenue AI. These are named optional blocks (`extraSections`), implemented as components in the website. The file only places them. + +**The "how we work" section stays in website code.** Its text is identical on five pages. An optional `method` field lets Revenue AI replace it with its Sprint steps. + +**Bow tie: milestones in code, perimeter in content.** The seven milestones, their KPIs and the enjeu colours are defined once in the website. The enjeu file holds only `activeStages`, validated against the milestone identifiers. The perimeter is a commercial claim the expert owns; KPI definitions must not exist in six copies. The method page reads all enjeu files to drive its hover filter. + +**Experts are declared on the Team member, not on the enjeu.** A Team member gains `enjeu` (one per person), `contribution`, `tools` (Tool slugs), `previousCompany` and `quote`, each translatable field in `fr`/`en`. An enjeu page lists the Team members whose `enjeu` matches. Membership lives in one place, and each expert owns their own file. We rejected an `experts` list in the enjeu file, which would let two files disagree. + +**Ownership and review.** Each enjeu file has one lead expert as code owner. The content lead (Edmond de Maistre) is a required reviewer for message and tone. The website maintainer reviews schema and rendering. The per-PR preview deploy already exists and is the expert's check before merge. + +**Publication.** A merge to `main` triggers a website redeploy through a Vercel deploy hook, so a correction is live within minutes on every page. Without it, the `static` cache strategy would delay enjeu, method and team pages by up to 24 hours. We rejected a one-hour cache strategy for enjeu pages only: the method page would contradict the enjeu page until its own cache expired. + +## Relation to ADR 0001 + +Enjeu pages are created once and then edited, so experts work in this repo through Claude Code. Notion import remains available for the first draft but is not the expected route for edits. + +## Consequences + +- New Zod schema for `enjeu` and an extended Team member schema in `scripts/schemas/`, with length limits per field. Flip cards have a fixed height, so an overlong text breaks the layout. +- `new-content` gains the `enjeu` type and an edit mode for one section; `translate-content` supports `enjeu`. +- `CONTEXT.md` gains the terms Enjeu and Milestone, and their relationships to Team member and Story. +- A `CODEOWNERS` file maps each enjeu file to its lead expert. Six experts need a GitHub account in the organisation and a run of `setup`. +- A tone and length guide for enjeu pages, written by Ocobo. +- A workflow on push to `main` calls the deploy hook. The hook URL is stored as a repository secret. + +## Revisit if + +- An expert must appear on several enjeux with a different contribution on each: move experts back into the enjeu file as `{slug, contribution}`. +- `extraSections` grows past three blocks: the pages have diverged and fixed pages become cheaper. +- The Vercel plan limits deployments, or merges exceed about twenty a day. From 7d8439772216dfd9c77c11c44f778d642f59a44d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?je=CC=81ro=CC=82me=20boileux?= Date: Tue, 20 Oct 2026 17:42:59 +0200 Subject: [PATCH 2/3] docs(adr): complete enjeu consequences and add stage and milestone terms --- CONTEXT.md | 26 +++++++++++++++++++----- docs/adr/0002-enjeu-pages-as-content.md | 27 ++++++++++++++++++------- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/CONTEXT.md b/CONTEXT.md index f0fdccc..b2dd358 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -1,12 +1,12 @@ # Ocobo Content Repository -This repo holds the markdown content consumed by the Ocobo website. It replaces a traditional CMS — Ocobo teams edit content here via Claude Code and ship it through GitHub PRs. +This repo holds the markdown content consumed by the Ocobo website. It replaces a traditional CMS: Ocobo teams edit content here via Claude Code and ship it through GitHub PRs. ## Language **Content**: -The umbrella term for everything published by the website that lives in this repo: blog posts, stories, team members, tools, jobs. -_Avoid_: posts (overloaded — see "Flagged ambiguities"). +The umbrella term for everything published by the website that lives in this repo: blog posts, stories, team members, tools, jobs, enjeux. +_Avoid_: posts (overloaded, see "Flagged ambiguities"). **Blog post**: A long-form article in `blog//`, typically authored by a team member, often tied to the Revenue Echoes podcast. @@ -26,7 +26,19 @@ _Avoid_: software, vendor, product. **Job**: An open position in `jobs//`. References a `hiringContact` (team member slug). -_Avoid_: role (overloaded — see "Flagged ambiguities"), offer, posting. +_Avoid_: role (overloaded, see "Flagged ambiguities"), offer, posting. + +**Enjeu**: +One of the six revenue problems Ocobo sells a mission on: Revenue Performance, Sales Experience, Sales Compensation, Customer Experience, Revenue Systems, Revenue AI. Each has one page, one lead expert and one file in `enjeux//`. +_Avoid_: challenge, issue, stake, problem, practice. + +**Stage**: +One of the seven steps of the customer cycle drawn on the bow tie, from first signal to expansion. Each has one KPI. +_Avoid_: milestone, step, phase. + +**Milestone**: +The state a prospect or customer reaches at the end of a Stage: MQL, SQL, SQO, new customer, active customer, active, upsell or cross-sell. +_Avoid_: jalon (French label only), stage. **Slug**: The kebab-case filename (without `.md`) that uniquely identifies a piece of content within its type. Cross-references between content types use slugs. @@ -41,8 +53,12 @@ The YAML block at the top of each markdown file, between `---` fences. Defines s - A **Job** has exactly one `hiringContact`, which is a **Team member** slug. - A **Team member** can be referenced by many **Blog posts** and **Jobs**. - A **Tool** can be referenced by many **Stories**. +- An **Enjeu** covers one or more **Stages**; several Enjeux can cover the same Stage. +- A **Stage** ends on exactly one **Milestone**. +- A **Team member** is an expert on at most one **Enjeu**; an Enjeu has one lead expert among them. +- An **Enjeu** cites one **Story** as its client case. ## Flagged ambiguities - **"post"** was used both as the repo name (`ocobo-posts` = all content) and as a synonym for "blog post" (e.g. `assets/posts/`). Resolved: use **Content** as the umbrella term and **Blog post** for articles. The repo name and `assets/posts/` directory are kept as-is for backward compatibility. -- **"role"** appears as both a Team member field (job title, e.g. "Partner") and a Story field (the speaker's role at the client). Resolved: both usages are job titles, just for different people — no rename needed, context disambiguates. +- **"role"** appears as both a Team member field (job title, e.g. "Partner") and a Story field (the speaker's role at the client). Resolved: both usages are job titles, just for different people. No rename needed, context disambiguates. diff --git a/docs/adr/0002-enjeu-pages-as-content.md b/docs/adr/0002-enjeu-pages-as-content.md index 244584c..4c61074 100644 --- a/docs/adr/0002-enjeu-pages-as-content.md +++ b/docs/adr/0002-enjeu-pages-as-content.md @@ -4,13 +4,23 @@ The website redesign adds six enjeu pages (Revenue Performance, Sales Experience ## Decisions -**Structured frontmatter, no body.** The page is lists and short blocks, not prose, so every section is a frontmatter field: hero, audience, workstreams, AI bridge line, client case (a Story slug plus a one-line summary), diagnostic question. Two pages carry a section of their own: the stack diagram on Revenue Systems, and the thesis on Revenue AI. These are named optional blocks (`extraSections`), implemented as components in the website. The file only places them. +**Structured frontmatter, no body.** The page is lists and short blocks, not prose, so every section is a frontmatter field: hero, audience, workstreams, AI bridge line, client case (a Story slug plus a one-line summary), diagnostic question. Two pages carry sections of their own: the stack diagram on Revenue Systems, and the thesis and "Une journée de commercial" on Revenue AI. These are website components, inserted when the enjeu key is `sy` or `ai`; the enjeu file does not mention them. The stack diagram shows Ocobo's technical doctrine, which the technology page and the home repeat, so its data lives in the website too. We rejected a list of named blocks placed by the file (`extraSections`): after the partner block became a field, it would have served a single block. -**The "how we work" section stays in website code.** Its text is identical on five pages. An optional `method` field lets Revenue AI replace it with its Sprint steps. +**Revenue AI is a sixth enjeu file.** Its page departs from the template on 7 of 13 sections, yet the home, the method page and "Les autres enjeux" read the same fields from all six enjeux. We rejected a page coded by hand, which would duplicate those fields outside the content. Its Sprint fills `method` with an overridden title. Its "Notre point de vue" fills `aiBridge` without a demo. The AI thesis and "Une journée de commercial" are website components, inserted when the enjeu key is `ai`. They are editorial pieces with heavy interaction, and no other enjeu will reuse them. -**Bow tie: milestones in code, perimeter in content.** The seven milestones, their KPIs and the enjeu colours are defined once in the website. The enjeu file holds only `activeStages`, validated against the milestone identifiers. The perimeter is a commercial claim the expert owns; KPI definitions must not exist in six copies. The method page reads all enjeu files to drive its hover filter. +**Agent window demos are content.** The AI bridge is a full section: a title, three bullets and an optional agent window demo. The five demos share one frame (channel, agent, time, question, footer) and a body built from five element types: a KPI trio, a highlighted figure, a progress bar, a status row (ok or alert), buttons. `aiBridge.demo` holds the frame and an ordered list of typed elements; one website component renders them. The scenario is the expert's sales argument, like the workstreams, so the expert edits it without a website PR. We rejected one component per enjeu, which would copy the same frame five times, and data in the website's `locales/`, which would block the expert. The window has a fixed height, so the schema caps the number of elements and the length of each text. -**Experts are declared on the Team member, not on the enjeu.** A Team member gains `enjeu` (one per person), `contribution`, `tools` (Tool slugs), `previousCompany` and `quote`, each translatable field in `fr`/`en`. An enjeu page lists the Team members whose `enjeu` matches. Membership lives in one place, and each expert owns their own file. We rejected an `experts` list in the enjeu file, which would let two files disagree. +**A partner block is an optional field open to every enjeu.** Sales Compensation shows Qobra under its workstreams: a logo, a positioning sentence, two before and after metrics, and their source. `partner` holds a Tool slug, the sentence, exactly two metrics and a required source, since the figures belong to a third party. The logo comes from the Tool file. We rejected a block named after Qobra: nothing in its structure is specific to Qobra, and a second partnership would need code. + +**The "how we work" section is content on every page.** Only its heading and the three step titles are shared, and they stay in website code. The subtitle, step texts, tools and note vary per enjeu and live in `method`. + +**Bow tie: stages in code, perimeter in content.** The seven stages, their milestones, their KPIs and the enjeu colours are defined once in the website. The enjeu file holds only `activeStages`, validated against the stage identifiers. The perimeter is a commercial claim the expert owns; KPI definitions must not exist in six copies. The method page reads all enjeu files to drive its hover filter. + +**Experts are declared on the Team member, not on the enjeu.** A Team member gains `enjeu` (one per person), `contribution`, `tools` (Tool slugs) and `previousCompanies`, each translatable field in `fr`/`en`. No mockup page shows an expert quote, so the planned `quote` field is dropped. An enjeu page lists the Team members whose `enjeu` matches. Membership lives in one place, and each expert owns their own file. We rejected an `experts` list in the enjeu file, which would let two files disagree. + +**Contribution and bio are two texts.** The existing `bio` stays the text of the about-us page, unchanged. `contribution` is a new, shorter text written to sell the enjeu; the enjeu and team pages show it with `previousCompanies`. Production bios already name past employers, so the about-us card does not show `previousCompanies`. We rejected moving employers out of the bios, which would rewrite production content, and a single text, which would cut the founders' longer presentation. + +**One ordered tool list per expert.** `tools` is ordered by the expert. The enjeu card shows the first four; the team page shows them all. In the mockup the enjeu cards show a shorter selection of the team page list, so one list covers both. We rejected a second list for the enjeu card, which could contradict the first, and a list per expert in the enjeu file, which would undo the single declaration above. **Ownership and review.** Each enjeu file has one lead expert as code owner. The content lead (Edmond de Maistre) is a required reviewer for message and tone. The website maintainer reviews schema and rendering. The per-PR preview deploy already exists and is the expert's check before merge. @@ -22,9 +32,12 @@ Enjeu pages are created once and then edited, so experts work in this repo throu ## Consequences -- New Zod schema for `enjeu` and an extended Team member schema in `scripts/schemas/`, with length limits per field. Flip cards have a fixed height, so an overlong text breaks the layout. +- New Zod schema for `enjeu` and an extended Team member schema in `scripts/schemas/`, with length limits per field. Flip cards and the agent window have a fixed height, so an overlong text breaks the layout. +- The `enjeu` schema caps the demo elements per window, requires exactly two partner metrics and a partner source, and has no `extraSections` field. +- New cross-references for validation: `partner.tool` and Team member `tools` point to Tool slugs, the client case to a Story slug, Team member `enjeu` to an enjeu key, and `activeStages` to the stage identifiers. +- Team member gains `enjeu`, `contribution`, `tools` and `previousCompanies`; there is no `quote` field. Existing files stay valid: the new fields are optional for people who are not experts. - `new-content` gains the `enjeu` type and an edit mode for one section; `translate-content` supports `enjeu`. -- `CONTEXT.md` gains the terms Enjeu and Milestone, and their relationships to Team member and Story. +- `CONTEXT.md` gains the terms Enjeu, Stage and Milestone, and their relationships to Team member and Story. - A `CODEOWNERS` file maps each enjeu file to its lead expert. Six experts need a GitHub account in the organisation and a run of `setup`. - A tone and length guide for enjeu pages, written by Ocobo. - A workflow on push to `main` calls the deploy hook. The hook URL is stored as a repository secret. @@ -32,5 +45,5 @@ Enjeu pages are created once and then edited, so experts work in this repo throu ## Revisit if - An expert must appear on several enjeux with a different contribution on each: move experts back into the enjeu file as `{slug, contribution}`. -- `extraSections` grows past three blocks: the pages have diverged and fixed pages become cheaper. +- A third enjeu needs a section of its own, or a section coded for one key needs editing by its expert: the pages have diverged and fixed pages become cheaper. - The Vercel plan limits deployments, or merges exceed about twenty a day. From 92d64edd2bd7fb9c6acf7648ffb14ebbcf4acee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?je=CC=81ro=CC=82me=20boileux?= Date: Tue, 20 Oct 2026 17:42:59 +0200 Subject: [PATCH 3/3] docs: drop GitButler from the repo rename plan --- docs/rename-repo-plan.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rename-repo-plan.md b/docs/rename-repo-plan.md index 138e2b0..a70c2bb 100644 --- a/docs/rename-repo-plan.md +++ b/docs/rename-repo-plan.md @@ -57,7 +57,7 @@ PR sur le website : | Vercel sert un 404 entre le rename et le redeploy | Prod down quelques minutes | Faire rename + update env var + redeploy dans la même fenêtre. GitHub renvoie 301 que `fetch` suit, mais ne pas s'y fier longtemps. | | Quelqu'un recrée un repo `ocobo-revops/posts` plus tard | Casse le redirect GitHub | Faible (org privée). Optionnel : placeholder (cf. section 5). | | Vercel deploy-hook / GitHub App liés au repo | Aucun en pratique — GitHub conserve l'ID interne | Vérifier que l'intégration Vercel "Git repo" affiche bien le nouveau nom après rename (#13). | -| PRs / branches GitButler ouvertes au moment du rename | Auto-redirigées | Vérifier les lanes actives, refaire `but pull` après rename. | +| PRs et branches ouvertes au moment du rename | Auto-redirigées | Vérifier les branches actives, refaire `git fetch` après rename. | ## Chemin critique