@ogygie/core 0.13.0 → 0.13.1

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.13.0",
3
+ "version": "0.13.1",
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
  "type": "module",
@@ -5,7 +5,8 @@
5
5
  * confirm, and that neither shadows the other.
6
6
  */
7
7
  import { describe, expect, it } from "vitest";
8
- import { declaredEntries, findSpecOwner } from "./manifest-owners.js";
8
+ import { declaredEntries, findSpecOwner, hasImplementation } from "./manifest-owners.js";
9
+ import type { SpecOwner, SpecOwnerKind } from "./manifest-owners.js";
9
10
  import { OgygieManifestSchema } from "./manifest.schema.js";
10
11
 
11
12
  function manifestWith(extra: Record<string, unknown>) {
@@ -250,3 +251,30 @@ describe("declaredEntries — tout ce que la carte déclare, à un seul endroit"
250
251
  expect(declaredEntries(withApps({}))).toEqual([]);
251
252
  });
252
253
  });
254
+
255
+ describe("hasImplementation — ce qui prouve qu'une spec draft a déjà du code", () => {
256
+ const owner = (
257
+ kind: SpecOwnerKind,
258
+ status: "skeleton" | "in-progress" | "delivered",
259
+ ): SpecOwner => ({ kind, name: "peu-importe", status, folder: "un/chemin" });
260
+
261
+ it("ÉTANT DONNÉ un statut au-delà de skeleton ALORS l'implémentation existe, dossier ou pas", () => {
262
+ for (const kind of ["feature", "package", "framework-package"] as SpecOwnerKind[]) {
263
+ expect(hasImplementation(owner(kind, "in-progress"), false)).toBe(true);
264
+ expect(hasImplementation(owner(kind, "delivered"), false)).toBe(true);
265
+ }
266
+ });
267
+
268
+ it("une FEATURE skeleton dont le dossier existe a bien du code — c'est la preuve historique", () => {
269
+ expect(hasImplementation(owner("feature", "skeleton"), true)).toBe(true);
270
+ expect(hasImplementation(owner("feature", "skeleton"), false)).toBe(false);
271
+ });
272
+
273
+ it("MUTATION : un PACKAGE skeleton dont le dossier existe n'a PAS de code — le manifeste EXIGE ce dossier", () => {
274
+ // Sans cette règle, une spec draft et un package skeleton ne peuvent pas
275
+ // coexister : verify-manifest réclame le dossier, verify-specs y lit du
276
+ // code, et le projet est coincé entre deux fichiers gouvernés.
277
+ expect(hasImplementation(owner("package", "skeleton"), true)).toBe(false);
278
+ expect(hasImplementation(owner("framework-package", "skeleton"), true)).toBe(false);
279
+ });
280
+ });
@@ -158,6 +158,28 @@ export function findSpecOwner(
158
158
  return { kind: owner.kind, name: owner.name, status: owner.status, folder: owner.folder };
159
159
  }
160
160
 
161
+ /**
162
+ * Does an implementation of this spec exist? The question the `draft` rule of
163
+ * the spec lifecycle rests on ("draft = no code yet").
164
+ *
165
+ * A declared status above `skeleton` always answers yes. A FOLDER on disk only
166
+ * answers yes for a FEATURE, whose folder is born with its code and is
167
+ * therefore evidence of it. A package's folder is NOT evidence: the manifest
168
+ * gate REQUIRES it to exist (MANIFEST_PACKAGE_NOT_FOUND), so a package
169
+ * declared `skeleton` whose spec is still `draft` could satisfy neither gate —
170
+ * one demands the folder, the other reads it as code, and the project is stuck
171
+ * between two governed files it may not edit. Observed on a consumer project
172
+ * the day it declared its first package before writing it.
173
+ *
174
+ * Lives here rather than in the gate script for the reason the whole module
175
+ * exists: the rule travels with the versioned package, not with a copy each
176
+ * project keeps.
177
+ */
178
+ export function hasImplementation(owner: SpecOwner, folderExists: boolean): boolean {
179
+ if (owner.status !== "skeleton") return true;
180
+ return owner.kind === "feature" && folderExists;
181
+ }
182
+
161
183
  /** What a refusal should suggest — both natures, never one that cannot exist. */
162
184
  export const SPEC_OWNER_FIX =
163
185
  "Set the owning entry to delivered in the manifest — a feature under `features`, a library under `packages`, or a published package under `framework.packages` (which names its spec explicitly) — or keep the spec in validated.";