@pukujan/create-modular-monolith 2.0.0 → 2.2.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/README.md +94 -23
- package/index.js +47 -0
- package/package.json +16 -19
- package/template/.cursor/commands/planning-study-log.md +25 -0
- package/template/.cursor/commands/pre-push-dev-log.md +52 -0
- package/template/.cursor/rules/api-documentation.mdc +21 -0
- package/template/.cursor/rules/file-exchange-inbox.mdc +29 -0
- package/template/.github/workflows/ci.yml +44 -0
- package/template/AGENTS.md +41 -0
- package/template/README.md +25 -55
- package/template/backend/.env.example +38 -0
- package/template/backend/package-lock.json +1118 -24
- package/template/backend/package.json +14 -4
- package/template/backend/scripts/check-module-boundaries.mjs +3 -0
- package/template/backend/src/modules/model-condenser/README.md +7 -0
- package/template/backend/src/modules/model-condenser/config/index.js +20 -0
- package/template/backend/src/modules/model-condenser/events/index.js +1 -0
- package/template/backend/src/modules/model-condenser/index.js +12 -0
- package/template/backend/src/modules/model-condenser/routes/health.routes.js +10 -0
- package/template/backend/src/modules/model-condenser/routes/index.js +10 -0
- package/template/backend/src/modules/model-condenser/routes/modelCondenser.routes.js +44 -0
- package/template/backend/src/modules/model-condenser/services/health.service.js +8 -0
- package/template/backend/src/modules/model-condenser/services/modelCondenser.facade.js +58 -0
- package/template/backend/src/modules/model-condenser/services/modelCondenser.service.js +513 -0
- package/template/backend/src/modules/model-condenser/tests/integration/modelCondenser.routes.test.js +40 -0
- package/template/backend/src/modules/model-condenser/tests/unit/modelCondenser.service.test.js +31 -0
- package/template/backend/src/modules/model-condenser/utils/index.js +1 -0
- package/template/backend/src/shared/contracts/consolidatedExports.contract.js +19 -0
- package/template/backend/src/shared/contracts/prePushDevLog.contract.js +28 -0
- package/template/backend/src/shared/domain/case-filing/core-models.js +117 -0
- package/template/backend/src/shared/http/errors.js +8 -0
- package/template/backend/src/shared/utils/consolidatedExport.js +30 -0
- package/template/backend/src/shared/utils/formatExchangeTimestamp.js +47 -0
- package/template/backend/src/shared/utils/formatExchangeTimestamp.test.js +30 -0
- package/template/backend/src/shared/utils/traceId.js +19 -0
- package/template/docs/API.md +42 -0
- package/template/docs/PUBLISHING.md +13 -1
- package/template/docs/README.md +7 -1
- package/template/docs/STARTER_PACK.md +4 -0
- package/template/docs/architecture/API_DOCUMENTATION_CONTRACT.md +112 -0
- package/template/docs/architecture/CONTRACTS_OVERVIEW.md +180 -0
- package/template/docs/architecture/EVAL_AND_CI.md +79 -0
- package/template/docs/architecture/MODULE_INTERNAL_CONTRACT.md +2 -0
- package/template/docs/architecture/PLATFORM_ARCHITECTURE.md +221 -0
- package/template/docs/architecture/REPO_ARTIFACT_LAYOUT.md +33 -0
- package/template/docs/architecture/contracts/apiDocumentationRegistry.contract.md +40 -0
- package/template/docs/architecture/contracts/changelog.jsonl +12 -0
- package/template/docs/architecture/contracts/consolidatedExports.contract.md +58 -0
- package/template/docs/architecture/contracts/fileExchange.contract.md +47 -0
- package/template/docs/architecture/contracts/manifest.json +39 -0
- package/template/docs/architecture/contracts/prePushDevLog.contract.md +69 -0
- package/template/docs/model-condenser/API.md +102 -0
- package/template/file-exchange/README.md +41 -0
- package/template/file-exchange/exports/.gitkeep +0 -0
- package/template/file-exchange/exports/consolidated-models.json +625 -0
- package/template/file-exchange/imports/.gitkeep +0 -0
- package/template/frontend/.env.example +2 -0
- package/template/frontend/package-lock.json +125 -122
- package/template/frontend/package.json +1 -1
- package/template/frontend/src/index.css +311 -0
- package/template/frontend/src/modules/_reference/services/health-api.js +1 -1
- package/template/frontend/src/shared/api/client.js +67 -5
- package/template/models/.gitkeep +0 -0
- package/template/package.json +13 -4
- package/template/scripts/check-api-docs.mjs +183 -0
- package/template/scripts/condense-file-structure.mjs +44 -0
- package/template/scripts/condense-models.mjs +70 -0
- package/template/scripts/condense-prompts.mjs +161 -0
- package/template/scripts/consolidated-output.mjs +49 -0
- package/template/scripts/export-consolidated-models.mjs +11 -0
- package/template/scripts/git-hooks/pre-push.sample +15 -0
- package/template/scripts/import-to-file-exchange.mjs +43 -0
- package/template/scripts/lib/api-inventory.mjs +182 -0
- package/template/scripts/lib/dev-log-human-format.mjs +360 -0
- package/template/scripts/lib/git-snapshot.mjs +46 -0
- package/template/scripts/lib/module-scaffold.mjs +37 -1
- package/template/scripts/lib/repo-tree.mjs +127 -0
- package/template/scripts/lib/run-tests.mjs +60 -0
- package/template/scripts/lint-contracts.mjs +57 -0
- package/template/scripts/lint-repo-artifacts.mjs +37 -0
- package/template/scripts/new-module.mjs +7 -0
- package/template/scripts/resolve-import-stamp.mjs +50 -0
- package/template/scripts/verify-dev-log.mjs +50 -0
- package/template/scripts/write-pre-push-dev-log.mjs +220 -0
- package/template/work-log/INDEX.md +3 -0
- package/template/work-log/README.md +40 -0
- package/template/work-log/dev-logs/README.md +97 -0
- package/template/work-log/dev-logs/schemas/dev-log-agent.v1.schema.json +119 -0
- package/template/work-log/dev-logs/templates/dev-log-human.template.md +10 -0
- package/template/work-log/handoffs/README.md +36 -0
- package/template/work-log/study-docs/README.md +13 -0
- package/bin/create-modular-monolith.js +0 -132
- package/template/backend/src/modules/_reference/evals/README.md +0 -6
- package/template/backend/src/modules/_reference/evals/datasets/example.cases.json +0 -12
- package/template/backend/src/modules/_reference/evals/runners/example.eval.mjs +0 -25
- package/template/scripts/sync-cli-template.mjs +0 -44
- /package/template/{frontend/src/modules → backend/db/migrations}/.gitkeep +0 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
2
|
+
import { join, relative } from "path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Scan the repo and build the consolidated model document (in memory).
|
|
6
|
+
* @param {{ repoRoot: string }} options
|
|
7
|
+
*/
|
|
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 = {
|
|
26
|
+
meta: {
|
|
27
|
+
generatedAt: new Date().toISOString(),
|
|
28
|
+
repositoryRoot: ROOT,
|
|
29
|
+
condensedBy: "model-condenser",
|
|
30
|
+
description:
|
|
31
|
+
"Consolidated inventory of JSON shapes used by the legal-prmpt-eng case filing pipeline."
|
|
32
|
+
},
|
|
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
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
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
|
+
export async function condenseModels({
|
|
476
|
+
repoRoot,
|
|
477
|
+
modelsDir,
|
|
478
|
+
consolidatedFileName = "consolidated-models.json",
|
|
479
|
+
writeFile: shouldWrite = true,
|
|
480
|
+
includePayload = false
|
|
481
|
+
}) {
|
|
482
|
+
const consolidated = await buildConsolidatedModels({ repoRoot });
|
|
483
|
+
const outputPath = join(modelsDir, consolidatedFileName);
|
|
484
|
+
|
|
485
|
+
if (shouldWrite) {
|
|
486
|
+
await mkdir(modelsDir, { recursive: true });
|
|
487
|
+
const jsonText = `${JSON.stringify(consolidated, null, 2)}\n`;
|
|
488
|
+
await writeFile(outputPath, jsonText, "utf8");
|
|
489
|
+
const { writeConsolidatedExport } = await import(
|
|
490
|
+
"../../../shared/utils/consolidatedExport.js"
|
|
491
|
+
);
|
|
492
|
+
await writeConsolidatedExport(repoRoot, consolidatedFileName, jsonText);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const exampleInstanceCount = Object.keys(consolidated.exampleInstances ?? {}).length;
|
|
496
|
+
|
|
497
|
+
return {
|
|
498
|
+
status: "condensed",
|
|
499
|
+
outputPath,
|
|
500
|
+
outputRelativePath: relative(repoRoot, outputPath),
|
|
501
|
+
modelCount: consolidated.inventory.length,
|
|
502
|
+
exampleInstanceCount,
|
|
503
|
+
generatedAt: consolidated.meta.generatedAt,
|
|
504
|
+
inventory: consolidated.inventory,
|
|
505
|
+
...(includePayload ? { consolidated } : {})
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
export async function readConsolidatedModels({ modelsDir, consolidatedFileName = "consolidated-models.json" }) {
|
|
510
|
+
const outputPath = join(modelsDir, consolidatedFileName);
|
|
511
|
+
const raw = await readFile(outputPath, "utf8");
|
|
512
|
+
return JSON.parse(raw);
|
|
513
|
+
}
|
package/template/backend/src/modules/model-condenser/tests/integration/modelCondenser.routes.test.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtemp, rm } from "fs/promises";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { tmpdir } from "os";
|
|
6
|
+
import { createTestApp } from "../../../../shared/testing/create-test-app.js";
|
|
7
|
+
|
|
8
|
+
test("POST /api/model-condenser/condense regenerates consolidated file", async () => {
|
|
9
|
+
const modelsDir = await mkdtemp(join(tmpdir(), "model-condenser-api-"));
|
|
10
|
+
process.env.MODEL_CONDENSER_OUTPUT_DIR = modelsDir;
|
|
11
|
+
|
|
12
|
+
const { register } = await import("../../index.js");
|
|
13
|
+
const app = createTestApp(register);
|
|
14
|
+
const server = app.listen(0);
|
|
15
|
+
const { port } = server.address();
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/model-condenser/condense`, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: { "Content-Type": "application/json" },
|
|
21
|
+
body: JSON.stringify({ includePayload: false })
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
assert.equal(res.status, 201);
|
|
25
|
+
const body = await res.json();
|
|
26
|
+
assert.equal(body.status, "condensed");
|
|
27
|
+
assert.ok(body.modelCount >= 16);
|
|
28
|
+
assert.ok(body.outputPath.includes("consolidated-models.json"));
|
|
29
|
+
|
|
30
|
+
const getRes = await fetch(`http://127.0.0.1:${port}/api/model-condenser/consolidated`);
|
|
31
|
+
assert.equal(getRes.status, 200);
|
|
32
|
+
const summary = await getRes.json();
|
|
33
|
+
assert.equal(summary.status, "ready");
|
|
34
|
+
assert.equal(summary.modelCount, body.modelCount);
|
|
35
|
+
} finally {
|
|
36
|
+
server.close();
|
|
37
|
+
delete process.env.MODEL_CONDENSER_OUTPUT_DIR;
|
|
38
|
+
await rm(modelsDir, { recursive: true, force: true });
|
|
39
|
+
}
|
|
40
|
+
});
|
package/template/backend/src/modules/model-condenser/tests/unit/modelCondenser.service.test.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtemp, readFile, rm } from "fs/promises";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { tmpdir } from "os";
|
|
6
|
+
import { condenseModels } from "../../services/modelCondenser.service.js";
|
|
7
|
+
import { getModuleConfig } from "../../config/index.js";
|
|
8
|
+
|
|
9
|
+
const { repoRoot } = getModuleConfig();
|
|
10
|
+
|
|
11
|
+
test("condenseModels writes consolidated-models.json", async () => {
|
|
12
|
+
const modelsDir = await mkdtemp(join(tmpdir(), "model-condenser-"));
|
|
13
|
+
try {
|
|
14
|
+
const result = await condenseModels({
|
|
15
|
+
repoRoot,
|
|
16
|
+
modelsDir,
|
|
17
|
+
consolidatedFileName: "consolidated-models.json"
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
assert.equal(result.status, "condensed");
|
|
21
|
+
assert.ok(result.modelCount >= 8);
|
|
22
|
+
assert.ok(result.exampleInstanceCount >= 2);
|
|
23
|
+
|
|
24
|
+
const raw = await readFile(join(modelsDir, "consolidated-models.json"), "utf8");
|
|
25
|
+
const parsed = JSON.parse(raw);
|
|
26
|
+
assert.equal(parsed.meta.condensedBy, "model-condenser");
|
|
27
|
+
assert.ok(parsed.inventory.some((entry) => entry.id === "CaseModel"));
|
|
28
|
+
} finally {
|
|
29
|
+
await rm(modelsDir, { recursive: true, force: true });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** @readonly Consolidated repo snapshot exports contract. */
|
|
2
|
+
|
|
3
|
+
export {
|
|
4
|
+
CONSOLIDATED_EXPORT_DIR,
|
|
5
|
+
CONSOLIDATED_FILENAMES,
|
|
6
|
+
writeConsolidatedExport
|
|
7
|
+
} from "../utils/consolidatedExport.js";
|
|
8
|
+
|
|
9
|
+
export const CONSOLIDATED_EXPORTS_VERSION = "v001";
|
|
10
|
+
|
|
11
|
+
/** API / model-condenser mirror path. */
|
|
12
|
+
export const CONSOLIDATED_MODELS_MIRROR_DIR = "models";
|
|
13
|
+
|
|
14
|
+
export const CONSOLIDATED_NPM_SCRIPTS = {
|
|
15
|
+
models: "condense-models",
|
|
16
|
+
prompts: "condense-prompts",
|
|
17
|
+
fileStructure: "condense-file-structure",
|
|
18
|
+
all: "condense:all"
|
|
19
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** @readonly Pre-push dev log contract (human MD + agent JSON). */
|
|
2
|
+
|
|
3
|
+
export const PRE_PUSH_DEV_LOG_VERSION = "v001";
|
|
4
|
+
export const DEV_LOG_AGENT_SCHEMA_VERSION = "1.0.0";
|
|
5
|
+
|
|
6
|
+
export const DEV_LOG_HUMAN_DIR = "work-log/dev-logs/human";
|
|
7
|
+
export const DEV_LOG_AGENT_DIR = "work-log/dev-logs/agent";
|
|
8
|
+
export const DEV_LOG_AGENT_SCHEMA = "work-log/dev-logs/schemas/dev-log-agent.v1.schema.json";
|
|
9
|
+
export const DEV_LOG_HUMAN_FORMAT = "scripts/lib/dev-log-human-format.mjs";
|
|
10
|
+
|
|
11
|
+
export const DEV_LOG_GENERATOR_SCRIPT = "scripts/write-pre-push-dev-log.mjs";
|
|
12
|
+
export const DEV_LOG_VERIFY_SCRIPT = "scripts/verify-dev-log.mjs";
|
|
13
|
+
export const DEV_LOG_API_INVENTORY_SCRIPT = "scripts/lib/api-inventory.mjs";
|
|
14
|
+
export const DEV_LOG_REPO_TREE_SCRIPT = "scripts/lib/repo-tree.mjs";
|
|
15
|
+
|
|
16
|
+
/** Same as `tree -I "node_modules|.git|dist|build"`. */
|
|
17
|
+
export const DEV_LOG_TREE_IGNORE_DIRS = ["node_modules", ".git", "dist", "build", ".DS_Store"];
|
|
18
|
+
|
|
19
|
+
/** Filename patterns (paired by `{NNN}_{date}_{time}` prefix). */
|
|
20
|
+
export const DEV_LOG_HUMAN_FILENAME_PATTERN =
|
|
21
|
+
"{NNN}_{YYYY-MM-DD}_{HH-MM}_dev-log_{slug}.md";
|
|
22
|
+
export const DEV_LOG_AGENT_FILENAME_PATTERN =
|
|
23
|
+
"{NNN}_{YYYY-MM-DD}_{HH-MM}_dev-log-agent_{slug}.json";
|
|
24
|
+
|
|
25
|
+
export const DEV_LOG_NPM_SCRIPTS = {
|
|
26
|
+
prePush: "dev-log:pre-push",
|
|
27
|
+
verify: "dev-log:verify"
|
|
28
|
+
};
|