TFD-027: Client Demande Hierarchy — Flat with Prefix, Jira-Synced
TFD-027: Client Demande Hierarchy — Flat with Prefix, Jira-Synced
Status: Accepted Date: 2026-05-28 Decision makers: CEO (Bruno) Consulted: Elena (Enterprise Architect), Marcel (Methodology), Nora (Nomenclature), Pablo (Production Line), Diego (Deployment), Camille (Intake) Refines: TFD-0019, TFD-0025, TFD-0026
Context
TFD-025 settled the top of the client tree (OneDrive/agent-ea/clients/<slug>/). What lives under <slug>/ was still ad hoc. Real engagements showed three structural pressures:
- Multi-discipline : the same client now sends both EA demandes (DAE) and project-management demandes (PM). One talent each. They share the client and often share the initiative.
- Multi-request granularity : a single demande (DAE-0007 Transgesco) accreted intrant sub-folders (
05-Evaluation wifi/) that have their own scope, vendor proposals, and would-be deliverables — i.e. sub-demandes disguised as intrants. - Macroscope/LeanIX context layer : there is a real business notion of Initiative / Projet / Transformation above the demande, plus a parallel Opérationnel / Capacité track for run-the-business work. Both need to be representable without bloating the tree.
A/B comparison between a nested shape (initiatives/<id>/projets/<id>/demandes/<id>/) and a flat shape (<DEMANDE-or-INIT>/) ran 2026-05-28 (experiments/demande-hierarchy/auto-research/research-log.md). Score: flat 12/12, nested 5/12.
Decision
Adopt a flat client tree : everything that has a Jira-issue counterpart sits directly under clients/<slug>/. Parent relations live in YAML frontmatter, never in folder paths.
clients/<client-slug>/
├── _client.md
├── INIT-0001/ ← Initiative (own brief + transverse deliverables)
│ ├── _context.md
│ └── out/
├── DAE-0004/ ← Demande EA (épopée)
│ ├── _demande.md
│ ├── intrants/
│ ├── decisions/
│ ├── notes/
│ └── out/
├── PM-0012/ ← Demande PM
├── DAE-0011.md ← Light-mode demande (one file)
├── OPS-0023/ ← Operational demande
└── _catalog/ ← D1 catalogue EA (per TFD-019)
Naming
All folders KEEP a slug : {ID}-{slug}/ (full mode), {ID}-{slug}.md (light mode).
Slug-everywhere rule confirmed by TFD-0028 (same day, after CEO observation that slugless folders were unrecognizable). TFD-0026 §"no slug" rule is superseded.
Slug rules:
- Lowercase, alphanumerics + hyphens (
[a-z0-9][a-z0-9-]*). - Concise but descriptive (e.g.,
DAE-0007-evaluation-systemes,DAE-0011-evaluation-wifi,INIT-0002-electrification). - The full title still lives in frontmatter — slug is the recognition handle, not the canonical title.
| Type | Prefix | Sequence | Use |
|---|---|---|---|
| Initiative | INIT-NNNN |
global across clients | transformation, project, multi-demande business context |
| Demande EA | DAE-NNNN |
global across clients (continues existing DAE sequence) | architecture work-package (full mode, discipline known) |
| Demande PM | PM-NNNN |
global across clients (new sequence, starts at 0001) | project-management work-package |
| Demande Ops | OPS-NNNN |
global across clients (new sequence) | run-the-business work |
| Demande OCM | OCM-NNNN |
reserved | organisational change management |
| Light-mode request | REQ-NNNN |
global across clients (client-side sequence, distinct from factory REQ) | quick intake before discipline is known; discipline: unclassified until classified |
Rationale for global (not per-client) : matches TFD-026 spirit (one ID = one artifact, no collision), keeps Jira Source ID field unique factory-wide, allows quick lookups without client qualifier.
REQ namespace disambiguation : factory-side REQ-NNNN lives in departments/<dept>/requests/REQ-NNNN/; client-side REQ-NNNN lives in clients/<slug>/REQ-NNNN.md (light mode) or clients/<slug>/REQ-NNNN/ (full mode). The two are separate sequences disambiguated by location. Cross-references must qualify with the path (e.g., "client REQ-0001 (Transgesco)" vs "REQ-0042 (factory)").
Promotion light → full keeps the ID : REQ-0001.md promoted to a folder stays REQ-0001/. Discipline can update in frontmatter (unclassified → ea/pm/ops) once known. Jira keys remain stable.
A### Macroscope codes (A100, A230, A290…) are not folder names — they are deliverable codes inside a demande's out/ and checklist entries inside _demande.md.
Frontmatter schema — _demande.md
---
id: DAE-0004
type: demande
discipline: ea # ea | pm | ops | ocm | …
title: SCADA Ignition — migration historians
status: active # shared 5-value enum (see below)
opened: 2026-05-12
closed: null
# Parent (exactly one of two)
parent_initiative: INIT-0001 # OR
parent_capacite: mobilite-billettique
# Optional parent demande when this is a sub-demande
parent_demande: null # e.g. DAE-0007 if split out
# Sync
jira_key: JCT-247
# Factory side
assignee_talent: agent-ea
assignee_persona: elena
# Sub-tasks = Macroscope deliverables
sous_taches:
- { code: A100, status: done, file: out/A100-portee.html }
- { code: A230, status: active, file: out/A230-orientations.html }
- { code: A290, status: backlog, file: null }
---
Required : id, type, discipline, title, status, opened, assignee_talent, (parent_initiative XOR parent_capacite).
Frontmatter schema — _context.md (Initiative)
---
id: INIT-0001
type: initiative
title: Renouvellement SCADA — bascule vers Ignition
status: active
opened: 2026-04-15
target_end: 2026-12-31
jira_key: JCT-200
# Optional
# sponsor: J. Tremblay (VP Ops STM)
# budget_cad: 1_250_000
---
Renouvellement SCADA — bascule des historians vers Ignition. Sponsor STM Ops, livraison fin 2026.
Body = one paragraph. The list of rattached demandes is not stored here — scan parent_initiative == this.id when needed.
Status enum (shared, all levels)
backlog | active | blocked | done | cancelled
Initiative, Demande, and Macroscope sub-task all use this five-value vocabulary. One enum, one mental model.
Sub-demandes — sister folder, not nested
A sub-investigation gets its own sister folder with parent_demande: in frontmatter. No suffix scheme (DAE-0007-A rejected — see auto-research log).
DAE-0007/ ← demande mère
DAE-0014/ ← sister, parent_demande: DAE-0007
DAE-0015/ ← sister, parent_demande: DAE-0007
Promotion criterion : independent status, independent assignee, or 2+ deliverables → promote from intrant bucket to sister demande.
Intrants — flat, grouped by source
intrants/ is plat. Sub-folders group by source (a vendor, a workshop, an RFI), never by phase or numbering. Phase/progress tracking lives in _demande.md.
DAE-0004/intrants/
├── ao-9130/
├── ateliers/
├── fournisseur-videotron/
└── emails/
Light mode
One demande can ship as a single .md file at the client root (clients/stm/DAE-0011.md) when it fits:
| Cas | Léger | Full |
|---|---|---|
| 1 livrable, < 4h | ✓ | |
| Q&R, RFI ponctuel | ✓ | |
| Sans intrants à classer | ✓ | |
| 2+ livrables, A### structurés | ✓ | |
| Intrants à classer, KDD attendus | ✓ |
Promotion light → full : scripts/promote-demande.mjs <path> creates the folder, moves the .md to _demande.md, scaffolds intrants/ decisions/ notes/ out/. Frontmatter unchanged, jira_key preserved.
Jira sync (asymétrique)
Repo = source of truth for content. Jira = visibility surface.
JSM portal (intake) ──pull──▶ clients/<slug>/<TYPE-NNNN>/
│
└──push──▶ Jira issue
(status, fields, sub-tasks)
- Pull at intake only : new JSM ticket → Camille runs
scripts/intake-from-jira.mjs <JIRA-KEY>which scaffolds the local folder withjira_key:filled. - Push from repo :
scripts/jira-sync.mjs <client-slug>scans_demande.mdfiles and pushesstatus,title,parent_initiative, sub-tasks. Manual trigger (or pre-commit hook if desired). No webhook, no GitHub Action at this stage. - No bidirectional content sync. Status lives in repo ; if someone edits it in Jira, the next push restores. Description in Jira is short + URL to the rendered deliverable.
Status mapping (5 → 5) :
| Repo | Jira |
|---|---|
backlog |
To Do |
active |
In Progress |
blocked |
Blocked |
done |
Done |
cancelled |
Cancelled |
Field mapping :
| Repo | Jira |
|---|---|
id |
Custom field Source ID |
title |
Summary |
parent_initiative |
Parent link (Initiative issue type) |
assignee_persona |
Assignee (mapped) |
sous_taches[] |
Jira sub-tasks (1 per A###) |
opened / closed |
Created / Resolution date |
jira_key |
Issue key (← Jira is master here, written back to repo) |
Acceptance criteria
-
naming-conventions.mdupdated with the prefix table (INIT/DAE/PM/OPS) and the per-client global sequence rule. -
_demande.mdand_context.mdJSON-schema-equivalent validators committed atexperiments/demande-hierarchy/validators/. -
scripts/jira-sync.mjs,scripts/intake-from-jira.mjs,scripts/promote-demande.mjsexist and pass a smoke test on one DAE. - DAE-0007 Transgesco migrated as proof-of-life : intrant sub-folders renamed (drop
00-01-numbering),05-Evaluation wifi/and04-requis gestions ressources/promoted to sister DAEs if criteria met. -
_catalog/D1 model updated withparent_initiative,parent_demande,disciplinecolumns on the demande object. - At least one
INIT-NNNN/exists for STM and one for Transgesco, with their demandes rattached viaparent_initiative.
Definition of done
- TFD-027 accepted and merged.
- TFD-025 carries a "refined by TFD-027" pointer.
- Memory updated :
client-solution-structureextended with the new under-client structure. - DAE-0007 Transgesco migration done, no broken paths in scripts.
- One end-to-end demande (intake → repo → Jira → deliverable URL) runs through the new pipeline.
Consequences
Pros
- One folder = one Jira issue. Sync is trivial.
- Adding a new discipline (e.g., OCM, Data) = adding a prefix in the table. No restructure.
- Re-parenting is data, not file moves.
- Light mode kills scaffolding overhead for the 50% of demandes that are short Q&R.
- Initiative/Capacité is recoverable from any demande's frontmatter — the agent always has its "tête" context.
Cons / risks
- Frontmatter discipline becomes load-bearing. A missing
parent_*field orphans a demande. Mitigation : validator script run pre-commit (CI item). ls clients/<slug>/will eventually have many entries (50+ for a long-running client). Acceptable —_catalog/and Jira give structured views.- Two clients with very different volumes get the same shape (no auto-folding). Acceptable — simplicity wins.
Out of scope (intentionally)
- Bidirectional content sync with Jira (description body, comments).
- Auto-promotion light → full (heuristic only, manual trigger).
- Capacity-level folder (
clients/<slug>/_cap-mobilite-billettique/). Only emerges if real content needs a home ; not by default. - Cross-client analytics (per TFD-019 hard isolation).
References
experiments/demande-hierarchy/proposals.md— A/B comparisonexperiments/demande-hierarchy/auto-research/research-log.md— eval scoringexperiments/demande-hierarchy/section-2-2-initiative.md— initiative section (10/10)- TFD-019 storage isolation
- TFD-025 client folder partition
- TFD-026 ID format (
{TYPE}-{NNNN})