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:

  1. 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.
  2. 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.
  3. 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 (unclassifiedea/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 with jira_key: filled.
  • Push from repo : scripts/jira-sync.mjs <client-slug> scans _demande.md files and pushes status, 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.md updated with the prefix table (INIT/DAE/PM/OPS) and the per-client global sequence rule.
  • _demande.md and _context.md JSON-schema-equivalent validators committed at experiments/demande-hierarchy/validators/.
  • scripts/jira-sync.mjs, scripts/intake-from-jira.mjs, scripts/promote-demande.mjs exist 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/ and 04-requis gestions ressources/ promoted to sister DAEs if criteria met.
  • _catalog/ D1 model updated with parent_initiative, parent_demande, discipline columns on the demande object.
  • At least one INIT-NNNN/ exists for STM and one for Transgesco, with their demandes rattached via parent_initiative.

Definition of done

  • TFD-027 accepted and merged.
  • TFD-025 carries a "refined by TFD-027" pointer.
  • Memory updated : client-solution-structure extended 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 comparison
  • experiments/demande-hierarchy/auto-research/research-log.md — eval scoring
  • experiments/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})