@mulmoclaude/core 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,8 +26,10 @@ user opens it at `/collections/<slug>`). **Do not use the `mc-` prefix.**
26
26
  > mimic other collections in the workspace. The whole point is a reproducible
27
27
  > billing suite. (If the user wants a *custom* collection instead, that's a
28
28
  > different task — use `config/helps/collection-skills.md`.) Existing records
29
- > under `data/clients/items` / `data/worklog/items` already match these schemas
30
- > and will render as-is once you author the skill — no data edits needed.
29
+ > under `data/clients/items` / `data/worklog/items` will render as-is **only
30
+ > when `worklog.clientId` values are already slugs AND a matching client
31
+ > record exists for each of them**. If either invariant is violated, the
32
+ > `ref` link breaks — the reconcile step below (§3) fixes both.
31
33
 
32
34
  ## Slug contract (do not change these)
33
35
 
@@ -206,10 +208,76 @@ If the user gives a clear "log N hours for X today", just write it. Use
206
208
 
207
209
  ---
208
210
 
211
+ ## 3. Reconcile pre-existing worklog data (MANDATORY before "Done")
212
+
213
+ Both skills are now authored. Before telling the user setup is complete,
214
+ audit any records that were already on disk under `data/worklog/items/` —
215
+ they may have been written before the `clients` collection existed, so
216
+ `clientId` values can be either display names (`"Singularity Society"`) or
217
+ missing-slug references. Both break the `ref` link at render time.
218
+
219
+ Run this reconcile every time — even when the setup looks fine — because
220
+ "looks fine" from the LLM side (schemas written, files present) is
221
+ distinct from "the ref actually resolves" (clientId is a valid slug AND
222
+ `data/clients/items/<slug>.json` exists).
223
+
224
+ **Steps** (idempotent; safe to re-run):
225
+
226
+ 1. **Inventory the existing worklog.** List `data/worklog/items/`. Read
227
+ every record and collect the distinct `clientId` values seen.
228
+ *Skip this step only when the directory does not exist at all.*
229
+ 2. **For each distinct `clientId` value**, classify it:
230
+ - **valid slug + matching client file exists** → no action.
231
+ - **valid slug but no matching `data/clients/items/<slug>.json`** →
232
+ create a stub client record: `id: <slug>`, `name` derived by
233
+ title-casing the slug (`acme-corp` → "Acme Corp"), other fields blank
234
+ so the user can fill them in later.
235
+ - **not a valid slug** (contains uppercase, spaces, `&`, `.`, etc.) →
236
+ (a) generate the slug (lowercase, spaces → `-`, drop punctuation,
237
+ collapse repeat hyphens, strip leading/trailing hyphens); (b)
238
+ **validate the generated slug against the `clients.id` contract**
239
+ (see § 1 record shape: `[a-z0-9-]`, 1–48 chars): if it is empty
240
+ (punctuation-only or all-whitespace input like `"!!!"`), or longer
241
+ than 48 chars, **stop and ask the user** for a valid slug rather
242
+ than writing an invalid filename. If it is >48 chars but has
243
+ meaningful prefix content, propose truncation at 48 (dropping any
244
+ trailing hyphen) and confirm with the user before proceeding; (c)
245
+ **check for slug collisions BEFORE writing anything**: if
246
+ `data/clients/items/<slug>.json` already exists AND its `name`
247
+ field does not match the display value being reconciled, stop and
248
+ disambiguate — either append a numeric suffix (`acme-corp-2`)
249
+ after confirming the new client is genuinely distinct, or ask the
250
+ user which client the worklog entries belong to. Do **not**
251
+ silently overwrite an existing client. Also stop when two
252
+ different display values in the current inventory slugify to the
253
+ same slug (e.g. `"ACME Corp"` and `"Acme Corp."`); ask the user
254
+ whether they're the same client (merge to one slug) or distinct
255
+ (assign each a suffixed slug); (d) create
256
+ `data/clients/items/<slug>.json` with `id: <slug>`, `name:
257
+ <original display value>`, other fields blank; (e) rewrite the raw
258
+ display value inside every affected `data/worklog/items/*.json`
259
+ to the new slug in the `clientId` field (preserve every other
260
+ field untouched).
261
+ 3. **Report** what changed: how many client records were created and how
262
+ many worklog `clientId` values were rewritten. Do not summarise the
263
+ raw records — point at `/collections/clients` and `/collections/worklog`.
264
+
265
+ If the worklog directory did not exist yet (fresh setup, no pre-existing
266
+ data), this step is a no-op and you can proceed straight to "Done".
267
+
268
+ **Don't skip this step because the LLM output says "existing records will
269
+ render as-is."** That claim only holds when both invariants (slug format
270
+ + matching client file) are already true; the reconcile confirms them.
271
+
272
+ ---
273
+
209
274
  ## Done
210
275
 
211
276
  Tell the user the two collections are ready at `/collections/clients` and
212
277
  `/collections/worklog`. The bridge mirrors the files and re-scans, so they appear
213
- without a restart. If invoicing is the goal, point them at the next step: run
214
- *"Set up invoicing for my business"* (the `config/helps/billing-invoice.md`
215
- recipe, which references these `clients` and pulls hours from this `worklog`).
278
+ without a restart. If the reconcile in §3 created client stubs or rewrote
279
+ `clientId` values, mention the counts so the user knows to fill in the
280
+ stubs' contact info. If invoicing is the goal, point them at the next
281
+ step: run *"Set up invoicing for my business"* (the
282
+ `config/helps/billing-invoice.md` recipe, which references these
283
+ `clients` and pulls hours from this `worklog`).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mulmoclaude/core",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "description": "Shared server-side core for MulmoClaude and MulmoTerminal — the always-shipped-together subsystems consolidated behind subpath exports so the two hosts can't drift. Server-only except the browser-safe ./whisper/client, ./workspace-setup/slug, ./translation/client and ./remote-view entries. All host specifics are injected.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -149,7 +149,7 @@
149
149
  },
150
150
  "dependencies": {
151
151
  "fast-xml-parser": "^5.9.3",
152
- "js-yaml": "^5.2.0",
152
+ "js-yaml": "^5.2.1",
153
153
  "zod": "^4.4.3"
154
154
  },
155
155
  "peerDependencies": {
@@ -160,9 +160,9 @@
160
160
  "@receptron/task-scheduler": "*",
161
161
  "@types/node": "^26.1.0",
162
162
  "gui-chat-protocol": "^0.4.0",
163
- "tsx": "^4.22.4",
163
+ "tsx": "^4.22.5",
164
164
  "typescript": "^6.0.3",
165
- "vite": "^8.1.2",
165
+ "vite": "^8.1.3",
166
166
  "vite-plugin-dts": "^5.0.3"
167
167
  },
168
168
  "license": "MIT"