/** * lib/proposal-tiers.ts — Canonical 3-tier proposal taxonomy of the BA * conversational skills: **Obligatoire / Suggestion / Élargissement**. * * SINGLE SOURCE OF TRUTH for how every client-facing BA proposal is tiered: * menu levels (applications, modules, sections, resources), use-case * discovery candidates, business-rule candidates and screen plans. The full * methodology (mandatory research, internal draft, self-audit, tiered * presentation) lives in `business-analyse/_workflow/proposal-method.md` — * repo-only, NOT deployed by the installer. * * The BA skills (deployed standalone, markdown-only) therefore carry the tier * table inline between `` markers. * `lib/__tests__/proposal-tiers-drift.test.ts` pins those tables to this * export — edit ALL carriers or the suite fails. Carriers: * * - business-analyse/_workflow/proposal-method.md (canonical methodology) * - business-analyse/create-menu/SKILL.md (all 4 menu levels) * - business-analyse/create-use-case/levels/discovery.md * - business-analyse/create-business-rules/levels/identify.md * - business-analyse/create-screen/SKILL.md * * Phases WITHOUT tiers (deliberate, do not add carriers there): actors * (candidates are tagged réutilisé/détecté/inféré — inventing roles corrupts * RBAC), data model and RBAC (traceable derivation phases — every item is * mandatory-because-traceable), and any `completeAuto` run (coverage audit, * no invention — see `_workflow/completeAuto-discipline.md`). */ /** One tier of the client-proposal taxonomy. */ export interface ProposalTier { /** Client-facing French tier name (rendered bold in the carried table). */ readonly name: string /** What belonging to this tier means. */ readonly meaning: string /** The test question that routes a candidate into this tier. */ readonly testQuestion: string } /** * The 3 tiers, in presentation order. A candidate that fits two tiers goes in * the HIGHER (more speculative) one; a proposal where every candidate lands in * Obligatoire is not categorised — it is playing safe (anti-pattern). */ export const PROPOSAL_TIERS: readonly ProposalTier[] = [ { name: 'Obligatoire', meaning: 'Core of the scope — without it the node loses its primary purpose', testQuestion: '"If I drop this item, does the scope lose its reason to exist?" — strict yes, rationale anchored in the client context or the existing tree', }, { name: 'Suggestion', meaning: 'Improves real usage at scale, or an industry standard often forgotten', testQuestion: '"Bulk, draft, export, exception handling, notification, audit trail, automation, delegation — does one of these apply here?"', }, { name: 'Élargissement', meaning: 'Beyond the initial scope — the vision direction', testQuestion: '"Analytics layer, AI-assisted action, predictive feature, collaborative angle — worth showing the client the future?"', }, ] as const /** Marker name carried by every inline copy of the tier table. */ export const PROPOSAL_TIERS_MARKER = 'proposal-tiers:v1' /** * Renders the canonical markdown block (markers included) that every carrier * must embed verbatim — the drift test compares parsed rows, so surrounding * prose stays free but the table itself must match. */ export function renderProposalTiersBlock(): string { const rows = PROPOSAL_TIERS.map( t => `| **${t.name}** | ${t.meaning} | ${t.testQuestion} |`, ).join('\n') return [ ``, '| Tier | Meaning | Test question |', '|---|---|---|', rows, ``, ].join('\n') }