@ogygie/core 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ogygie/core",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "Ogygie framework core: manifest/spec/error schemas, generic tenancy and gate tooling. Ships TypeScript sources — Ogygie products run tsx/vitest by construction (D1).",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -7,7 +7,7 @@
7
7
  * rejection of a non-kebab-case blockedBy target.
8
8
  */
9
9
  import { describe, expect, it } from "vitest";
10
- import { SpecFrontmatterSchema, compositionOfSpec, parseSpecDocument, parseSpecFrontmatter } from "./spec.schema.js";
10
+ import { SpecFrontmatterSchema, assumptionsOfSpec, compositionOfSpec, parseSpecDocument, parseSpecFrontmatter } from "./spec.schema.js";
11
11
 
12
12
  const NOMINAL_FRONTMATTER = `
13
13
  id: order-flow
@@ -179,3 +179,13 @@ describe("compositionOfSpec — la composition déclarée sous # Design (D53)",
179
179
  expect(compositionOfSpec("# Design\nRien.\n")).toBeNull();
180
180
  });
181
181
  });
182
+
183
+ describe("assumptionsOfSpec — les hypothèses déclarées sous # Hypothèses (D57)", () => {
184
+ it("lit une puce par hypothèse dans la section seulement ; sans section, aucune ; une section vide, aucune", () => {
185
+ const spec = "# @intent\nx\n\n# Budget\n- P95.\n\n# Hypothèses\n- Dix questions, choisies par le modèle.\n* Les réponses sont en anglais.\nUne ligne sans puce.\n\n# Design\n- Composition : liste\n";
186
+ expect(assumptionsOfSpec(spec)).toEqual(["Dix questions, choisies par le modèle.", "Les réponses sont en anglais."]);
187
+ expect(assumptionsOfSpec("# @intent\n- Hypothèses : aucune\n")).toEqual([]);
188
+ expect(assumptionsOfSpec("# Hypothèses\n\n# Design\n- a\n")).toEqual([]);
189
+ });
190
+ });
191
+
@@ -245,3 +245,27 @@ export function compositionOfSpec(content: string): string | null {
245
245
  const section = next === null ? rest : rest.slice(0, next.index);
246
246
  return COMPOSITION_LINE.exec(section)?.[1] ?? null;
247
247
  }
248
+
249
+ // ------------------------------------------------------------- # Hypothèses (D57)
250
+ export const ASSUMPTIONS_SECTION_TITLE = "Hypothèses";
251
+
252
+ /**
253
+ * The assumptions a draft declares under `# Hypothèses` (spec
254
+ * ogygie-hosted-mode B3, D57): one bullet per point the model decided that
255
+ * the brief did not say. The section sits after `# Budget` and is admitted
256
+ * like `# Design`: the six canonical sections are read in order, anything
257
+ * after them is the spec's own. Empty when the spec declares none.
258
+ */
259
+ export function assumptionsOfSpec(content: string): string[] {
260
+ const heading = new RegExp(`^# ${ASSUMPTIONS_SECTION_TITLE}\\s*$`, "mu").exec(content);
261
+ if (heading === null) return [];
262
+ const rest = content.slice(heading.index + heading[0].length);
263
+ const next = /^# /mu.exec(rest);
264
+ const section = next === null ? rest : rest.slice(0, next.index);
265
+ return section
266
+ .split("\n")
267
+ .map((line) => line.trim())
268
+ .filter((line) => /^[-*]\s+\S/u.test(line))
269
+ .map((line) => line.replace(/^[-*]\s+/u, "").trim());
270
+ }
271
+