Commit graph

12 commits

Author SHA1 Message Date
Titus
6c04bf692c 3.0.0 batch: template-free everywhere, read-key pinless download, docs
- Template fetch retired: Create/Download/Paste no longer fetch or write the
  legacy PluginFiles/Templates + Handlebars sets (nothing reads them — every
  note write is frontmatter via writeElement). includeLegacyTemplates defaults
  false; the two Create* template commands kept as a reversible margin.
- Read-only keys need no PIN: WorldDownloadModal detects ow_r_ / demo keys and
  greys out the PIN field; validation accepts a blank PIN and sends "". Write
  keys and private-world write keys still require the 4-digit PIN (unit-checked:
  read keys skip, write keys don't — no auth hole). pin type: number -> string.
- Docs: README read-key note + all commands current; release notes reversioned
  2.4.0 -> 3.0.0 (the frontmatter era: format change + folder bridge + span
  retirement = a major).

Gate: tsc clean, build clean, 60/60 tests + pinless-detection unit check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 09:12:53 +02:00
Titus
7ed0c1cfca 2.4.0 prep: ValidateWorld skips frontmatter, README + release notes current
- ValidateWorld: skip frontmatter notes instead of showing false 'missing
  Id/Name' errors on migrated worlds (the span linter can't judge them; a
  frontmatter-aware validator is future work). The user-facing lie is the
  actual harm; this removes it without a full rewrite.
- README: frontmatter format + clickable [[wikilink]] fields, the Migrate
  command + note-format/migration section, folder Export/Import commands,
  folder-bridge in the ecosystem section. Voice preserved (Captain's), facts
  brought to 2.4.0.
- 2.4.0 release-notes draft (the GitHub release body + Captain's gate).

Gate: tsc clean, build clean, 60/60 tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 08:32:25 +02:00
Titus
10951cbacf gate fix: writeElement filename uses canonical sanitizeFileName
The download id->name map sanitizes wikilink targets with WorldService's
sanitizeFileName (strips control chars + trailing dot/space, 'unnamed'
fallback), but writeElement named the actual file with a WEAKER regex
(colon/slash class only). A name with a trailing dot or control char would
name the file one way and the [[link]] another -> dangling link. Both now
use sanitizeFileName, so the wikilink always matches the on-disk basename.

Also files the download-resolver brief.

Gate: tsc clean, build clean, npm test 60/60 (orchestrator hands).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 22:50:10 +02:00
Titus
41f8ab1839 docs: file the readability-bout brief
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 22:24:52 +02:00
Titus
491b05d232 smoke: test both export destinations + note which dialog path fires
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:41:54 +02:00
Titus
6887dbe8f4 docs: file the export-picker brief (was untracked)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:41:17 +02:00
Titus
bdb1eff8d1 docs: core smoke checklist + Atlas-handoff design finding
CORE-SMOKE.md: the 10-min pass. Records what Skeld verified by execution
(migration correctness on Captain's real MessWorld run — 22/22, 0 residue;
extension-field round-trip incl. nested objects, both via the plugin's own
transform) vs the human-only pass (download/save/migrate/folder-bridge).

atlas-handoff-design.md: why the plugin can't auto-detect the Atlas root
(browser FS-Access sandbox, opaque IDB handles, no shared path — confirmed
against Atlas source in the Forge), and that a destination picker is the
chosen smoothing (executor building it).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 17:37:26 +02:00
Titus
801cf64a44 Phase C: OnlyWorlds folder bridge (export/import commands)
Two additive commands making the plugin the folder format's fourth
implementation (after Atlas, Magelet, azgaar-converter):

- Export as OnlyWorlds folder: walk a world's notes -> conformant folder
  under OW-folder-export/ (vault-internal). world.json id+name, spatial
  bucketing, body type/local_updated_at/created_at stamp, <slug>--<tail>
  filenames. [[wikilink]] -> ow://type/uuid where names resolve.
- Import OnlyWorlds folder: read a world folder placed in the vault, mint
  frontmatter notes via Phase B's writeElement. ow:// -> [[wikilink]],
  skip-existing-id safety, abort on world-id mismatch, verbatim extension
  fields.

Pure serializers in vault/folder-format.ts (no Obsidian imports), 13 new
tests: conformance pins for all four Atlas-ingest facts + in-memory
round-trip (object equality on ids/links/extension keys/story). Phase B's
21 tests stay green. Smoke section G appended.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 14:03:07 +02:00
Titus
4b056022eb gate fixes: frontmatter elements keep their id through Upload/Copy
readElement returns id OUTSIDE .fields; the Phase B guards in Export and Copy
returned parsed.fields bare, so on a fully-migrated world Upload World would
silently skip EVERY element (the sweep's if-no-id-continue) while reporting
success, and Copy World would emit id-less JSON that breaks paste dedupe.
Both guards now return { id, ...fields }. Smoke step 17 strengthened to check
the uploaded count and spot-check ids in copied JSON, so this class stays dead
at the user layer too.

Gate: tsc clean, build clean, npm test 21/21 (orchestrator hands).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:54:47 +02:00
Titus
c11db83d57 R9: writer-minted migration fixtures + entity-decode fix
Mint authentic old-format span fixtures via the REAL writer (real handlebars lib
+ verbatim upstream templates + linkify helper + [[id]]->[[Name]] rewrite) rather
than hand-written strings, per the R9 addendum. test/span-writer.ts (minter, with
disclosure that template STRINGS are transcribed but the RENDER is genuine) +
test/migration-roundtrip.test.ts (5 cases: Character multi-links, Narrative
body->story, apostrophe/HTML-entity, comma-in-linked-name, extension-field
round-trip).

The writer-minted apostrophe fixture caught a REAL bug hand-written fixtures
missed: the migration path did NOT decode HTML entities, so a pre-2.2.2 escaped
note ("The Kid&#x27;s Family") would migrate with the corruption preserved. Fixed:
element-transform.ts parseSpanNote now decodes via the pure decodeHtmlEntities
(matching SaveElement/ExportWorld). Propagates to every span-read path (migration,
readElement span fallback). 21/21 tests pass.

SMOKE-CHECKLIST.md: added the copy/throwaway-vault-first caution as the first line.

Also folds in a working-tree safety change to MigrateWorldCommand + main.ts that I
did NOT author (a teammate added it mid-flight): migration takes auto-sync's
markSelfWrite guard so a user with auto-sync on doesn't mass-PATCH migrated notes
to the server (which would break backup-based reversibility). Sound change; added
the null-guard (this.autoSync?.) it was missing so tsc passes. Flagged to lead.

Gate: tsc clean, esbuild build succeeds, npm test 21/21.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:49:32 +02:00
Titus
59feec9338 Phase B: frontmatter flip (span -> YAML frontmatter)
Wake the dormant vault/element-file.ts write path and make YAML frontmatter
the note format for new writes, with read-tolerance for legacy span notes.

Pure logic (Obsidian-free, unit-tested under `npm test`):
- vault/element-transform.ts: frontmatter<->payload, span parse, span->frontmatter,
  link normalization (single string / multi list, never comma-joined), extension-key
  passthrough (atlas_/shadow_/x_), body<->description|story mapping, read-before-PATCH diff.
- test/element-transform.test.ts + tsconfig.test.json: 16 node:test cases incl. a real
  span-format Character fixture. Wired to `npm test` (pretest compiles to test-dist/).

Wiring:
- element-file.ts: readElement preserves extension fields + maps body to story for
  Narrative (R3/R5); frontmatter-first with legacy span fallback; writeElement round-trips
  extension keys and respects count-suffixed folders.
- DownloadWorldCommand + CreateElementCommand: emit frontmatter via writeElement (no more
  Handlebars element templates).
- NoteLinker: reworked onto metadataCache + SDK FIELD_SCHEMA; writes link IDs into
  frontmatter (new FieldSelectionModal picks the field). NameChanger syncs frontmatter name.
- SaveElementCommand: read-before-PATCH (GET, diff, PATCH only changed fields) (R7).
- ExportWorldCommand + CopyWorldCommand: read frontmatter via readElement, span as fallback
  (mixed worlds upload/copy correctly).
- MigrateWorldCommand: "Migrate world notes to frontmatter" — backup-first (abort on failure),
  idempotent, end report modal (converted/skipped/failed).

Deferred (see docs report): PasteWorld still emits span; ValidateWorld is still a span linter
(diagnostic only, no writes). docs/SMOKE-CHECKLIST.md is the manual release gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:43:56 +02:00
Titus
a5cf498ed7 2.3.0: OnlyWorlds v2 API transport + smoke-hardened sync + Delete Element
- All sync paths on /api/v2: Download World = /changes cursor walk (incremental
  re-downloads, rewind detection, honest delete reporting); Upload World =
  create/update sweep via /bulk + PATCH (server-only elements never deleted);
  Save/auto-sync via shared V2Client. Legacy worldsync routes fully retired.
- Filename sanitization: Windows-illegal chars (':' etc.) no longer mangle
  notes into NTFS streams; wikilinks use sanitized targets, names stay exact.
- Link safety: unresolvable links skip the whole field (server values kept,
  surfaced in a toast); uuid-shaped wikilinks pass through as ids.
- Delete Element command: typed-name confirm -> server DELETE -> note to trash.
- Progress toasts for download/upload; README rewritten (v2 semantics +
  ecosystem section).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:22:24 +02:00