@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.
Files changed (97) hide show
  1. package/README.md +94 -23
  2. package/index.js +47 -0
  3. package/package.json +16 -19
  4. package/template/.cursor/commands/planning-study-log.md +25 -0
  5. package/template/.cursor/commands/pre-push-dev-log.md +52 -0
  6. package/template/.cursor/rules/api-documentation.mdc +21 -0
  7. package/template/.cursor/rules/file-exchange-inbox.mdc +29 -0
  8. package/template/.github/workflows/ci.yml +44 -0
  9. package/template/AGENTS.md +41 -0
  10. package/template/README.md +25 -55
  11. package/template/backend/.env.example +38 -0
  12. package/template/backend/package-lock.json +1118 -24
  13. package/template/backend/package.json +14 -4
  14. package/template/backend/scripts/check-module-boundaries.mjs +3 -0
  15. package/template/backend/src/modules/model-condenser/README.md +7 -0
  16. package/template/backend/src/modules/model-condenser/config/index.js +20 -0
  17. package/template/backend/src/modules/model-condenser/events/index.js +1 -0
  18. package/template/backend/src/modules/model-condenser/index.js +12 -0
  19. package/template/backend/src/modules/model-condenser/routes/health.routes.js +10 -0
  20. package/template/backend/src/modules/model-condenser/routes/index.js +10 -0
  21. package/template/backend/src/modules/model-condenser/routes/modelCondenser.routes.js +44 -0
  22. package/template/backend/src/modules/model-condenser/services/health.service.js +8 -0
  23. package/template/backend/src/modules/model-condenser/services/modelCondenser.facade.js +58 -0
  24. package/template/backend/src/modules/model-condenser/services/modelCondenser.service.js +513 -0
  25. package/template/backend/src/modules/model-condenser/tests/integration/modelCondenser.routes.test.js +40 -0
  26. package/template/backend/src/modules/model-condenser/tests/unit/modelCondenser.service.test.js +31 -0
  27. package/template/backend/src/modules/model-condenser/utils/index.js +1 -0
  28. package/template/backend/src/shared/contracts/consolidatedExports.contract.js +19 -0
  29. package/template/backend/src/shared/contracts/prePushDevLog.contract.js +28 -0
  30. package/template/backend/src/shared/domain/case-filing/core-models.js +117 -0
  31. package/template/backend/src/shared/http/errors.js +8 -0
  32. package/template/backend/src/shared/utils/consolidatedExport.js +30 -0
  33. package/template/backend/src/shared/utils/formatExchangeTimestamp.js +47 -0
  34. package/template/backend/src/shared/utils/formatExchangeTimestamp.test.js +30 -0
  35. package/template/backend/src/shared/utils/traceId.js +19 -0
  36. package/template/docs/API.md +42 -0
  37. package/template/docs/PUBLISHING.md +13 -1
  38. package/template/docs/README.md +7 -1
  39. package/template/docs/STARTER_PACK.md +4 -0
  40. package/template/docs/architecture/API_DOCUMENTATION_CONTRACT.md +112 -0
  41. package/template/docs/architecture/CONTRACTS_OVERVIEW.md +180 -0
  42. package/template/docs/architecture/EVAL_AND_CI.md +79 -0
  43. package/template/docs/architecture/MODULE_INTERNAL_CONTRACT.md +2 -0
  44. package/template/docs/architecture/PLATFORM_ARCHITECTURE.md +221 -0
  45. package/template/docs/architecture/REPO_ARTIFACT_LAYOUT.md +33 -0
  46. package/template/docs/architecture/contracts/apiDocumentationRegistry.contract.md +40 -0
  47. package/template/docs/architecture/contracts/changelog.jsonl +12 -0
  48. package/template/docs/architecture/contracts/consolidatedExports.contract.md +58 -0
  49. package/template/docs/architecture/contracts/fileExchange.contract.md +47 -0
  50. package/template/docs/architecture/contracts/manifest.json +39 -0
  51. package/template/docs/architecture/contracts/prePushDevLog.contract.md +69 -0
  52. package/template/docs/model-condenser/API.md +102 -0
  53. package/template/file-exchange/README.md +41 -0
  54. package/template/file-exchange/exports/.gitkeep +0 -0
  55. package/template/file-exchange/exports/consolidated-models.json +625 -0
  56. package/template/file-exchange/imports/.gitkeep +0 -0
  57. package/template/frontend/.env.example +2 -0
  58. package/template/frontend/package-lock.json +125 -122
  59. package/template/frontend/package.json +1 -1
  60. package/template/frontend/src/index.css +311 -0
  61. package/template/frontend/src/modules/_reference/services/health-api.js +1 -1
  62. package/template/frontend/src/shared/api/client.js +67 -5
  63. package/template/models/.gitkeep +0 -0
  64. package/template/package.json +13 -4
  65. package/template/scripts/check-api-docs.mjs +183 -0
  66. package/template/scripts/condense-file-structure.mjs +44 -0
  67. package/template/scripts/condense-models.mjs +70 -0
  68. package/template/scripts/condense-prompts.mjs +161 -0
  69. package/template/scripts/consolidated-output.mjs +49 -0
  70. package/template/scripts/export-consolidated-models.mjs +11 -0
  71. package/template/scripts/git-hooks/pre-push.sample +15 -0
  72. package/template/scripts/import-to-file-exchange.mjs +43 -0
  73. package/template/scripts/lib/api-inventory.mjs +182 -0
  74. package/template/scripts/lib/dev-log-human-format.mjs +360 -0
  75. package/template/scripts/lib/git-snapshot.mjs +46 -0
  76. package/template/scripts/lib/module-scaffold.mjs +37 -1
  77. package/template/scripts/lib/repo-tree.mjs +127 -0
  78. package/template/scripts/lib/run-tests.mjs +60 -0
  79. package/template/scripts/lint-contracts.mjs +57 -0
  80. package/template/scripts/lint-repo-artifacts.mjs +37 -0
  81. package/template/scripts/new-module.mjs +7 -0
  82. package/template/scripts/resolve-import-stamp.mjs +50 -0
  83. package/template/scripts/verify-dev-log.mjs +50 -0
  84. package/template/scripts/write-pre-push-dev-log.mjs +220 -0
  85. package/template/work-log/INDEX.md +3 -0
  86. package/template/work-log/README.md +40 -0
  87. package/template/work-log/dev-logs/README.md +97 -0
  88. package/template/work-log/dev-logs/schemas/dev-log-agent.v1.schema.json +119 -0
  89. package/template/work-log/dev-logs/templates/dev-log-human.template.md +10 -0
  90. package/template/work-log/handoffs/README.md +36 -0
  91. package/template/work-log/study-docs/README.md +13 -0
  92. package/bin/create-modular-monolith.js +0 -132
  93. package/template/backend/src/modules/_reference/evals/README.md +0 -6
  94. package/template/backend/src/modules/_reference/evals/datasets/example.cases.json +0 -12
  95. package/template/backend/src/modules/_reference/evals/runners/example.eval.mjs +0 -25
  96. package/template/scripts/sync-cli-template.mjs +0 -44
  97. /package/template/{frontend/src/modules → backend/db/migrations}/.gitkeep +0 -0
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync, existsSync } from "fs";
3
+ import { join, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
7
+ const manifestPath = join(repoRoot, "docs/architecture/contracts/manifest.json");
8
+
9
+ const PATH_KEYS = [
10
+ "file",
11
+ "doc",
12
+ "overview",
13
+ "userDoc",
14
+ "timestampHelper",
15
+ "importScript",
16
+ "utility",
17
+ "schema",
18
+ "generator",
19
+ "humanFormat",
20
+ "verify",
21
+ "apiInventory",
22
+ "repoTree",
23
+ "workLogReadme",
24
+ "registry",
25
+ "lintScript",
26
+ "architectureDoc",
27
+ "promptVersions"
28
+ ];
29
+
30
+ if (!existsSync(manifestPath)) {
31
+ console.error("Missing contracts manifest:", manifestPath);
32
+ process.exit(1);
33
+ }
34
+
35
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
36
+ const failures = [];
37
+
38
+ for (const [key, entry] of Object.entries(manifest)) {
39
+ const paths = PATH_KEYS.map((k) => entry?.[k]).filter(Boolean);
40
+ if (!paths.length) {
41
+ failures.push(`${key}: no documented paths in manifest`);
42
+ continue;
43
+ }
44
+ for (const rel of paths) {
45
+ const abs = join(repoRoot, rel);
46
+ if (!existsSync(abs)) {
47
+ failures.push(`${key}: path not found — ${rel}`);
48
+ }
49
+ }
50
+ }
51
+
52
+ if (failures.length) {
53
+ console.error("Contract lint failed:\n" + failures.map((f) => ` - ${f}`).join("\n"));
54
+ process.exit(1);
55
+ }
56
+
57
+ console.log(`Contract lint OK (${Object.keys(manifest).length} entries)`);
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+ import { existsSync } from "fs";
3
+ import { join, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
7
+
8
+ const requiredPaths = [
9
+ "docs/architecture/CONTRACTS_OVERVIEW.md",
10
+ "docs/architecture/REPO_ARTIFACT_LAYOUT.md",
11
+ "docs/architecture/contracts/manifest.json",
12
+ "docs/architecture/contracts/changelog.jsonl",
13
+ "docs/architecture/contracts/fileExchange.contract.md",
14
+ "docs/architecture/contracts/consolidatedExports.contract.md",
15
+ "docs/architecture/contracts/prePushDevLog.contract.md",
16
+ "docs/architecture/contracts/apiDocumentationRegistry.contract.md",
17
+ "backend/src/shared/contracts/prePushDevLog.contract.js",
18
+ "backend/src/shared/contracts/consolidatedExports.contract.js",
19
+ "work-log/dev-logs/schemas/dev-log-agent.v1.schema.json",
20
+ "work-log/dev-logs/human",
21
+ "work-log/dev-logs/agent",
22
+ "file-exchange/README.md",
23
+ "file-exchange/imports",
24
+ "file-exchange/exports",
25
+ "docs/API.md",
26
+ "backend/src/modules/_reference/index.js",
27
+ "backend/src/modules/model-condenser/index.js"
28
+ ];
29
+
30
+ const failures = requiredPaths.filter((rel) => !existsSync(join(repoRoot, rel)));
31
+
32
+ if (failures.length) {
33
+ console.error("Repo artifact lint failed — missing:\n" + failures.map((f) => ` - ${f}`).join("\n"));
34
+ process.exit(1);
35
+ }
36
+
37
+ console.log(`Repo artifact lint OK (${requiredPaths.length} paths)`);
@@ -4,6 +4,7 @@ import { join } from "path";
4
4
  import {
5
5
  getBackendFiles,
6
6
  getFrontendFiles,
7
+ getModuleApiDocContent,
7
8
  toTitleCase
8
9
  } from "./lib/module-scaffold.mjs";
9
10
 
@@ -47,11 +48,17 @@ function writeTree(baseDir, files) {
47
48
  writeTree(backendDir, getBackendFiles(name));
48
49
  writeTree(frontendDir, getFrontendFiles(name, label));
49
50
 
51
+ const docsDir = join(root, "docs", name);
52
+ mkdirSync(docsDir, { recursive: true });
53
+ writeFileSync(join(docsDir, "API.md"), getModuleApiDocContent(name, label), "utf8");
54
+
50
55
  console.log(`Created module: ${name}`);
51
56
  console.log(` backend/src/modules/${name}/`);
52
57
  console.log(` frontend/src/modules/${name}/`);
58
+ console.log(` docs/${name}/API.md`);
53
59
  console.log("");
54
60
  console.log("Next:");
61
+ console.log(` Add rows to docs/API.md (Module index + Endpoint registry) for /api/${name}`);
55
62
  console.log(" npm run lint:architecture");
56
63
  console.log(` npm test`);
57
64
  console.log(` npm run test:evals -- ${name}`);
@@ -0,0 +1,50 @@
1
+ import { access } from "fs/promises";
2
+ import { join, dirname } from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { normalizeExchangeStamp } from "../backend/src/shared/utils/formatExchangeTimestamp.js";
5
+
6
+ const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
7
+
8
+ /**
9
+ * Resolve imports/{stamp}/ — accepts human or legacy compact stamps.
10
+ * @param {string} [stamp]
11
+ * @returns {Promise<string>} resolved stamp folder name under imports/
12
+ */
13
+ export async function resolveImportStamp(stamp) {
14
+ const candidates = [];
15
+ if (stamp) {
16
+ candidates.push(stamp, normalizeExchangeStamp(stamp));
17
+ }
18
+ const importsRoot = join(repoRoot, "file-exchange/imports");
19
+ const { readdir } = await import("fs/promises");
20
+ let dirs = [];
21
+ try {
22
+ dirs = await readdir(importsRoot);
23
+ } catch {
24
+ dirs = [];
25
+ }
26
+ if (!stamp && dirs.length) {
27
+ dirs.sort();
28
+ candidates.push(dirs[dirs.length - 1]);
29
+ }
30
+ const seen = new Set();
31
+ for (const c of candidates) {
32
+ if (!c || seen.has(c)) continue;
33
+ seen.add(c);
34
+ try {
35
+ await access(join(importsRoot, c));
36
+ return c;
37
+ } catch {
38
+ /* try next */
39
+ }
40
+ }
41
+ throw new Error(
42
+ stamp
43
+ ? `No import folder for stamp: ${stamp}`
44
+ : "No file-exchange/imports/{stamp}/ folder found"
45
+ );
46
+ }
47
+
48
+ export function importDirForStamp(stamp) {
49
+ return join(repoRoot, "file-exchange/imports", stamp);
50
+ }
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import { readFile, readdir } from "fs/promises";
3
+ import { join, dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
7
+ const agentDir = join(repoRoot, "work-log/dev-logs/agent");
8
+
9
+ const files = (await readdir(agentDir)).filter((f) => f.endsWith(".json")).sort();
10
+ const latest = files[files.length - 1];
11
+ const agentPath = join(agentDir, latest);
12
+ const humanName = latest.replace("_dev-log-agent_", "_dev-log_").replace(/\.json$/, ".md");
13
+ const humanPath = join(repoRoot, "work-log/dev-logs/human", humanName);
14
+
15
+ const agent = JSON.parse(await readFile(agentPath, "utf8"));
16
+ const human = await readFile(humanPath, "utf8");
17
+
18
+ const required = ["meta", "summary", "apis", "git", "tests", "repositoryTree", "changes", "decisions"];
19
+ const missing = required.filter((k) => !(k in agent));
20
+
21
+ console.log("Latest pair:");
22
+ console.log(" agent:", agentPath.replace(repoRoot + "/", ""));
23
+ console.log(" human:", humanPath.replace(repoRoot + "/", ""));
24
+
25
+ let failed = 0;
26
+ function assert(name, ok) {
27
+ console.log(ok ? " PASS" : " FAIL", name);
28
+ if (!ok) failed += 1;
29
+ }
30
+
31
+ assert("agent JSON has required keys", missing.length === 0);
32
+ assert("apis.http.active > 0", agent.apis.http.active.length > 0);
33
+ assert("repositoryTree.treeText present", agent.repositoryTree.treeText.length > 100);
34
+ assert("treeIgnoreFlag set", agent.repositoryTree.treeIgnoreFlag?.includes("node_modules"));
35
+ assert("tests ran", agent.tests.ran === true);
36
+
37
+ assert("human: TOC", human.includes("## Table of contents"));
38
+ assert("human: Part I", human.includes("Part I — Summary"));
39
+ assert("human: Part II", human.includes("Part II — Detailed"));
40
+ assert("human: mermaid blocks", (human.match(/```mermaid/g) || []).length >= 3);
41
+ assert("human: I.3 API summary", human.includes("I.3 API surface"));
42
+ assert("human: I.5 test audit", human.includes("I.5 Test audit"));
43
+ assert("human: II.10 full tree", human.includes("II.10 Repository tree"));
44
+
45
+ const treeSection = human.split("II.10 Repository tree")[1] || "";
46
+ assert("tree excludes node_modules dir", !treeSection.includes("node_modules/"));
47
+
48
+ console.log("\nTests:", agent.tests.summary);
49
+ console.log(failed === 0 ? "\nAll checks passed." : `\n${failed} check(s) failed.`);
50
+ process.exit(failed === 0 ? 0 : 1);
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Create paired pre-push dev logs: human (markdown) + agent (JSON audit).
4
+ *
5
+ * Usage:
6
+ * npm run dev-log:pre-push -- --slug consolidated-exports
7
+ * npm run dev-log:pre-push -- --slug my-topic --program 005 --no-tests
8
+ * npm run dev-log:pre-push -- --check # verify agent log exists for HEAD
9
+ */
10
+ import { readFile, writeFile, mkdir, readdir } from "fs/promises";
11
+ import { join, dirname } from "path";
12
+ import { fileURLToPath } from "url";
13
+ import { formatWorkLogTimestamp } from "../backend/src/shared/utils/formatExchangeTimestamp.js";
14
+ import { buildRepoTree, TREE_IGNORE_DIRS } from "./lib/repo-tree.mjs";
15
+ import { collectGitSnapshot } from "./lib/git-snapshot.mjs";
16
+ import { runTestSuite } from "./lib/run-tests.mjs";
17
+ import { collectApiInventory, formatApisMarkdown } from "./lib/api-inventory.mjs";
18
+ import { buildHumanDevLog } from "./lib/dev-log-human-format.mjs";
19
+
20
+ const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
21
+ const humanDir = join(repoRoot, "work-log/dev-logs/human");
22
+ const agentDir = join(repoRoot, "work-log/dev-logs/agent");
23
+
24
+ function parseArgs(argv) {
25
+ const out = { slug: "", program: "005", noTests: false, check: false, title: "" };
26
+ for (let i = 0; i < argv.length; i += 1) {
27
+ const a = argv[i];
28
+ if (a === "--slug" && argv[i + 1]) out.slug = argv[++i];
29
+ else if (a === "--program" && argv[i + 1]) out.program = argv[++i];
30
+ else if (a === "--title" && argv[i + 1]) out.title = argv[++i];
31
+ else if (a === "--no-tests") out.noTests = true;
32
+ else if (a === "--check") out.check = true;
33
+ }
34
+ return out;
35
+ }
36
+
37
+ function slugify(s) {
38
+ return String(s)
39
+ .toLowerCase()
40
+ .replace(/[^a-z0-9]+/g, "-")
41
+ .replace(/^-|-$/g, "");
42
+ }
43
+
44
+ function classifyChangedFiles(changedFiles) {
45
+ const byArea = {};
46
+ const added = [];
47
+ const modified = [];
48
+ const deleted = [];
49
+
50
+ for (const { code, path } of changedFiles) {
51
+ const area = path.split("/")[0] || "root";
52
+ if (!byArea[area]) byArea[area] = [];
53
+ byArea[area].push(path);
54
+ if (code.includes("A") || code === "??") added.push(path);
55
+ else if (code.includes("D")) deleted.push(path);
56
+ else modified.push(path);
57
+ }
58
+
59
+ return { byArea, added, modified, deleted };
60
+ }
61
+
62
+ async function findAgentLogForSha(sha) {
63
+ let files;
64
+ try {
65
+ files = await readdir(agentDir);
66
+ } catch {
67
+ return null;
68
+ }
69
+ for (const f of files.filter((x) => x.endsWith(".json")).sort().reverse()) {
70
+ const raw = await readFile(join(agentDir, f), "utf8");
71
+ const doc = JSON.parse(raw);
72
+ if (doc.git?.sha === sha) return join(agentDir, f);
73
+ }
74
+ return null;
75
+ }
76
+
77
+ async function main() {
78
+ const args = parseArgs(process.argv.slice(2));
79
+ const git = await collectGitSnapshot(repoRoot);
80
+
81
+ if (args.check) {
82
+ const found = await findAgentLogForSha(git.sha);
83
+ if (!found) {
84
+ console.error(`No agent dev-log for HEAD (${git.shortSha}). Run: npm run dev-log:pre-push -- --slug <topic>`);
85
+ process.exit(1);
86
+ }
87
+ console.log(`OK: agent dev-log matches HEAD → ${found.replace(repoRoot + "/", "")}`);
88
+ return;
89
+ }
90
+
91
+ if (!args.slug) {
92
+ console.error("Usage: npm run dev-log:pre-push -- --slug <kebab-topic> [--program 005] [--no-tests]");
93
+ process.exit(1);
94
+ }
95
+
96
+ const { date, time, folder: stamp } = formatWorkLogTimestamp();
97
+ const entryId = String(args.program).padStart(3, "0");
98
+ const slug = slugify(args.slug);
99
+ const base = `${entryId}_${date}_${time}`;
100
+ const humanFilename = `${base}_dev-log_${slug}.md`;
101
+ const agentFilename = `${base}_dev-log-agent_${slug}.json`;
102
+ const humanPath = join(humanDir, humanFilename);
103
+ const agentPath = join(agentDir, agentFilename);
104
+ const humanRel = `work-log/dev-logs/human/${humanFilename}`;
105
+ const agentRel = `work-log/dev-logs/agent/${agentFilename}`;
106
+
107
+ console.log("Building repository tree…");
108
+ const tree = await buildRepoTree(repoRoot);
109
+
110
+ console.log("Collecting API inventory…");
111
+ const apis = await collectApiInventory(repoRoot);
112
+ const apisMarkdown = formatApisMarkdown(apis);
113
+ const treeIgnoreList = TREE_IGNORE_DIRS.filter((d) => d !== ".DS_Store").join("`, `");
114
+
115
+ console.log(args.noTests ? "Skipping tests." : "Running npm test…");
116
+ const tests = await runTestSuite(repoRoot, { run: !args.noTests });
117
+ const changes = classifyChangedFiles(git.changedFiles);
118
+
119
+ const title = args.title || slug.replace(/-/g, " ");
120
+ const apisDetailedMarkdown = apisMarkdown;
121
+
122
+ const humanBody = buildHumanDevLog({
123
+ title,
124
+ entryId,
125
+ date,
126
+ time,
127
+ humanFilename,
128
+ agentFilename,
129
+ git,
130
+ tests,
131
+ apis,
132
+ apisDetailedMarkdown,
133
+ tree,
134
+ treeIgnoreList
135
+ });
136
+
137
+ const agentDoc = {
138
+ meta: {
139
+ schemaVersion: "1.0.0",
140
+ entryId,
141
+ slug,
142
+ generatedAt: new Date().toISOString(),
143
+ humanLogPath: humanRel,
144
+ audience: "agent",
145
+ filledBy: "script",
146
+ handoffRefs: []
147
+ },
148
+ summary:
149
+ "FILL: One-paragraph audit summary for the next agent. What shipped, current state, blockers.",
150
+ apis,
151
+ git: {
152
+ branch: git.branch,
153
+ sha: git.sha,
154
+ shortSha: git.shortSha,
155
+ changedFiles: git.changedFiles,
156
+ diffStatAgainstHead: git.diffStatAgainstHead,
157
+ diffCachedStat: git.diffCachedStat,
158
+ recentCommits: git.recentCommits
159
+ },
160
+ tests: {
161
+ ran: tests.ran,
162
+ exitCode: tests.exitCode,
163
+ summary: tests.summary,
164
+ passed: tests.passed,
165
+ failed: tests.failed,
166
+ commands: tests.ran ? ["npm test"] : []
167
+ },
168
+ repositoryTree: {
169
+ capturedAt: new Date().toISOString(),
170
+ excludeDirs: TREE_IGNORE_DIRS,
171
+ treeIgnoreFlag: 'tree -I "node_modules|.git|dist|build"',
172
+ stats: tree.stats,
173
+ treeText: tree.treeText,
174
+ flatPathCount: tree.flatPaths.length
175
+ },
176
+ changes: {
177
+ ...changes,
178
+ narrative: ["FILL: bullet list of intentional behavioral / API changes"]
179
+ },
180
+ decisions: [
181
+ {
182
+ id: "D1",
183
+ decision: "FILL",
184
+ rationale: "FILL",
185
+ alternativesRejected: ["FILL"],
186
+ tradeoff: "FILL"
187
+ }
188
+ ],
189
+ iterations: [
190
+ {
191
+ attempt: 1,
192
+ action: "FILL: what was tried",
193
+ outcome: "FILL: pass/fail/deferred",
194
+ blockedBy: ""
195
+ }
196
+ ],
197
+ tradeoffs: ["FILL"],
198
+ improvements: ["FILL"],
199
+ regressions: ["FILL"],
200
+ risks: ["FILL"],
201
+ followUps: ["FILL"]
202
+ };
203
+
204
+ await mkdir(humanDir, { recursive: true });
205
+ await mkdir(agentDir, { recursive: true });
206
+ await writeFile(humanPath, humanBody);
207
+ await writeFile(agentPath, JSON.stringify(agentDoc, null, 2));
208
+
209
+ console.log("\nPre-push dev logs created:");
210
+ console.log(` Human: ${humanRel}`);
211
+ console.log(` Agent: ${agentRel}`);
212
+ console.log(` Git: ${git.branch} @ ${git.shortSha}`);
213
+ console.log(` Tests: ${tests.summary}`);
214
+ console.log("\nNext: fill FILL sections in the agent JSON and narrative sections in the human MD, then add rows to work-log/INDEX.md");
215
+ }
216
+
217
+ main().catch((err) => {
218
+ console.error(err);
219
+ process.exit(1);
220
+ });
@@ -0,0 +1,3 @@
1
+ # Work log — index
2
+
3
+ See [README.md](./README.md). Add handoffs, study-docs, and dev-log rows as you work.
@@ -0,0 +1,40 @@
1
+ # Work log
2
+
3
+ Planning artifacts for this repo: **what to build** (handoffs) and **how we decided** (study docs).
4
+
5
+ ```text
6
+ work-log/
7
+ README.md ← you are here
8
+ INDEX.md ← full index (handoffs + study-docs + dev-logs)
9
+ handoffs/ ← numbered specs, starter packs (002, 005, …)
10
+ study-docs/ ← study logs, planning notes, blog drafts
11
+ dev-logs/ ← pre-push audit: human/ + agent/ (paired per push)
12
+ ```
13
+
14
+ ## When to use which folder
15
+
16
+ | Folder | Put here |
17
+ |--------|----------|
18
+ | **handoffs/** | Implementation specs, second/third handoffs, starter pack snapshots |
19
+ | **study-docs/** | Conversation study logs, design rationale, portfolio / recruiter logs |
20
+ | **dev-logs/** | What shipped — **paired human MD + agent JSON** before each push |
21
+
22
+ ## Filename convention (all three folders)
23
+
24
+ ```text
25
+ {NNN}_{YYYY-MM-DD}_{HH-MM}_{kind}_{short-slug}.md
26
+ ```
27
+
28
+ | Folder | `kind` value | Example |
29
+ |--------|----------------|---------|
30
+ | handoffs/ | `handoff`, `handoff-v2`, `handoff-original`, … | `005_2026-05-23_10-49_handoff-original_…` |
31
+ | study-docs/ | `study-log` | `006_2026-05-23_11-21_study-log_cursor-planning-phase` |
32
+ | dev-logs/ | `dev-log` (fixed) | `001_2026-05-24_14-30_dev-log_work-log-reorg` |
33
+
34
+ Details: [handoffs/README.md](./handoffs/README.md) · [study-docs/README.md](./study-docs/README.md) · [dev-logs/README.md](./dev-logs/README.md)
35
+
36
+ ## 005 program order
37
+
38
+ 1. Original spec → [handoffs/005_…_handoff-original_…](./handoffs/005_2026-05-23_10-49_handoff-original_parsed-cache-rule-authority.md)
39
+ 2. v3 architecture → [handoffs/005_…_handoff-v3_…](./handoffs/005_2026-05-23_11-20_handoff-v3_filing-structure-architecture.md)
40
+ 3. v2 pipeline → [handoffs/005_…_handoff-v2_…](./handoffs/005_2026-05-23_11-14_handoff-v2_planned-review-in-cursor.md)
@@ -0,0 +1,97 @@
1
+ # Dev logs
2
+
3
+ Session **what we shipped** notes — written **before each push** as a paired human + agent audit.
4
+
5
+ ## Two types (required before push)
6
+
7
+ | Type | Folder | Format | Audience |
8
+ |------|--------|--------|----------|
9
+ | **Human** | `human/` | Markdown (narrative, tables) | You, reviewers, GitHub |
10
+ | **Agent audit** | `agent/` | JSON (`dev-log-agent.v1`) | Cursor / automation — fast structured read |
11
+
12
+ Same timestamp prefix on both files so they stay paired:
13
+
14
+ ```text
15
+ 005_2026-05-23_17-30_dev-log_consolidated-exports.md ← human/
16
+ 005_2026-05-23_17-30_dev-log-agent_consolidated-exports.json ← agent/
17
+ ```
18
+
19
+ ## Pre-push workflow
20
+
21
+ ```bash
22
+ # 1. Generate pair (git + npm test + full repo tree auto-filled)
23
+ npm run dev-log:pre-push -- --slug <kebab-topic> --program 005
24
+
25
+ # 2. Fill FILL sections in agent JSON, then human markdown
26
+
27
+ # 3. Optional: block push if no agent log for HEAD
28
+ npm run dev-log:pre-push -- --check
29
+ ```
30
+
31
+ Cursor: use command **Pre-push dev log** (`.cursor/commands/pre-push-dev-log.md`).
32
+
33
+ ### Human log (two-part markdown)
34
+
35
+ Each `human/*.md` file has:
36
+
37
+ **Part I — Summary** (read first): table of contents, mermaid diagrams (HTTP modules, pipeline versions, pre-push flow), audit tables for APIs, prompt/version contracts, tests, git, condensed repo tree.
38
+
39
+ **Part II — Detailed**: goals, decisions, changes, iterations, full API registry, full git snapshot, full repository tree.
40
+
41
+ Generator: `scripts/lib/dev-log-human-format.mjs`
42
+
43
+ ### Agent log schema
44
+
45
+ `schemas/dev-log-agent.v1.schema.json` — machine-readable:
46
+
47
+ - `meta`, `summary`, `apis` (HTTP active/stub/deprecated, versioned contracts, CLI), `git`, `tests`, `repositoryTree`
48
+ - `changes`, `decisions[]`, `iterations[]`
49
+ - `tradeoffs`, `improvements`, `regressions`, `risks`, `followUps`
50
+
51
+ Agents should read the **JSON** first when resuming work; humans read **markdown**.
52
+
53
+ ## vs other work-log folders
54
+
55
+ | Folder | Audience | Content |
56
+ |--------|----------|---------|
57
+ | **handoffs/** | Implementers | Spec — what to build |
58
+ | **study-docs/** | You + recruiters | Why — planning conversation |
59
+ | **dev-logs/** | You + agents + reviewers | What landed — audit per push |
60
+
61
+ ## vs `docs/DEVLOG_V2.md`
62
+
63
+ | Location | Use |
64
+ |----------|-----|
65
+ | [docs/DEVLOG_V2.md](../../docs/DEVLOG_V2.md) | Long-lived architecture narrative |
66
+ | **dev-logs/** | Incremental per-push audit entries |
67
+
68
+ ## Legacy entries
69
+
70
+ Older single-file logs at `dev-logs/*.md` (repo root of this folder) predate the human/agent split. New entries go only under `human/` and `agent/`.
71
+
72
+ ## Filename convention
73
+
74
+ ```text
75
+ {NNN}_{YYYY-MM-DD}_{HH-MM}_dev-log_{slug}.md → human/
76
+ {NNN}_{YYYY-MM-DD}_{HH-MM}_dev-log-agent_{slug}.json → agent/
77
+ ```
78
+
79
+ | Part | Meaning |
80
+ |------|---------|
81
+ | `NNN` | Program id (`005`) or global sequence (`001`) |
82
+ | `YYYY-MM-DD` | Session date |
83
+ | `HH-MM` | Finish time (24h) |
84
+ | `slug` | Kebab-case topic |
85
+
86
+ ## Git hook (optional)
87
+
88
+ ```bash
89
+ cp scripts/git-hooks/pre-push.sample .git/hooks/pre-push
90
+ chmod +x .git/hooks/pre-push
91
+ ```
92
+
93
+ Reminds you to run `dev-log:pre-push`; use `--check` to enforce agent log for current `HEAD`.
94
+
95
+ ## GitHub
96
+
97
+ Track in git. No secrets, credentials, or real filing text. Update [../INDEX.md](../INDEX.md) after each pair.