@pukujan/create-modular-monolith 2.2.1 → 2.2.3

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.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/template/.cursor/rules/file-exchange-inbox.mdc +1 -1
  3. package/template/ARCHITECTURE_EXPORT_README.md +30 -0
  4. package/template/EXPORT_MANIFEST.json +74 -0
  5. package/template/NOTICE +2 -3
  6. package/template/README.md +1 -61
  7. package/template/backend/package.json +1 -1
  8. package/template/backend/src/modules/model-condenser/services/modelCondenser.service.js +36 -465
  9. package/template/backend/src/modules/model-condenser/tests/unit/modelCondenser.service.test.js +2 -3
  10. package/template/backend/src/shared/utils/consolidatedExport.js +155 -11
  11. package/template/backend/src/shared/utils/consolidatedExport.test.js +50 -0
  12. package/template/docs/README.md +0 -1
  13. package/template/docs/architecture/EVAL_AND_CI.md +68 -41
  14. package/template/docs/architecture/REPO_ARTIFACT_LAYOUT.md +57 -14
  15. package/template/docs/architecture/contracts/consolidatedExports.contract.md +14 -10
  16. package/template/docs/architecture/contracts/manifest.json +17 -0
  17. package/template/file-exchange/README.md +20 -15
  18. package/template/frontend/package.json +1 -1
  19. package/template/package.json +3 -3
  20. package/template/scripts/condense-all.mjs +52 -0
  21. package/template/scripts/condense-file-structure.mjs +19 -10
  22. package/template/scripts/condense-models.mjs +5 -3
  23. package/template/scripts/condense-prompts.mjs +13 -51
  24. package/template/scripts/consolidated-output.mjs +22 -10
  25. package/template/scripts/export-architecture-starter.mjs +389 -0
  26. package/template/scripts/lib/api-inventory.mjs +16 -61
  27. package/template/scripts/lib/repo-tree.mjs +26 -4
  28. package/template/scripts/sync-cli-template.mjs +44 -0
  29. package/template/scripts/write-pre-push-dev-log.mjs +1 -0
  30. package/template/file-exchange/exports/consolidated-models.json +0 -625
@@ -2,476 +2,47 @@ import { readFile, writeFile, mkdir } from "fs/promises";
2
2
  import { join, relative } from "path";
3
3
 
4
4
  /**
5
- * Scan the repo and build the consolidated model document (in memory).
6
- * @param {{ repoRoot: string }} options
5
+ * Platform-only schema inventory for scaffolded modular monoliths.
6
+ * Domain modules add models via their own manifests and condense runs.
7
7
  */
8
8
  export async function buildConsolidatedModels({ repoRoot }) {
9
- const ROOT = repoRoot;
10
-
11
- async function readJson(relPath) {
12
- const abs = join(ROOT, relPath);
13
- const raw = await readFile(abs, "utf8");
14
- return JSON.parse(raw);
15
- }
16
-
17
- async function tryReadJson(relPath) {
18
- try {
19
- return await readJson(relPath);
20
- } catch {
21
- return null;
22
- }
23
- }
24
-
25
- const models = {
9
+ return {
26
10
  meta: {
27
11
  generatedAt: new Date().toISOString(),
28
- repositoryRoot: ROOT,
12
+ repositoryRoot: repoRoot,
29
13
  condensedBy: "model-condenser",
30
- description:
31
- "Consolidated inventory of JSON shapes used by the legal-prmpt-eng case filing pipeline."
14
+ description: "Schema inventory for modular-monolith platform modules (_reference, contracts)."
32
15
  },
33
- inventory: [],
34
- definitions: {}
35
- };
36
-
37
- function addModel({
38
- id,
39
- name,
40
- category,
41
- sourcePaths,
42
- description,
43
- schema,
44
- example,
45
- exampleSourcePath
46
- }) {
47
- models.inventory.push({
48
- id,
49
- name,
50
- category,
51
- sourcePaths,
52
- description,
53
- hasSchema: Boolean(schema),
54
- hasExample: Boolean(example),
55
- exampleSourcePath: exampleSourcePath ?? null
56
- });
57
- models.definitions[id] = {
58
- name,
59
- category,
60
- sourcePaths,
61
- description,
62
- schema: schema ?? null,
63
- example: example ?? null
16
+ inventory: [
17
+ {
18
+ id: "ReferenceHealth",
19
+ name: "ReferenceHealth",
20
+ category: "platform",
21
+ sourcePaths: ["backend/src/modules/_reference/schemas/health.schema.js"],
22
+ description: "Example health check response shape from the _reference module.",
23
+ hasSchema: true,
24
+ hasExample: false,
25
+ exampleSourcePath: null
26
+ }
27
+ ],
28
+ definitions: {
29
+ ReferenceHealth: {
30
+ name: "ReferenceHealth",
31
+ category: "platform",
32
+ sourcePaths: ["backend/src/modules/_reference/schemas/health.schema.js"],
33
+ description: "GET /api/_reference/health response fields.",
34
+ schema: { status: "string", module: "string", version: "string" },
35
+ example: null
36
+ }
37
+ },
38
+ exampleInstances: {},
39
+ evalDatasets: {
40
+ description: "Per-module eval fixture paths (health-check shape tests).",
41
+ paths: ["backend/src/modules/_reference/evals/datasets/example.cases.json"]
42
+ }
64
43
  };
65
44
  }
66
45
 
67
- // --- Core domain (from handoffs/.../core-models.ts + shared JS typedefs) ---
68
- addModel({
69
- id: "CaseModel",
70
- name: "CaseModel",
71
- category: "core_domain",
72
- sourcePaths: [
73
- "work-log/handoffs/001_2026-05-23_starter_case-filing-ai-updated/models/typescript/core-models.ts",
74
- "backend/src/shared/domain/case-filing/core-models.js"
75
- ],
76
- description: "Top-level case identity and phase tracking.",
77
- schema: {
78
- caseId: "string",
79
- county: "string | null",
80
- court: "string | null",
81
- indexNumber: "string | null",
82
- caseName: "string | null",
83
- caseType: "string | null",
84
- judgeName: "string | null",
85
- partName: "string | null",
86
- currentPhase: "string | null",
87
- currentMiniPhase: "string | null",
88
- confidence: "high | medium | low"
89
- },
90
- example: await readJson("data/case-filing-ai/examples/case.json"),
91
- exampleSourcePath: "data/case-filing-ai/examples/case.json"
92
- });
93
-
94
- addModel({
95
- id: "DocumentModel",
96
- name: "DocumentModel",
97
- category: "core_domain",
98
- sourcePaths: [
99
- "work-log/handoffs/001_2026-05-23_starter_case-filing-ai-updated/models/typescript/core-models.ts",
100
- "backend/src/shared/domain/case-filing/core-models.js",
101
- "work-log/handoffs/001_2026-05-23_starter_case-filing-ai-updated/db/migrations/001_case_filing_ai_schema.sql"
102
- ],
103
- description: "A filing document linked to a case (NYSCEF-style metadata).",
104
- schema: {
105
- documentId: "string",
106
- caseId: "string",
107
- nyscefDocNo: "number | null",
108
- title: "string | null",
109
- documentType: "string | null",
110
- filedDateTime: "string | null",
111
- filedBy: "string | null",
112
- sourceFileName: "string",
113
- pageCount: "number | null",
114
- extractionStatus: "string",
115
- textReviewStatus: "unreviewed | partially_reviewed | reviewed | rejected"
116
- }
117
- });
118
-
119
- addModel({
120
- id: "DocumentTextVersionModel",
121
- name: "DocumentTextVersionModel",
122
- category: "core_domain",
123
- sourcePaths: [
124
- "work-log/handoffs/001_2026-05-23_starter_case-filing-ai-updated/models/typescript/core-models.ts",
125
- "backend/src/shared/domain/case-filing/core-models.js"
126
- ],
127
- description: "Versioned extracted or reviewed text for a document (filing-text-vault).",
128
- schema: {
129
- id: "string",
130
- caseId: "string",
131
- documentId: "string",
132
- versionType: "embedded_text | ocr_text | ai_parsed_text | human_reviewed_text",
133
- textContent: "string (optional)",
134
- structuredJson: "unknown (optional)",
135
- extractionMethod: "pdf_text | ocr | llm | human_review",
136
- reviewStatus: "unreviewed | partially_reviewed | reviewed | rejected",
137
- createdBy: "system | ai | human",
138
- createdAt: "string (ISO datetime)"
139
- }
140
- });
141
-
142
- addModel({
143
- id: "TaskModel",
144
- name: "TaskModel",
145
- category: "core_domain",
146
- sourcePaths: [
147
- "work-log/handoffs/001_2026-05-23_starter_case-filing-ai-updated/models/typescript/core-models.ts",
148
- "backend/src/shared/domain/case-filing/core-models.js"
149
- ],
150
- description: "Docketing task with due date and workflow status.",
151
- schema: {
152
- taskId: "string",
153
- caseId: "string",
154
- documentId: "string (optional)",
155
- taskDescription: "string",
156
- taskType: "string",
157
- responsibleParty: "string | null",
158
- dueDate: "string | null",
159
- dueDateStatus: "fixed | calculated | no_fixed_due_date | needs_review",
160
- status:
161
- "ai_extracted_unreviewed | source_supported_auto_saved | conditional | needs_ocr_review | corrected_later | superseded | human_verified",
162
- sourcePage: "number (optional)",
163
- confidence: "high | medium | low",
164
- docketingNote: "string (optional)"
165
- }
166
- });
167
-
168
- addModel({
169
- id: "HumanReviewItemModel",
170
- name: "HumanReviewItemModel",
171
- category: "core_domain",
172
- sourcePaths: [
173
- "work-log/handoffs/001_2026-05-23_starter_case-filing-ai-updated/models/typescript/core-models.ts",
174
- "backend/src/shared/domain/case-filing/core-models.js"
175
- ],
176
- description: "Mandatory human review queue item (OCR, handwriting, stamps, etc.).",
177
- schema: {
178
- itemId: "string",
179
- caseId: "string",
180
- documentId: "string",
181
- pageNumber: "number",
182
- location: "string",
183
- issue: "string",
184
- reason: "string",
185
- suggestedAction: "string",
186
- cropFilePath: "string (optional)",
187
- blocking: "boolean",
188
- status: "pending | reviewed | resolved"
189
- }
190
- });
191
-
192
- const emptySnapshot = {
193
- snapshotId: null,
194
- caseId: null,
195
- afterDocNo: null,
196
- currentPhase: null,
197
- currentMiniPhase: null,
198
- confirmedFacts: [],
199
- carriedForwardContext: [],
200
- openTasks: [],
201
- completedTasks: [],
202
- conditionalTasks: [],
203
- deadlines: [],
204
- supersededDeadlines: [],
205
- unresolvedHumanReviewItems: [],
206
- conflicts: [],
207
- auditNotes: []
208
- };
209
-
210
- addModel({
211
- id: "CaseStateSnapshotModel",
212
- name: "CaseStateSnapshotModel",
213
- category: "core_domain",
214
- sourcePaths: [
215
- "work-log/handoffs/001_2026-05-23_starter_case-filing-ai-updated/models/typescript/core-models.ts",
216
- "backend/src/shared/domain/case-filing/core-models.js",
217
- "backend/src/modules/case-filing-ai/services/localJsonStore.service.js"
218
- ],
219
- description: "Rolling case state after each processed document.",
220
- schema: {
221
- snapshotId: "string | null",
222
- caseId: "string | null",
223
- afterDocNo: "number | null",
224
- currentPhase: "string | null",
225
- currentMiniPhase: "string | null",
226
- confirmedFacts: "unknown[]",
227
- carriedForwardContext: "unknown[]",
228
- openTasks: "TaskModel[] | string[] (runtime varies)",
229
- completedTasks: "TaskModel[] | string[]",
230
- conditionalTasks: "TaskModel[] | string[]",
231
- deadlines: "unknown[]",
232
- supersededDeadlines: "unknown[]",
233
- unresolvedHumanReviewItems: "HumanReviewItemModel[]",
234
- conflicts: "unknown[]",
235
- auditNotes: "string[]"
236
- },
237
- example: await readJson("data/case-filing-ai/examples/case-snapshot.json"),
238
- exampleSourcePath: "data/case-filing-ai/examples/case-snapshot.json"
239
- });
240
-
241
- addModel({
242
- id: "EmptyCaseStateSnapshot",
243
- name: "EmptyCaseStateSnapshot",
244
- category: "runtime_default",
245
- sourcePaths: ["backend/src/modules/case-filing-ai/services/localJsonStore.service.js"],
246
- description: "Initial snapshot written when a new batch is created.",
247
- schema: emptySnapshot,
248
- example: emptySnapshot
249
- });
250
-
251
- // --- Prompt output schemas ---
252
- addModel({
253
- id: "MasterCaseFilingPromptOutput",
254
- name: "MasterCaseFilingPromptOutput",
255
- category: "prompt_output",
256
- sourcePaths: [
257
- "backend/src/modules/case-filing-ai/prompts/master-case-filing.prompt.md",
258
- "work-log/handoffs/002_2026-05-23_00-42_handoff_second.md"
259
- ],
260
- description: "Strict JSON returned by the master case-filing LLM prompt per document.",
261
- schema: {
262
- documentMetadata: "object",
263
- extractionQuality: "object",
264
- docketEntry: "object",
265
- caseUpdates: "object",
266
- parties: "array",
267
- witnesses: "array",
268
- tasks: "array",
269
- deadlines: "array",
270
- humanReviewItems: "array",
271
- updatedCaseSnapshot: "object",
272
- auditNotes: "array",
273
- inferredPartRuleText: "string",
274
- partRuleExtracts: "array"
275
- }
276
- });
277
-
278
- addModel({
279
- id: "PartRuleParsed",
280
- name: "PartRuleParsed",
281
- category: "prompt_output",
282
- sourcePaths: ["backend/src/modules/case-filing-ai/prompts/rule-parse.prompt.md"],
283
- description: "Structured part/judge rules after parsing user-supplied rule text.",
284
- schema: {
285
- partName: "string | null",
286
- judgeName: "string | null",
287
- county: "string | null",
288
- court: "string | null",
289
- rules: "string[]",
290
- schedulingNotes: "string[]",
291
- deadlinePolicies: "string[]",
292
- sourceSummary: "string",
293
- confidence: "high | medium | low"
294
- },
295
- example: await tryReadJson("data/case-filing-ai/batches/batch-002/rule/part-rules-parsed.json"),
296
- exampleSourcePath: "data/case-filing-ai/batches/batch-002/rule/part-rules-parsed.json"
297
- });
298
-
299
- addModel({
300
- id: "PartRuleStoredRecord",
301
- name: "PartRuleStoredRecord",
302
- category: "pipeline_storage",
303
- sourcePaths: ["backend/src/modules/case-filing-ai/services/uploadBatch.service.js"],
304
- description: "part-rules-parsed.json on disk: PartRuleParsed plus batch metadata.",
305
- schema: {
306
- source: "user_paste | user_upload | inferred_from_filings | pending_inference | none",
307
- savedAt: "string (ISO datetime)",
308
- extraction: "PartRuleExtraction | null",
309
- inferredFromDocs: "array (optional)",
310
- "...PartRuleParsed fields": "see PartRuleParsed"
311
- }
312
- });
313
-
314
- addModel({
315
- id: "PartRuleExtraction",
316
- name: "PartRuleExtraction",
317
- category: "pipeline_storage",
318
- sourcePaths: ["backend/src/modules/case-filing-ai/services/uploadBatch.service.js"],
319
- description: "part-rules-extraction.json: file upload metadata for a part rule file.",
320
- schema: {
321
- storedName: "string",
322
- originalName: "string",
323
- fileKind: "pdf | text | image | office | binary",
324
- extractionQuality: "ExtractionQuality",
325
- mimeType: "string",
326
- sizeBytes: "number"
327
- },
328
- example: await tryReadJson("data/case-filing-ai/batches/batch-002/rule/part-rules-extraction.json"),
329
- exampleSourcePath: "data/case-filing-ai/batches/batch-002/rule/part-rules-extraction.json"
330
- });
331
-
332
- addModel({
333
- id: "ExtractionQuality",
334
- name: "ExtractionQuality",
335
- category: "pipeline_runtime",
336
- sourcePaths: ["backend/src/modules/case-filing-ai/services/documentText.service.js"],
337
- description: "Text extraction quality flags for uploads and filings.",
338
- schema: {
339
- method: "string",
340
- fileKind: "string",
341
- textLength: "number",
342
- ocr_needed: "boolean",
343
- ocr_used: "boolean",
344
- ocr_model: "string | null",
345
- ocr_pages: "number[] | null",
346
- reviewStatus: "ai_extracted_unreviewed | ...",
347
- note: "string | null"
348
- }
349
- });
350
-
351
- addModel({
352
- id: "DocumentProcessingOutput",
353
- name: "DocumentProcessingOutput",
354
- category: "pipeline_storage",
355
- sourcePaths: [
356
- "backend/src/modules/case-filing-ai/services/uploadBatch.service.js",
357
- "data/case-filing-ai/batches/batch-002/outputs/doc-001.json"
358
- ],
359
- description: "Per-document JSON saved under data/.../outputs/doc-NNN.json after LLM processing.",
360
- schema: {
361
- docKey: "string",
362
- docIndex: "number",
363
- storedName: "string",
364
- originalName: "string",
365
- fileKind: "string",
366
- model: "string",
367
- usage: "object",
368
- documentMetadata: "object",
369
- extractionQuality: "object",
370
- docketEntry: "object",
371
- caseUpdates: "object",
372
- parties: "array",
373
- witnesses: "array",
374
- tasks: "array",
375
- deadlines: "array",
376
- humanReviewItems: "array",
377
- auditNotes: "array"
378
- },
379
- example: await tryReadJson("data/case-filing-ai/batches/batch-002/outputs/doc-001.json"),
380
- exampleSourcePath: "data/case-filing-ai/batches/batch-002/outputs/doc-001.json"
381
- });
382
-
383
- addModel({
384
- id: "BatchProcessingResult",
385
- name: "BatchProcessingResult",
386
- category: "api_response",
387
- sourcePaths: ["backend/src/modules/case-filing-ai/services/uploadBatch.service.js"],
388
- description: "POST /process-batch and GET /batches/:id/results response body.",
389
- schema: {
390
- batchId: "string",
391
- caseSnapshot: "CaseStateSnapshotModel",
392
- documents: "DocumentProcessingOutput[]",
393
- tasks: "array (aggregated)",
394
- deadlines: "array (aggregated)",
395
- humanReviewItems: "array (aggregated)",
396
- partRule: "PartRuleStoredRecord | null"
397
- }
398
- });
399
-
400
- addModel({
401
- id: "BatchStatus",
402
- name: "BatchStatus",
403
- category: "api_response",
404
- sourcePaths: ["backend/src/modules/case-filing-ai/services/uploadBatch.service.js"],
405
- description: "GET /batches/:id/status response body.",
406
- schema: {
407
- batchId: "string",
408
- status: "pending | processing | completed",
409
- currentStep: "string",
410
- currentDocument: "string | null",
411
- processedCount: "number",
412
- totalCount: "number"
413
- }
414
- });
415
-
416
- addModel({
417
- id: "RuleTextExtractResponse",
418
- name: "RuleTextExtractResponse",
419
- category: "api_response",
420
- sourcePaths: [
421
- "backend/src/modules/case-filing-ai/services/ruleText.service.js",
422
- "backend/src/modules/case-filing-ai/routes/caseFiling.routes.js"
423
- ],
424
- description: "POST /extract-rule-text response body.",
425
- schema: {
426
- text: "string",
427
- fileKind: "string",
428
- extractionQuality: "ExtractionQuality",
429
- originalName: "string"
430
- }
431
- });
432
-
433
- // --- Example JSON files (instances, also listed) ---
434
- const exampleJsonPaths = [
435
- "data/case-filing-ai/examples/case.json",
436
- "data/case-filing-ai/examples/case-snapshot.json",
437
- "work-log/handoffs/001_2026-05-23_starter_case-filing-ai-updated/examples/local-json/case.json",
438
- "work-log/handoffs/001_2026-05-23_starter_case-filing-ai-updated/examples/local-json/case-snapshot.json",
439
- "data/case-filing-ai/batches/batch-001/case-snapshot.json",
440
- "data/case-filing-ai/batches/batch-002/case-snapshot.json",
441
- "data/case-filing-ai/batches/batch-002/outputs/doc-002.json"
442
- ];
443
-
444
- models.exampleInstances = {};
445
- for (const rel of exampleJsonPaths) {
446
- const data = await tryReadJson(rel);
447
- if (data) {
448
- models.exampleInstances[relative(ROOT, rel)] = data;
449
- }
450
- }
451
-
452
- // Module eval datasets (health-check fixtures, not domain models)
453
- models.evalDatasets = {
454
- description: "Per-module eval fixture files (API health shape tests, not case domain models).",
455
- paths: [
456
- "backend/src/modules/_reference/evals/datasets/example.cases.json",
457
- "backend/src/modules/case-filing-ai/evals/datasets/example.cases.json",
458
- "backend/src/modules/case-workflow/evals/datasets/example.cases.json",
459
- "backend/src/modules/court-rules/evals/datasets/example.cases.json",
460
- "backend/src/modules/filing-pipeline/evals/datasets/example.cases.json",
461
- "backend/src/modules/filing-text-vault/evals/datasets/example.cases.json",
462
- "backend/src/modules/human-review/evals/datasets/example.cases.json",
463
- "backend/src/modules/task-docketing/evals/datasets/example.cases.json"
464
- ],
465
- sharedShape: await tryReadJson("backend/src/modules/task-docketing/evals/datasets/example.cases.json")
466
- };
467
-
468
- return models;
469
- }
470
-
471
- /**
472
- * Model condenser: writes models/consolidated-models.json and returns a summary.
473
- * @param {{ repoRoot: string, modelsDir: string, consolidatedFileName?: string, writeFile?: boolean, includePayload?: boolean }} options
474
- */
475
46
  export async function condenseModels({
476
47
  repoRoot,
477
48
  modelsDir,
@@ -489,17 +60,17 @@ export async function condenseModels({
489
60
  const { writeConsolidatedExport } = await import(
490
61
  "../../../shared/utils/consolidatedExport.js"
491
62
  );
492
- await writeConsolidatedExport(repoRoot, consolidatedFileName, jsonText);
63
+ await writeConsolidatedExport(repoRoot, consolidatedFileName, jsonText, {
64
+ condensedBy: "model-condenser"
65
+ });
493
66
  }
494
67
 
495
- const exampleInstanceCount = Object.keys(consolidated.exampleInstances ?? {}).length;
496
-
497
68
  return {
498
69
  status: "condensed",
499
70
  outputPath,
500
71
  outputRelativePath: relative(repoRoot, outputPath),
501
72
  modelCount: consolidated.inventory.length,
502
- exampleInstanceCount,
73
+ exampleInstanceCount: 0,
503
74
  generatedAt: consolidated.meta.generatedAt,
504
75
  inventory: consolidated.inventory,
505
76
  ...(includePayload ? { consolidated } : {})
@@ -18,13 +18,12 @@ test("condenseModels writes consolidated-models.json", async () => {
18
18
  });
19
19
 
20
20
  assert.equal(result.status, "condensed");
21
- assert.ok(result.modelCount >= 8);
22
- assert.ok(result.exampleInstanceCount >= 2);
21
+ assert.ok(result.modelCount >= 1);
23
22
 
24
23
  const raw = await readFile(join(modelsDir, "consolidated-models.json"), "utf8");
25
24
  const parsed = JSON.parse(raw);
26
25
  assert.equal(parsed.meta.condensedBy, "model-condenser");
27
- assert.ok(parsed.inventory.some((entry) => entry.id === "CaseModel"));
26
+ assert.ok(parsed.inventory.some((entry) => entry.id === "ReferenceHealth"));
28
27
  } finally {
29
28
  await rm(modelsDir, { recursive: true, force: true });
30
29
  }