@latysh-development/monolith 0.0.0-stage → 0.1.0-alpha.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 (291) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -2
  3. package/dist/brief/cli.d.ts +30 -0
  4. package/dist/brief/cli.d.ts.map +1 -0
  5. package/dist/brief/cli.js +85 -0
  6. package/dist/brief/collect.d.ts +17 -0
  7. package/dist/brief/collect.d.ts.map +1 -0
  8. package/dist/brief/collect.js +302 -0
  9. package/dist/brief/config.d.ts +40 -0
  10. package/dist/brief/config.d.ts.map +1 -0
  11. package/dist/brief/config.js +85 -0
  12. package/dist/brief/constants.d.ts +10 -0
  13. package/dist/brief/constants.d.ts.map +1 -0
  14. package/dist/brief/constants.js +9 -0
  15. package/dist/brief/docs.d.ts +53 -0
  16. package/dist/brief/docs.d.ts.map +1 -0
  17. package/dist/brief/docs.js +229 -0
  18. package/dist/brief/freshness.d.ts +104 -0
  19. package/dist/brief/freshness.d.ts.map +1 -0
  20. package/dist/brief/freshness.js +263 -0
  21. package/dist/brief/git-signals.d.ts +17 -0
  22. package/dist/brief/git-signals.d.ts.map +1 -0
  23. package/dist/brief/git-signals.js +101 -0
  24. package/dist/brief/index.d.ts +11 -0
  25. package/dist/brief/index.d.ts.map +1 -0
  26. package/dist/brief/index.js +5 -0
  27. package/dist/brief/reconcile.d.ts +75 -0
  28. package/dist/brief/reconcile.d.ts.map +1 -0
  29. package/dist/brief/reconcile.js +259 -0
  30. package/dist/brief/render.d.ts +60 -0
  31. package/dist/brief/render.d.ts.map +1 -0
  32. package/dist/brief/render.js +407 -0
  33. package/dist/brief/sanitize.d.ts +38 -0
  34. package/dist/brief/sanitize.d.ts.map +1 -0
  35. package/dist/brief/sanitize.js +48 -0
  36. package/dist/brief/selectors.d.ts +36 -0
  37. package/dist/brief/selectors.d.ts.map +1 -0
  38. package/dist/brief/selectors.js +75 -0
  39. package/dist/brief/story.d.ts +18 -0
  40. package/dist/brief/story.d.ts.map +1 -0
  41. package/dist/brief/story.js +59 -0
  42. package/dist/brief/types.d.ts +110 -0
  43. package/dist/brief/types.d.ts.map +1 -0
  44. package/dist/brief/types.js +11 -0
  45. package/dist/cli/index.d.ts +3 -0
  46. package/dist/cli/index.d.ts.map +1 -0
  47. package/dist/cli/index.js +166 -0
  48. package/dist/cli/init-args.d.ts +16 -0
  49. package/dist/cli/init-args.d.ts.map +1 -0
  50. package/dist/cli/init-args.js +24 -0
  51. package/dist/cli/ui-args.d.ts +23 -0
  52. package/dist/cli/ui-args.d.ts.map +1 -0
  53. package/dist/cli/ui-args.js +66 -0
  54. package/dist/cli/ui.d.ts +40 -0
  55. package/dist/cli/ui.d.ts.map +1 -0
  56. package/dist/cli/ui.js +180 -0
  57. package/dist/coordinatorium/checkpoint.d.ts +44 -0
  58. package/dist/coordinatorium/checkpoint.d.ts.map +1 -0
  59. package/dist/coordinatorium/checkpoint.js +124 -0
  60. package/dist/coordinatorium/cli.d.ts +39 -0
  61. package/dist/coordinatorium/cli.d.ts.map +1 -0
  62. package/dist/coordinatorium/cli.js +203 -0
  63. package/dist/coordinatorium/errors.d.ts +112 -0
  64. package/dist/coordinatorium/errors.d.ts.map +1 -0
  65. package/dist/coordinatorium/errors.js +145 -0
  66. package/dist/coordinatorium/index.d.ts +11 -0
  67. package/dist/coordinatorium/index.d.ts.map +1 -0
  68. package/dist/coordinatorium/index.js +5 -0
  69. package/dist/coordinatorium/lock.d.ts +187 -0
  70. package/dist/coordinatorium/lock.d.ts.map +1 -0
  71. package/dist/coordinatorium/lock.js +659 -0
  72. package/dist/coordinatorium/mission.d.ts +32 -0
  73. package/dist/coordinatorium/mission.d.ts.map +1 -0
  74. package/dist/coordinatorium/mission.js +87 -0
  75. package/dist/coordinatorium/state-keeper.d.ts +115 -0
  76. package/dist/coordinatorium/state-keeper.d.ts.map +1 -0
  77. package/dist/coordinatorium/state-keeper.js +396 -0
  78. package/dist/coordinatorium/types.d.ts +69 -0
  79. package/dist/coordinatorium/types.d.ts.map +1 -0
  80. package/dist/coordinatorium/types.js +27 -0
  81. package/dist/durable/assess.d.ts +105 -0
  82. package/dist/durable/assess.d.ts.map +1 -0
  83. package/dist/durable/assess.js +239 -0
  84. package/dist/durable/canonical.d.ts +25 -0
  85. package/dist/durable/canonical.d.ts.map +1 -0
  86. package/dist/durable/canonical.js +78 -0
  87. package/dist/durable/cli.d.ts +37 -0
  88. package/dist/durable/cli.d.ts.map +1 -0
  89. package/dist/durable/cli.js +141 -0
  90. package/dist/durable/errors.d.ts +188 -0
  91. package/dist/durable/errors.d.ts.map +1 -0
  92. package/dist/durable/errors.js +317 -0
  93. package/dist/durable/fs-store.d.ts +233 -0
  94. package/dist/durable/fs-store.d.ts.map +1 -0
  95. package/dist/durable/fs-store.js +1393 -0
  96. package/dist/durable/index.d.ts +20 -0
  97. package/dist/durable/index.d.ts.map +1 -0
  98. package/dist/durable/index.js +13 -0
  99. package/dist/durable/log.d.ts +33 -0
  100. package/dist/durable/log.d.ts.map +1 -0
  101. package/dist/durable/log.js +157 -0
  102. package/dist/durable/memory-store.d.ts +51 -0
  103. package/dist/durable/memory-store.d.ts.map +1 -0
  104. package/dist/durable/memory-store.js +302 -0
  105. package/dist/durable/projection.d.ts +35 -0
  106. package/dist/durable/projection.d.ts.map +1 -0
  107. package/dist/durable/projection.js +328 -0
  108. package/dist/durable/prune-check.d.ts +17 -0
  109. package/dist/durable/prune-check.d.ts.map +1 -0
  110. package/dist/durable/prune-check.js +45 -0
  111. package/dist/durable/register.d.ts +100 -0
  112. package/dist/durable/register.d.ts.map +1 -0
  113. package/dist/durable/register.js +532 -0
  114. package/dist/durable/retention.d.ts +49 -0
  115. package/dist/durable/retention.d.ts.map +1 -0
  116. package/dist/durable/retention.js +59 -0
  117. package/dist/durable/runtime.d.ts +497 -0
  118. package/dist/durable/runtime.d.ts.map +1 -0
  119. package/dist/durable/runtime.js +1271 -0
  120. package/dist/durable/seams.d.ts +40 -0
  121. package/dist/durable/seams.d.ts.map +1 -0
  122. package/dist/durable/seams.js +21 -0
  123. package/dist/durable/testing.d.ts +11 -0
  124. package/dist/durable/testing.d.ts.map +1 -0
  125. package/dist/durable/testing.js +25 -0
  126. package/dist/durable/types.d.ts +442 -0
  127. package/dist/durable/types.d.ts.map +1 -0
  128. package/dist/durable/types.js +75 -0
  129. package/dist/graph/builder.d.ts +11 -0
  130. package/dist/graph/builder.d.ts.map +1 -0
  131. package/dist/graph/builder.js +153 -0
  132. package/dist/graph/identity.d.ts +37 -0
  133. package/dist/graph/identity.d.ts.map +1 -0
  134. package/dist/graph/identity.js +46 -0
  135. package/dist/graph/index.d.ts +7 -0
  136. package/dist/graph/index.d.ts.map +1 -0
  137. package/dist/graph/index.js +3 -0
  138. package/dist/graph/resolver.d.ts +41 -0
  139. package/dist/graph/resolver.d.ts.map +1 -0
  140. package/dist/graph/resolver.js +36 -0
  141. package/dist/graph/types.d.ts +42 -0
  142. package/dist/graph/types.d.ts.map +1 -0
  143. package/dist/graph/types.js +1 -0
  144. package/dist/http/index.d.ts +5 -0
  145. package/dist/http/index.d.ts.map +1 -0
  146. package/dist/http/index.js +2 -0
  147. package/dist/http/server.d.ts +76 -0
  148. package/dist/http/server.d.ts.map +1 -0
  149. package/dist/http/server.js +195 -0
  150. package/dist/http/ui-assets.d.ts +22 -0
  151. package/dist/http/ui-assets.d.ts.map +1 -0
  152. package/dist/http/ui-assets.js +100 -0
  153. package/dist/index.d.ts +31 -0
  154. package/dist/index.d.ts.map +1 -0
  155. package/dist/index.js +26 -0
  156. package/dist/init/index.d.ts +12 -0
  157. package/dist/init/index.d.ts.map +1 -0
  158. package/dist/init/index.js +90 -0
  159. package/dist/init/prompts.d.ts +3 -0
  160. package/dist/init/prompts.d.ts.map +1 -0
  161. package/dist/init/prompts.js +47 -0
  162. package/dist/init/scaffold.d.ts +25 -0
  163. package/dist/init/scaffold.d.ts.map +1 -0
  164. package/dist/init/scaffold.js +204 -0
  165. package/dist/init/types.d.ts +14 -0
  166. package/dist/init/types.d.ts.map +1 -0
  167. package/dist/init/types.js +1 -0
  168. package/dist/readmodel/dashboard.d.ts +40 -0
  169. package/dist/readmodel/dashboard.d.ts.map +1 -0
  170. package/dist/readmodel/dashboard.js +236 -0
  171. package/dist/readmodel/index.d.ts +3 -0
  172. package/dist/readmodel/index.d.ts.map +1 -0
  173. package/dist/readmodel/index.js +1 -0
  174. package/dist/readmodel/types.d.ts +104 -0
  175. package/dist/readmodel/types.d.ts.map +1 -0
  176. package/dist/readmodel/types.js +15 -0
  177. package/dist/root/index.d.ts +179 -0
  178. package/dist/root/index.d.ts.map +1 -0
  179. package/dist/root/index.js +418 -0
  180. package/dist/root/safe-write.d.ts +268 -0
  181. package/dist/root/safe-write.d.ts.map +1 -0
  182. package/dist/root/safe-write.js +966 -0
  183. package/dist/root/write-artifacts.d.ts +20 -0
  184. package/dist/root/write-artifacts.d.ts.map +1 -0
  185. package/dist/root/write-artifacts.js +42 -0
  186. package/dist/scriptorium/compile.d.ts +4 -0
  187. package/dist/scriptorium/compile.d.ts.map +1 -0
  188. package/dist/scriptorium/compile.js +185 -0
  189. package/dist/scriptorium/frontmatter.d.ts +13 -0
  190. package/dist/scriptorium/frontmatter.d.ts.map +1 -0
  191. package/dist/scriptorium/frontmatter.js +90 -0
  192. package/dist/scriptorium/index.d.ts +3 -0
  193. package/dist/scriptorium/index.d.ts.map +1 -0
  194. package/dist/scriptorium/index.js +1 -0
  195. package/dist/scriptorium/markdown.d.ts +31 -0
  196. package/dist/scriptorium/markdown.d.ts.map +1 -0
  197. package/dist/scriptorium/markdown.js +88 -0
  198. package/dist/scriptorium/types.d.ts +78 -0
  199. package/dist/scriptorium/types.d.ts.map +1 -0
  200. package/dist/scriptorium/types.js +1 -0
  201. package/dist/types/enforcement.d.ts +11 -0
  202. package/dist/types/enforcement.d.ts.map +1 -0
  203. package/dist/types/enforcement.js +1 -0
  204. package/dist/types/integrity.d.ts +33 -0
  205. package/dist/types/integrity.d.ts.map +1 -0
  206. package/dist/types/integrity.js +1 -0
  207. package/dist/types/project-definition.d.ts +30 -0
  208. package/dist/types/project-definition.d.ts.map +1 -0
  209. package/dist/types/project-definition.js +1 -0
  210. package/dist/ui/assets/cosmic-glass-bg-Dwx47zPu.webp +0 -0
  211. package/dist/ui/assets/index-C4NhMaq7.js +28 -0
  212. package/dist/ui/assets/index-DMZ7ZvZY.css +1 -0
  213. package/dist/ui/index.html +13 -0
  214. package/dist/validate/index.d.ts +6 -0
  215. package/dist/validate/index.d.ts.map +1 -0
  216. package/dist/validate/index.js +214 -0
  217. package/dist/version.d.ts +2 -0
  218. package/dist/version.d.ts.map +1 -0
  219. package/dist/version.js +1 -0
  220. package/package.json +67 -4
  221. package/templates/.gitkeep +0 -0
  222. package/templates/default-instance/instruction/.cursorrules +53 -0
  223. package/templates/default-instance/instruction/.windsurfrules +53 -0
  224. package/templates/default-instance/instruction/CLAUDE.md +53 -0
  225. package/templates/default-instance/monolith/context/boot/HANDOFF.md +49 -0
  226. package/templates/default-instance/monolith/context/boot/WORK_SESSION.md +30 -0
  227. package/templates/default-instance/monolith/context/execution/current-state.md +30 -0
  228. package/templates/default-instance/monolith/context/maps/dependency-map.md +38 -0
  229. package/templates/default-instance/monolith/context/maps/project-map.md +85 -0
  230. package/templates/default-instance/monolith/context/maps/roadmap-index.md +41 -0
  231. package/templates/default-instance/monolith/context/maps/routing-map.md +147 -0
  232. package/templates/default-instance/monolith/context/maps/semantic-map.md +73 -0
  233. package/templates/default-instance/monolith/context/models/model-registry.md +37 -0
  234. package/templates/default-instance/monolith/context/models/privacy-classification.md +35 -0
  235. package/templates/default-instance/monolith/context/templates/plan-template.md +56 -0
  236. package/templates/default-instance/monolith/governance/README.md +29 -0
  237. package/templates/default-instance/monolith/governance/RULE_SCHEMA.md +58 -0
  238. package/templates/default-instance/monolith/learning/INDEX.md +48 -0
  239. package/templates/default-instance/monolith/learning/injection-map.md +49 -0
  240. package/templates/default-instance/monolith/learning/taxonomy.md +30 -0
  241. package/templates/default-instance/monolith/memory/errors/README.md +26 -0
  242. package/templates/default-instance/monolith/memory/errors/SCHEMA.md +47 -0
  243. package/templates/default-instance/monolith/memory/errors/TAXONOMY.md +27 -0
  244. package/templates/default-instance/monolith/memory/long-term/architecture-principles.md +31 -0
  245. package/templates/default-instance/monolith/memory/long-term/decisions.md +36 -0
  246. package/templates/default-instance/monolith/memory/long-term/execution-history.md +20 -0
  247. package/templates/default-instance/monolith/memory/long-term/tech-debt.md +25 -0
  248. package/templates/default-instance/monolith/memory/working/active.md +41 -0
  249. package/templates/default-instance/monolith/memory/working/governance-result.md +0 -0
  250. package/templates/default-instance/monolith/memory/working/integrity-result.md +0 -0
  251. package/templates/default-instance/monolith/memory/working/story-index.md +28 -0
  252. package/templates/default-instance/monolith/protocols/boot/boot-sequence.md +59 -0
  253. package/templates/default-instance/monolith/protocols/cognition/cognitive-modes.md +90 -0
  254. package/templates/default-instance/monolith/protocols/cognition/dry-run.md +56 -0
  255. package/templates/default-instance/monolith/protocols/cognition/execution-trace-format.md +52 -0
  256. package/templates/default-instance/monolith/protocols/cognition/semantic-planning.md +51 -0
  257. package/templates/default-instance/monolith/protocols/execution/hybrid-execution.md +37 -0
  258. package/templates/default-instance/monolith/protocols/execution/local-llm-governance.md +42 -0
  259. package/templates/default-instance/monolith/protocols/governance/command-policy.md +51 -0
  260. package/templates/default-instance/monolith/protocols/governance/core-invariants.md +40 -0
  261. package/templates/default-instance/monolith/protocols/governance/memory-governance.md +54 -0
  262. package/templates/default-instance/monolith/protocols/governance/metadata-schema.md +62 -0
  263. package/templates/default-instance/monolith/protocols/governance/skills.md +53 -0
  264. package/templates/default-instance/monolith/protocols/governance/work-session.md +46 -0
  265. package/templates/default-instance/monolith/skills/INDEX.md +28 -0
  266. package/templates/default-instance/monolith/skills/current-status/SKILL.md +47 -0
  267. package/templates/default-instance/monolith/skills/finalize-story/SKILL.md +86 -0
  268. package/templates/default-instance/monolith/skills/global-status/SKILL.md +56 -0
  269. package/templates/default-instance/monolith/skills/self-improvement/SKILL.md +77 -0
  270. package/templates/default-instance/monolith/state.json +11 -0
  271. package/templates/default-instance/monolith/tests/INDEX.md +57 -0
  272. package/templates/default-instance/monolith/tests/scenarios/architecture-review.md +32 -0
  273. package/templates/default-instance/monolith/tests/scenarios/debug-failure.md +32 -0
  274. package/templates/default-instance/monolith/tests/scenarios/dry-run-activation.md +33 -0
  275. package/templates/default-instance/monolith/tests/scenarios/execution-trace-verification.md +31 -0
  276. package/templates/default-instance/monolith/tests/scenarios/finalize-story.md +35 -0
  277. package/templates/default-instance/monolith/tests/scenarios/implement-feature.md +36 -0
  278. package/templates/default-instance/monolith/tests/scenarios/local-llm-delegation.md +31 -0
  279. package/templates/default-instance/monolith/tests/scenarios/planning-request.md +32 -0
  280. package/templates/default-instance/monolith/tests/scenarios/sensitive-context-task.md +30 -0
  281. package/templates/default-instance/monolith/tests/scenarios/validator-verification.md +33 -0
  282. package/templates/default-instance/monolith/tests/validation/expected-behavior.md +70 -0
  283. package/templates/default-instance/monolith/tests/validators/INDEX.md +17 -0
  284. package/templates/default-instance/monolith/tests/validators/delegation-semantics.md +22 -0
  285. package/templates/default-instance/monolith/tests/validators/mode-consistency.md +25 -0
  286. package/templates/default-instance/monolith/tests/validators/privacy-routing.md +23 -0
  287. package/templates/default-instance/monolith/tests/validators/protocol-references.md +27 -0
  288. package/templates/default-instance/monolith/tests/validators/routing-integrity.md +30 -0
  289. package/templates/default-instance/projectroot/AGENTS.md +107 -0
  290. package/templates/default-instance/projectroot/monolith.config.json +32 -0
  291. package/templates/default-instance/projectroot/project.definition.mjs.template +41 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dmytro Latysh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,133 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <img src="./assets/branding/monolith-banner.png" alt="MONOLITH — Cognitive Infrastructure for AI" width="100%">
3
+ </p>
2
4
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
5
+ <h1 align="center">MONOLITH</h1>
6
+
7
+ <p align="center">
8
+ <strong>Cognitive Infrastructure for AI</strong><br>
9
+ Observable · Structured · Governed
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="./LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"></a>
14
+ <a href="./package.json"><img src="https://img.shields.io/badge/node-%3E%3D20-339933.svg" alt="Node.js >= 20"></a>
15
+ <img src="https://img.shields.io/badge/status-alpha-orange.svg" alt="Status: alpha">
16
+ <a href="https://hub.xerj.org/contributors/Demion-L"><img src="https://hub.xerj.org/contributors/Demion-L/badge.svg" alt="XERJ Corpus Hub contributor"></a>
17
+ </p>
18
+
19
+ ---
20
+
21
+ MONOLITH is an open-source toolkit for observable project state, structured
22
+ knowledge, and explicit execution governance. It gives a
23
+ human and an AI assistant a shared, inspectable record of a project —
24
+ structured knowledge, explicit read and write boundaries, and traceable
25
+ decisions — instead of relying on an assistant's unaided memory.
26
+
27
+ > **[MANIFESTO.md](./MANIFESTO.md)** — why this project exists and the engineering principles that govern it.
28
+ > **[AGENTS.md](./AGENTS.md)** — current scope, source structure, and the authoritative entry point for AI assistants working in this repository.
29
+
30
+ ## Why MONOLITH?
31
+
32
+ AI assistants lose context between sessions, and what a project "knows" ends up
33
+ scattered across chats, notes and habits. MONOLITH keeps that knowledge in the
34
+ project itself, in a form both a person and an assistant can read, check and
35
+ trust — using mechanisms that are deterministic and testable.
36
+
37
+ ## What MONOLITH Does
38
+
39
+ - **Observable** — `monolith brief` summarizes project state in the terminal, including whether it looks stale; Mission Control shows it in a local, read-only web interface.
40
+ - **Structured** — structure is extracted from a project's own markdown (frontmatter, headings, explicit links) into an explicit relationship graph. No embeddings, no LLM, no fuzzy matching.
41
+ - **Governed** — protocols, skills and cognitive modes describe how an assistant reads and updates project memory; missions and checkpoints record progress. This is a recorded discipline, not a runtime gate.
42
+ - **Reproducible** — same input, same output. State lives in a schema-versioned file written with a lock and atomic replace; Durable State adds a hash-chained event log.
43
+ - **Bounded** — managed files are read and written through strict primitives that refuse symlinks; `monolith validate` never executes project code; Mission Control has no write endpoint.
44
+
45
+ ## Quick Start
46
+
47
+ Requires Node.js >= 20. MONOLITH is an alpha package that is **not published
48
+ to npm**. Build it from a clone:
49
+
50
+ ```bash
51
+ git clone https://github.com/Demion-L/the_monolith.git
52
+ cd the_monolith
53
+ pnpm install
54
+ pnpm build
55
+ npm link # exposes the `monolith` command (or run: node dist/cli/index.js)
56
+ ```
57
+
58
+ Once the first alpha is published, `npm install -g @latysh-development/monolith@alpha`
59
+ becomes the primary install path ([release notes](./docs/release/alpha-installation.md)).
60
+
61
+ Then, in a project directory:
62
+
63
+ ```bash
64
+ monolith init # scaffold a MONOLITH instance (refuses to overwrite existing files)
65
+ monolith validate # static integrity check of the instance
66
+ monolith brief # bounded, read-only summary of project state
67
+ monolith ui # Mission Control at http://127.0.0.1:4173 ; Ctrl+C to stop
68
+ ```
69
+
70
+ `monolith ui` must be run inside a directory that contains a MONOLITH
71
+ instance, and it does not open a browser — open the printed address yourself.
72
+ All commands: [CLI reference](./docs/reference/cli.md).
73
+
74
+ ## Architecture
75
+
76
+ ```text
77
+ Markdown artifact → Scriptorium (extraction) → ERG (relationship graph) → governed project memory
78
+
79
+ MONOLITH Kernel (src/) ──► Project Instance (.monolith/, monolith.config.json)
80
+ │ safe, bounded reads
81
+ ▼
82
+ Read model ──► loopback HTTP API ──► Mission Control UI
83
+ ```
84
+
85
+ > **Kernel knows HOW. Instance knows WHAT.** The Kernel holds generic
86
+ > mechanics and has no hardcoded knowledge of any project; an Instance — the
87
+ > `.monolith/` directory of a project using MONOLITH, this repository
88
+ > included — supplies the project's own knowledge. Details:
89
+ > [`docs/architecture/`](./docs/architecture/README.md).
90
+
91
+ ## Ecosystem
92
+
93
+ - **[Latysh Development](https://github.com/Demion-L)** — developer of MONOLITH.
94
+ - **[Webway](https://github.com/Demion-L/webway)** — a separate, independently developed project on semantic and associative knowledge linking. It was extracted from MONOLITH and has no runtime coupling to it.
95
+ - **SphynX** — a related application. MONOLITH does not depend on it, and this repository does not document it as a current consumer of MONOLITH.
96
+
97
+ ## Community & Recognition
98
+
99
+ MONOLITH is listed as a contributor project on
100
+ [XERJ Corpus Hub](https://hub.xerj.org/contributors/Demion-L). At the time of
101
+ writing, the contributor page exists, a MONOLITH commit is selected, the corpus
102
+ status is `candidate` and the manifest is `draft`. This is a contribution
103
+ listing — not an official partnership, endorsement or certification.
104
+
105
+ ## Documentation
106
+
107
+ - [CLI reference](./docs/reference/cli.md) — every command, `init`, `validate`, `brief`, `mission`, `durable`, `ui`
108
+ - [Mission Control](./docs/reference/mission-control.md) — sections, LIVE/DEMO, skins, limitations
109
+ - [Security & Limitations](./docs/reference/security-and-limitations.md) — threat model, platform and filesystem caveats
110
+ - [Programmatic API](./docs/reference/write-scope-api.md) · [Development](./docs/reference/development.md)
111
+ - [Architecture](./docs/architecture/README.md) · [ADRs](./docs/adr/README.md) · [Knowledge-base index](./docs/README.md)
112
+ - [`AGENTS.md`](./AGENTS.md) — scope and entry point for AI assistants · [`MANIFESTO.md`](./MANIFESTO.md) — principles
113
+
114
+ ## Project Status
115
+
116
+ **Alpha** (`0.1.0-alpha.0`). The API and CLI are experimental and may change
117
+ without notice. Not published to npm. Durable State and the Coordinatorium
118
+ (`monolith durable`, `monolith mission`) are experimental and are not part of
119
+ the Quick Start.
120
+
121
+ **Security:** Mission Control is read-only, listens on `127.0.0.1` only and has
122
+ no authentication. Concurrent adversarial mutation of a project is unsupported;
123
+ Windows is unverified. Durable State is merged but its security acceptance is
124
+ still open. See [Security & Limitations](./docs/reference/security-and-limitations.md).
125
+
126
+ **Not yet implemented:** a write-enabled UI, authentication for Mission
127
+ Control, `monolith context <topic>`, `monolith status`, `monolith dry-run`, the
128
+ enforcement gate engine and integrity validators (type declarations only), and
129
+ protocol drift detection. `AGENTS.md` is the authoritative status record.
130
+
131
+ ## License
132
+
133
+ [MIT](./LICENSE) © 2026 Dmytro Latysh
@@ -0,0 +1,30 @@
1
+ /**
2
+ * `monolith brief` CLI command handler.
3
+ *
4
+ * stdout carries only the brief's own content; CLI diagnostics and the
5
+ * INCOMPLETE warning go to stderr (section 12). Exit codes (DEC-CONTEXT-002):
6
+ * 0 — brief formed (COMPLETE or INCOMPLETE)
7
+ * 1 — CLI usage error (thrown as a plain Error, caught by src/cli/index.ts's main())
8
+ * 2 — --strict and the result is INCOMPLETE
9
+ *
10
+ * `--reconcile` (SELF-AWARENESS-002) prints the State Reconciliation Report
11
+ * instead of the brief: what a Freshness verdict rests on and what a person
12
+ * should check. Same read-only contract, same exit codes (a report is formed
13
+ * -> 0, whatever the Freshness; usage error -> 1). It does not combine with
14
+ * `--strict`, whose meaning (Completeness) belongs to the brief.
15
+ */
16
+ export interface BriefArgsOk {
17
+ ok: true;
18
+ maxChars: number;
19
+ strict: boolean;
20
+ /** Present (true) only when `--reconcile` was given, so existing result shapes are unchanged. */
21
+ reconcile?: true;
22
+ }
23
+ export interface BriefArgsErr {
24
+ ok: false;
25
+ message: string;
26
+ }
27
+ export type BriefArgsResult = BriefArgsOk | BriefArgsErr;
28
+ export declare function parseBriefArgs(args: string[]): BriefArgsResult;
29
+ export declare function runBrief(args: string[], targetDir?: string): Promise<void>;
30
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/brief/cli.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;GAcG;AAMH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,IAAI,CAAC;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,iGAAiG;IACjG,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,YAAY,CAAC;AAIzD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CA6C9D;AAED,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4BvF"}
@@ -0,0 +1,85 @@
1
+ /* eslint-disable no-console */
2
+ /**
3
+ * `monolith brief` CLI command handler.
4
+ *
5
+ * stdout carries only the brief's own content; CLI diagnostics and the
6
+ * INCOMPLETE warning go to stderr (section 12). Exit codes (DEC-CONTEXT-002):
7
+ * 0 — brief formed (COMPLETE or INCOMPLETE)
8
+ * 1 — CLI usage error (thrown as a plain Error, caught by src/cli/index.ts's main())
9
+ * 2 — --strict and the result is INCOMPLETE
10
+ *
11
+ * `--reconcile` (SELF-AWARENESS-002) prints the State Reconciliation Report
12
+ * instead of the brief: what a Freshness verdict rests on and what a person
13
+ * should check. Same read-only contract, same exit codes (a report is formed
14
+ * -> 0, whatever the Freshness; usage error -> 1). It does not combine with
15
+ * `--strict`, whose meaning (Completeness) belongs to the brief.
16
+ */
17
+ import { collect } from './collect.js';
18
+ import { render, DEFAULT_MAX_CHARS } from './render.js';
19
+ import { reconcile, renderReconciliation } from './reconcile.js';
20
+ const USAGE = 'Usage: monolith brief [--max-chars N] [--strict]\n monolith brief --reconcile [--max-chars N]';
21
+ export function parseBriefArgs(args) {
22
+ let maxChars = DEFAULT_MAX_CHARS;
23
+ let strict = false;
24
+ let reconcileMode = false;
25
+ const unknown = [];
26
+ let i = 0;
27
+ while (i < args.length) {
28
+ const arg = args[i];
29
+ if (arg === '--strict') {
30
+ strict = true;
31
+ i++;
32
+ continue;
33
+ }
34
+ if (arg === '--reconcile') {
35
+ reconcileMode = true;
36
+ i++;
37
+ continue;
38
+ }
39
+ if (arg === '--max-chars') {
40
+ const value = args[i + 1];
41
+ if (value === undefined) {
42
+ return { ok: false, message: `--max-chars requires a value.\n\n${USAGE}` };
43
+ }
44
+ const n = Number(value);
45
+ if (!Number.isInteger(n) || n <= 0) {
46
+ return { ok: false, message: `--max-chars must be a positive integer, got "${value}".\n\n${USAGE}` };
47
+ }
48
+ maxChars = n;
49
+ i += 2;
50
+ continue;
51
+ }
52
+ unknown.push(arg);
53
+ i++;
54
+ }
55
+ if (unknown.length > 0) {
56
+ return { ok: false, message: `Unknown argument(s) for 'monolith brief': ${unknown.join(' ')}\n\n${USAGE}` };
57
+ }
58
+ if (reconcileMode && strict) {
59
+ return { ok: false, message: `--strict cannot be combined with --reconcile.\n\n${USAGE}` };
60
+ }
61
+ return reconcileMode ? { ok: true, maxChars, strict, reconcile: true } : { ok: true, maxChars, strict };
62
+ }
63
+ export async function runBrief(args, targetDir = process.cwd()) {
64
+ const parsed = parseBriefArgs(args);
65
+ if (!parsed.ok) {
66
+ throw new Error(parsed.message);
67
+ }
68
+ const model = await collect(targetDir);
69
+ if (parsed.reconcile) {
70
+ if (!model.freshness || !model.freshnessEvidence) {
71
+ throw new Error('monolith brief --reconcile: no freshness evidence could be collected.');
72
+ }
73
+ process.stdout.write(renderReconciliation(reconcile(model.freshnessEvidence, model.freshness), parsed.maxChars).text);
74
+ process.exit(0);
75
+ }
76
+ const { text, completeness } = render(model, parsed.maxChars);
77
+ process.stdout.write(text);
78
+ if (completeness === 'INCOMPLETE') {
79
+ console.error('monolith brief: result is INCOMPLETE — see the Diagnostics section above.');
80
+ }
81
+ if (parsed.strict && completeness === 'INCOMPLETE') {
82
+ process.exit(2);
83
+ }
84
+ process.exit(0);
85
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `collect()` — the read-only Collection stage of the brief pipeline.
3
+ *
4
+ * readMonolithRoot → collect() → BriefModel → render(model, budget) → stdout
5
+ *
6
+ * Strictly read-only: no directories, lock files, or state.json are ever
7
+ * created; `mutateState()` is never imported here. state.json is read at
8
+ * most once (one `readFile` call) per invocation — every section that needs
9
+ * Coordinatorium data derives it from that single snapshot via the pure
10
+ * selectors in selectors.ts, never by calling `listMissions()`/
11
+ * `listCheckpoints()` (which each independently re-read the file).
12
+ */
13
+ import type { BriefModel } from './types.js';
14
+ /** SEC-001B.1: `active.md` is a short hand-written context file (~3 KB today). */
15
+ export declare const MAX_ACTIVE_CONTEXT_BYTES: number;
16
+ export declare function collect(targetDir: string): Promise<BriefModel>;
17
+ //# sourceMappingURL=collect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect.d.ts","sourceRoot":"","sources":["../../src/brief/collect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAwBH,OAAO,KAAK,EACV,UAAU,EAMX,MAAM,YAAY,CAAC;AAepB,kFAAkF;AAClF,eAAO,MAAM,wBAAwB,QAAa,CAAC;AAsRnD,wBAAsB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAkEpE"}
@@ -0,0 +1,302 @@
1
+ /**
2
+ * `collect()` — the read-only Collection stage of the brief pipeline.
3
+ *
4
+ * readMonolithRoot → collect() → BriefModel → render(model, budget) → stdout
5
+ *
6
+ * Strictly read-only: no directories, lock files, or state.json are ever
7
+ * created; `mutateState()` is never imported here. state.json is read at
8
+ * most once (one `readFile` call) per invocation — every section that needs
9
+ * Coordinatorium data derives it from that single snapshot via the pure
10
+ * selectors in selectors.ts, never by calling `listMissions()`/
11
+ * `listCheckpoints()` (which each independently re-read the file).
12
+ */
13
+ import { existsSync } from 'fs';
14
+ import { join, relative, posix } from 'path';
15
+ import { readBriefConfig } from './config.js';
16
+ import { resolveMonolithRootValue, checkContainedFile, readBoundedFileStable, isReadSecurityViolation, findWriteArtifacts, describeWriteArtifacts, } from '../root/index.js';
17
+ import { discoverDocs } from './docs.js';
18
+ import { extractCurrentStory } from './story.js';
19
+ import { selectActiveMissions, selectRecentFinishedMissions, selectLatestCheckpoint, hasCheckpointOrderMismatch, } from './selectors.js';
20
+ import { stateFilePath, validateState, createInitialState, MAX_STATE_BYTES } from '../coordinatorium/index.js';
21
+ import { ACTIVE_MD_REL, PROTOCOLS_DIR, ROADMAP_INDEX_REL, STORY_INDEX_REL, HANDOFF_REL } from './constants.js';
22
+ import { evaluateFreshness, parseActiveStoryClaim, parseRoadmapActiveStory, parseRoadmapStatuses, parseStoryIndexStatuses, } from './freshness.js';
23
+ import { readGitSignals } from './git-signals.js';
24
+ const RECENT_FINISHED_MISSIONS_LIMIT = 5;
25
+ /** SEC-001B.1: `active.md` is a short hand-written context file (~3 KB today). */
26
+ export const MAX_ACTIVE_CONTEXT_BYTES = 256 * 1024;
27
+ function isPlainObject(v) {
28
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
29
+ }
30
+ async function readStateForBrief(targetDir, monolithRoot, root, diagnostics) {
31
+ const stateDir = join(targetDir, monolithRoot);
32
+ const filePath = stateFilePath(stateDir);
33
+ const provenance = { source: relative(targetDir, filePath) };
34
+ // SEC-001A: never read a state.json that resolves outside the project.
35
+ const escape = checkContainedFile(root, 'state.json');
36
+ if (escape) {
37
+ diagnostics.push({
38
+ code: 'PATH_OUTSIDE_PROJECT',
39
+ severity: 'error',
40
+ message: `${provenance.source} was not read: ${escape.message}`,
41
+ });
42
+ return { summary: { status: 'unreadable', provenance }, state: createInitialState() };
43
+ }
44
+ // Rule (section 4, #1): existence is established by the same bounded,
45
+ // non-blocking open that reads the file (SEC-001B.1) — a FIFO or an
46
+ // over-limit file is rejected before any content is read.
47
+ const read = readBoundedFileStable(filePath, provenance.source, { boundary: targetDir, maxBytes: MAX_STATE_BYTES, allowEmpty: true }, true);
48
+ if (!read.ok && read.code === 'FILE_MISSING') {
49
+ diagnostics.push({
50
+ code: 'STATE_NOT_INITIALIZED',
51
+ severity: 'info',
52
+ message: `${provenance.source} does not exist — no missions have ever been recorded.`,
53
+ readCode: 'FILE_MISSING',
54
+ });
55
+ return { summary: { status: 'absent', provenance }, state: createInitialState() };
56
+ }
57
+ if (!read.ok) {
58
+ diagnostics.push({
59
+ code: 'STATE_UNREADABLE',
60
+ severity: 'error',
61
+ message: `${provenance.source} exists but could not be read: ${read.code}: ${read.detail}`,
62
+ readCode: read.code,
63
+ });
64
+ return { summary: { status: 'unreadable', provenance }, state: createInitialState() };
65
+ }
66
+ const raw = 'content' in read ? read.content : '';
67
+ // A literally empty file is distinguished from corrupted content
68
+ // (DEC-CONTEXT-002: "corrupted state.json must not be treated as empty
69
+ // state") precisely so the STATE_UNREADABLE path below is never reached
70
+ // for this case by accident — it gets its own diagnostic instead.
71
+ //
72
+ // Review finding F-1: an empty state.json means no actual state could be
73
+ // read — that is a loss of information, not a benign "zero missions"
74
+ // result (which only a successfully parsed, valid file can report).
75
+ // severity 'error' here is what makes render() treat this as INCOMPLETE
76
+ // (and --strict exit 2), the same way STATE_UNREADABLE already does.
77
+ if (raw.trim() === '') {
78
+ diagnostics.push({
79
+ code: 'STATE_EMPTY',
80
+ severity: 'error',
81
+ message: `${provenance.source} exists but is empty — no state could be read.`,
82
+ });
83
+ return { summary: { status: 'empty', provenance }, state: createInitialState() };
84
+ }
85
+ let parsedJson;
86
+ try {
87
+ parsedJson = JSON.parse(raw);
88
+ }
89
+ catch (err) {
90
+ const msg = err instanceof Error ? err.message : String(err);
91
+ diagnostics.push({
92
+ code: 'STATE_UNREADABLE',
93
+ severity: 'error',
94
+ message: `${provenance.source} is not valid JSON: ${msg}`,
95
+ });
96
+ return { summary: { status: 'unreadable', provenance }, state: createInitialState() };
97
+ }
98
+ const onDiskSchemaVersion = isPlainObject(parsedJson) && typeof parsedJson.schemaVersion === 'number' ? parsedJson.schemaVersion : undefined;
99
+ let state;
100
+ try {
101
+ // Supports schemaVersion 2 and an in-memory v1→v2 migration of legacy
102
+ // files — nothing is ever written back (section 4, #6/#7).
103
+ state = validateState(parsedJson);
104
+ }
105
+ catch (err) {
106
+ const msg = err instanceof Error ? err.message : String(err);
107
+ diagnostics.push({
108
+ code: 'STATE_UNREADABLE',
109
+ severity: 'error',
110
+ message: `${provenance.source} failed schema validation: ${msg}`,
111
+ });
112
+ return { summary: { status: 'unreadable', schemaVersionOnDisk: onDiskSchemaVersion, provenance }, state: createInitialState() };
113
+ }
114
+ if (onDiskSchemaVersion === 1) {
115
+ diagnostics.push({
116
+ code: 'STATE_LEGACY_SCHEMA_MIGRATED',
117
+ severity: 'info',
118
+ message: `${provenance.source} is schemaVersion 1 — read via the existing in-memory v1→v2 migration; nothing was written to disk.`,
119
+ });
120
+ }
121
+ return { summary: { status: 'ok', schemaVersionOnDisk: onDiskSchemaVersion, provenance }, state };
122
+ }
123
+ function collectMissions(targetDir, monolithRoot, state, diagnostics) {
124
+ const active = selectActiveMissions(state);
125
+ const recentFinished = selectRecentFinishedMissions(state, RECENT_FINISHED_MISSIONS_LIMIT);
126
+ if (active.length > 1) {
127
+ diagnostics.push({
128
+ code: 'MULTIPLE_ACTIVE_MISSIONS',
129
+ severity: 'info',
130
+ message: `${active.length} missions are in_progress simultaneously: ${active.map((m) => m.id).join(', ')}.`,
131
+ });
132
+ }
133
+ const filePath = stateFilePath(join(targetDir, monolithRoot));
134
+ return { active, recentFinished, provenance: { source: `${relative(targetDir, filePath)}#tactical.missions` } };
135
+ }
136
+ function collectCheckpoints(targetDir, monolithRoot, state, active, diagnostics) {
137
+ const filePath = stateFilePath(join(targetDir, monolithRoot));
138
+ const provenance = { source: `${relative(targetDir, filePath)}#tactical.checkpoints` };
139
+ return active.map((m) => {
140
+ const latest = selectLatestCheckpoint(state, m.id);
141
+ if (latest && hasCheckpointOrderMismatch(state, m.id, latest)) {
142
+ diagnostics.push({
143
+ code: 'CHECKPOINT_ORDER_MISMATCH',
144
+ severity: 'info',
145
+ message: `Mission "${m.id}": another checkpoint has a later createdAt than "${latest.id}" (the highest-ordinal one) — timestamp order and ID order disagree.`,
146
+ });
147
+ }
148
+ return { missionId: m.id, latest, provenance };
149
+ });
150
+ }
151
+ function collectStory(targetDir, monolithRoot, root, diagnostics) {
152
+ const activeMdAbs = join(targetDir, monolithRoot, ACTIVE_MD_REL);
153
+ const relPath = relative(targetDir, activeMdAbs);
154
+ // SEC-001A: never present a file outside the project as project context.
155
+ const escape = checkContainedFile(root, ACTIVE_MD_REL);
156
+ if (escape) {
157
+ diagnostics.push({
158
+ code: 'PATH_OUTSIDE_PROJECT',
159
+ severity: 'error',
160
+ message: `${relPath} was not read: ${escape.message}`,
161
+ });
162
+ return { fileExists: false, hasCurrentStorySection: false, excerpt: null, lineRange: null, provenance: null };
163
+ }
164
+ const read = readBoundedFileStable(activeMdAbs, relPath, { boundary: targetDir, maxBytes: MAX_ACTIVE_CONTEXT_BYTES, allowEmpty: true }, true);
165
+ if (!read.ok) {
166
+ // SEC-001B.1-R (M-1): three different situations, three different diagnostics.
167
+ // Only a really absent file is "missing" (a warning: a project may have no story
168
+ // yet). A file the safe-read policy refuses, or one that cannot be read, is an
169
+ // error: the story exists but was not shown, so the brief is INCOMPLETE. The
170
+ // detail never carries file content — only the label and the typed code.
171
+ if (read.code === 'FILE_MISSING') {
172
+ diagnostics.push({
173
+ code: 'ACTIVE_MD_MISSING',
174
+ severity: 'warn',
175
+ message: `${relPath} does not exist.`,
176
+ readCode: read.code,
177
+ });
178
+ }
179
+ else if (isReadSecurityViolation(read.code)) {
180
+ diagnostics.push({
181
+ code: 'ACTIVE_MD_REJECTED',
182
+ severity: 'error',
183
+ message: `${relPath} exists but was refused by the safe-read policy and not read: ${read.code}: ${read.detail}`,
184
+ readCode: read.code,
185
+ });
186
+ }
187
+ else {
188
+ diagnostics.push({
189
+ code: 'ACTIVE_MD_UNREADABLE',
190
+ severity: 'error',
191
+ message: `${relPath} exists but could not be read: ${read.code}: ${read.detail}`,
192
+ readCode: read.code,
193
+ });
194
+ }
195
+ return { fileExists: false, hasCurrentStorySection: false, excerpt: null, lineRange: null, provenance: null };
196
+ }
197
+ const content = 'content' in read ? read.content : '';
198
+ const extraction = extractCurrentStory(content);
199
+ if (!extraction.hasCurrentStorySection) {
200
+ diagnostics.push({
201
+ code: 'ACTIVE_MD_NO_CURRENT_STORY',
202
+ severity: 'info',
203
+ message: `${relPath} exists but has no "## Current Story" section.`,
204
+ });
205
+ }
206
+ return {
207
+ fileExists: true,
208
+ hasCurrentStorySection: extraction.hasCurrentStorySection,
209
+ excerpt: extraction.excerpt,
210
+ lineRange: extraction.lineRange,
211
+ provenance: { source: extraction.lineRange ? `${relPath}:${extraction.lineRange}` : relPath },
212
+ };
213
+ }
214
+ /**
215
+ * Best-effort read of one state document for the freshness check. A missing,
216
+ * refused or unreadable file simply contributes no evidence — freshness never
217
+ * adds a diagnostic of its own and never makes the brief INCOMPLETE.
218
+ */
219
+ function readStateDocument(targetDir, monolithRoot, root, rel) {
220
+ if (checkContainedFile(root, rel))
221
+ return null;
222
+ const abs = join(targetDir, monolithRoot, rel);
223
+ const read = readBoundedFileStable(abs, relative(targetDir, abs), { boundary: targetDir, maxBytes: MAX_ACTIVE_CONTEXT_BYTES, allowEmpty: true }, true);
224
+ return read.ok && 'content' in read ? read.content : null;
225
+ }
226
+ function collectFreshness(targetDir, monolithRoot, root, story) {
227
+ const docs = [ACTIVE_MD_REL, ROADMAP_INDEX_REL, STORY_INDEX_REL, HANDOFF_REL];
228
+ const stateFiles = docs.map((rel) => posix.normalize(`${monolithRoot}/${rel}`));
229
+ const content = new Map(docs.map((rel) => [rel, readStateDocument(targetDir, monolithRoot, root, rel)]));
230
+ const roadmap = content.get(ROADMAP_INDEX_REL) ?? null;
231
+ const storyIndex = content.get(STORY_INDEX_REL) ?? null;
232
+ const evidence = {
233
+ git: readGitSignals(targetDir, stateFiles),
234
+ activeStory: story.fileExists && story.hasCurrentStorySection ? parseActiveStoryClaim(story.excerpt) : { kind: 'unknown' },
235
+ roadmapActiveStory: roadmap === null ? null : parseRoadmapActiveStory(roadmap),
236
+ roadmapStatuses: roadmap === null ? new Map() : parseRoadmapStatuses(roadmap),
237
+ storyIndexStatuses: storyIndex === null ? new Map() : parseStoryIndexStatuses(storyIndex),
238
+ recordedText: [...content.values()].filter((c) => c !== null).join('\n'),
239
+ stateFiles: new Set(stateFiles),
240
+ };
241
+ return { result: evaluateFreshness(evidence), evidence };
242
+ }
243
+ export async function collect(targetDir) {
244
+ const diagnostics = [];
245
+ const config = readBriefConfig(targetDir, diagnostics);
246
+ // Re-validates (and realpath-checks) the root readBriefConfig just accepted.
247
+ const root = resolveMonolithRootValue(targetDir, config.monolithRoot);
248
+ const monolithRootAbs = root.rootAbs;
249
+ const monolithInitialized = existsSync(monolithRootAbs);
250
+ if (!monolithInitialized) {
251
+ diagnostics.push({
252
+ code: 'MONOLITH_NOT_INITIALIZED',
253
+ severity: 'warn',
254
+ message: `No MONOLITH instance found at "${config.monolithRoot}" — nothing to summarize.`,
255
+ });
256
+ }
257
+ if (monolithInitialized) {
258
+ // Read-only (SEC-001B.2): names only. A warning, never an error — it does
259
+ // not make the brief INCOMPLETE — and nothing is deleted or repaired.
260
+ const artifacts = findWriteArtifacts(monolithRootAbs);
261
+ if (artifacts.length > 0) {
262
+ diagnostics.push({
263
+ code: 'WRITE_ARTIFACTS_PRESENT',
264
+ severity: 'warn',
265
+ message: describeWriteArtifacts(artifacts, config.monolithRoot),
266
+ });
267
+ }
268
+ }
269
+ const identity = {
270
+ name: config.projectName,
271
+ monolithRoot: config.monolithRoot,
272
+ provenance: { source: 'monolith.config.json#project.name' },
273
+ };
274
+ const { summary: stateKeeper, state } = await readStateForBrief(targetDir, config.monolithRoot, root, diagnostics);
275
+ const missions = collectMissions(targetDir, config.monolithRoot, state, diagnostics);
276
+ const checkpoints = collectCheckpoints(targetDir, config.monolithRoot, state, missions.active, diagnostics);
277
+ const story = collectStory(targetDir, config.monolithRoot, root, diagnostics);
278
+ const { result: freshness, evidence: freshnessEvidence } = collectFreshness(targetDir, config.monolithRoot, root, story);
279
+ const protocolsRelDir = `${config.monolithRoot}/${PROTOCOLS_DIR}`;
280
+ const mandatoryProtocols = {
281
+ items: discoverDocs(targetDir, [protocolsRelDir], (fields) => fields.get('enforcement') === 'mandatory', diagnostics),
282
+ provenance: { source: protocolsRelDir },
283
+ };
284
+ const adrDirs = config.adrDirs;
285
+ const adrs = {
286
+ items: discoverDocs(targetDir, adrDirs, (fields) => fields.get('status') === 'accepted', diagnostics),
287
+ provenance: { source: adrDirs.join(', ') },
288
+ };
289
+ return {
290
+ identity,
291
+ monolithInitialized,
292
+ stateKeeper,
293
+ missions,
294
+ checkpoints,
295
+ story,
296
+ mandatoryProtocols,
297
+ adrs,
298
+ diagnostics,
299
+ freshness,
300
+ freshnessEvidence,
301
+ };
302
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Minimal `monolith.config.json` reader for the brief subsystem.
3
+ *
4
+ * Mirrors the existing `readMonolithRoot()` duplicated in
5
+ * `src/coordinatorium/cli.ts` and `src/validate/index.ts` — same
6
+ * "duplicate rather than couple CLI layers together" precedent. Adds one
7
+ * new, explicit, optional field: `contextGateway.adrDirs` (DEC-CONTEXT-003 /
8
+ * section 8 — the existing config contract had no way to name additional
9
+ * ADR directories, so this is the minimal extension to it, not a new
10
+ * configuration subsystem).
11
+ *
12
+ * Review finding F-6: a missing config file is a legitimate, silent
13
+ * default (no project has been configured yet). A *present but corrupted*
14
+ * config file is not the same thing and must not be masked as "healthy
15
+ * project, defaults apply" — it is reported via CONFIG_UNREADABLE and
16
+ * forces Completeness: INCOMPLETE, same severity model as STATE_UNREADABLE.
17
+ * Likewise, a present-but-wrong-shape `contextGateway.adrDirs` (not an
18
+ * array) is reported via CONFIG_ADR_DIRS_INVALID rather than silently
19
+ * collapsing to `[]`. Non-string entries *within* an otherwise-valid array
20
+ * are dropped with a lower-severity (warn) note — a partially-junk list is
21
+ * a lesser problem than the field having the wrong shape entirely.
22
+ */
23
+ import type { BriefDiagnostic } from './types.js';
24
+ export interface BriefConfig {
25
+ monolithRoot: string;
26
+ projectName: string;
27
+ /**
28
+ * ADR directories to scan, relative to targetDir. Defaults to the canonical
29
+ * `docs/adr`; `monolith.config.json`'s `contextGateway.adrDirs` replaces
30
+ * (does not extend) that default. No legacy directory is scanned implicitly.
31
+ */
32
+ adrDirs: string[];
33
+ }
34
+ /**
35
+ * Throws RootContainmentError when `monolithRoot` violates the SEC-001A
36
+ * containment policy (see src/root) — the brief never degrades to a default
37
+ * root in that case.
38
+ */
39
+ export declare function readBriefConfig(targetDir: string, diagnostics?: BriefDiagnostic[]): BriefConfig;
40
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/brief/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAIlD,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAMD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,GAAE,eAAe,EAAO,GAAG,WAAW,CAuDnG"}