24 Commits
Author SHA1 Message Date
DumiFlex bcc5a4ae57 feat(assembler): negatives are opt-in with $negatives, prefilled on new nodes (#278) 2026-11-04 23:25:50 +01:00
DumiFlex 2510428602 feat(manager): Library health page (#197)
One page that checks the whole library for broken references, constraint
rules for options that are gone, variables nothing sets, duplicates, unused
items and other clutter, with Open, Delete and Ignore on each row, plus a
variety check that runs a bundle over 200 random seeds. An open canvas tab
reports which library items and variables it uses, so canvas-only items
don't read as unused. The sidebar shows a problem/warning count and the
Dashboard health card uses the same checks.
2026-10-29 14:02:49 +01:00
Dumitru Picalau 657dc07831 feat(ai): assistant overhaul: tool registry, server-kept chats, search index, security, page tools, web search, saved chats, images, permissions, derivation NOT (schema 10), OpenAI Responses API (#64)
Phases 2 to 8 of the AI assistant overhaul plus fixes from real-model chats: one tool registry with native tool calls, server-kept chat logs with prompt caching, an FTS5 library index with opt-in meaning search, keys bound to their address and a guard on every AI route, page tools and opt-in web search, saved chats with streaming, Try again, a context meter and compaction, chat images, delete/category/tag permissions with Undo, constraint and test cards, card fixes and previews, questions with answers to click, the derivation NOT toggle (schema v10, community catalog 0021), request fixes for the newest Claude models, and OpenAI's Responses API. Review fixes: Try again clears the old turn's fixes and answers, category Undo removes only what it made, a failed delete rolls back in order, not_matches patterns are checked, busy chats refuse a second request, and chat search ignores stored JSON.
2026-10-14 14:56:12 +02:00
Claude c46ce8dba7 feat(templates): save the Assembler's negative template
Migration 019 adds an optional `negative_template` column to `templates`
(row-level, no schema bump). NULL marks a template saved before
negatives existed. The repository, importer (add, replace with
COALESCE, undo), templates API and TS types carry it, and the manager's
template editor gets a Negative template field.
2026-09-30 09:22:47 +00:00
Claude 87ac907403 feat(api): save Test Runner scenarios
Migration 018 adds a test_scenarios table, and /wp/api/test/scenarios
gives it list/get/create/update/delete. A scenario stores its stack
(library modules and bundles referenced live by id), pinned $var
values, seed spec and output variable, plus two JSON blobs the Test
Runner writes: a baseline to compare against and the latest run
summary. Listing can filter to the scenarios that use a given module
or bundle, for the "used in N scenarios" hint.

Scenarios are local only. They are not part of Import/Export or the
community, and this is a row-level table, so no schema_version bump.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xwq95EvrKu6WgE9D5rahKy
2026-09-26 01:54:11 +00:00
DumiFlex 8cf40241cf fix(db): record migration checksums and refuse a foreign schema
`migrate()` skips everything `<= MAX(version)`, which made an integer
the entire identity of a schema. It is not, and two failures followed
from that, both silent:

1. A database migrated by a NEWER build. Every migration is already
   "applied", so the runner does nothing and the caller reads a schema
   it has never seen. Additive columns survive; a shape change does not.
2. A database at the SAME version but a different shape, because a
   migration file changed after it had already run somewhere — a
   revised migration, a hand-edited file, an abandoned branch. Version
   numbers match, so nothing compared anything.

Both corrupt cumulatively: every subsequent write goes through code
whose assumptions about the row shape are wrong, and the user has no
signal until something much later fails for an unrelated-looking reason.

Migration 017 adds a nullable `checksum` column; the runner records the
sha256 of the file it actually executed. `assert_schema_compatible`
checks both failure modes and runs at the top of `migrate()`, so no
caller can forget it.

NULL is treated as "unknown", never "mismatched". Rows applied before
017 existed cannot retroactively grow a checksum, and backfilling them
from the files on disk would assert exactly the compatibility the check
exists to verify. So the version-ahead half protects every database
immediately; the divergence half protects migrations from 018 onward.
2026-08-09 01:52:47 +03:00
DumiFlex 39529a669b feat(engine): migrate library v1->v2 (multi-tag) + bump schema version
engine/migrations/v1_to_v2.py: option sub_category->sub_categories[]; sub-category name slugify (whitespace/grammar chars->_, reserved suffixed, de-duped) cascaded to options + tag_groups; instance category_filter list->boolean-expr string + exclude_null flag; nested @{uuid:a,b,null} refs->@{uuid:a or b!null}. Idempotent. Two entry points share one core: envelope migrate_v1_to_v2 (import/export, registered in _CHAIN) + per-row migrate_module/bundle_v1_to_v2 (lazy/bulk DB path).

Register _CHAIN[1] + bump engine CURRENT_SCHEMA_VERSION 1->2. New SQL migration 016 marches stale DB rows 1->2 at boot via run_bulk_lazy + the per-row migrators (014's documented bump pattern). TS mirror (migrations.ts) + client CURRENT bump follow next (no cross-language version assertion couples them). Update version/entity-count assertions in payload-migration, migration-runner, and export-build tests.
2026-06-07 01:40:53 +03:00
DumiFlex 395af63b1a feat(engine): content_rating column on library rows (migration 015)
Adds `content_rating TEXT NOT NULL DEFAULT 'safe'` to modules + bundles.
Defaults all pre-015 rows to 'safe'. Repository create/update accept a
new content_rating kwarg with validation against {'safe', 'nsfw'}.
Importer _insert_module / _insert_bundle pass entity.content_rating
through; _update_module / _update_bundle use COALESCE(?, content_rating)
so an install path that omits the key preserves the existing flag.

Why: user wants two NSFW behaviors:
1. Edit page toggle to mark a module/bundle as NSFW locally.
2. Community posts with content_rating='nsfw' auto-stamp NSFW on
   the local row at install time.

This is the engine half of that work -- the schema, the data
plumbing, and validation. The SPA half (IdentityCard toggle + pill
render on ModuleListView + install.ts plumbing) lands next.

Tests cover: migration head bump to 15, column existence on both
tables, safe default backfill, accepts nsfw, rejects invalid, update
toggle round-trip, update without content_rating preserves prior.
2026-06-06 05:22:02 +03:00
DumiFlex 73ca8a3f9a feat(engine): bulk lazy runner — eager iteration of lazy_migrate_row
Implements spec §4 Flow 4: bulk-at-boot is NOT a parallel forward-chain
implementation; it's eager iteration of lazy_migrate_row over every
stale row. Same original-first logic, same re-projection, zero
divergence. Eliminates the transient-wrong window where bulk could
write fabricated defaults over recoverable data in original_payload_json.

run_bulk_lazy returns the failure count; caller surfaces a dashboard
banner when > 0. STALE_PREDICATE catches both 'sv < current' AND
'tolerantly-installed row whose original now outranks the projection'.

Migration 014 invokes run_bulk_lazy at the end of up() — no-op pass
at v1, but validates the wiring is correct. Future bump migrations
(015 = v2, etc) will reuse the same helper.
2026-06-05 14:21:31 +03:00
DumiFlex c46cbbf509 feat(engine): migration 014 - payload schema_version + verbatim-local mirror
Adds the four payload-versioning columns to modules + bundles per
the community-web spec sections 4-5:
- schema_version (NOT NULL DEFAULT 1)
- original_payload_json (NULL - only set during tolerant install)
- tolerant_drift_status ('none' | 'pending' | 'broken')
- schema_migrated_at (separate from updated_at - structural migration
  is not a user edit)

Backfill is implicit via the DEFAULT on the NOT NULL columns.
2026-06-05 12:50:54 +03:00
DumiFlex 9957702099 feat(community): track install origin + update-available indicator
Library rows installed from the community embed now carry the source
post slug + installed version_number in the engine sqlite. The SPA
uses both surfaces: a "community" pill next to the row name (so users
can tell at a glance which entries were authored locally vs pulled
from the embed), and a warn-coloured "v{N}" pill when the post has
shipped a newer version since install.

Engine:
- Migration 013 adds community_post_slug + community_version_number
  to modules + bundles. Both nullable; locally-authored rows stay
  NULL forever. Implemented as a .py migration with PRAGMA
  table_info guards so rerunning against an already-migrated DB
  (test fixtures rewind migrations to exercise older paths) is a
  clean no-op rather than "duplicate column name".
- _insert_module / _insert_bundle (importer.py) persist the fields
  when the entity dict carries them.
- _row_to_module / _row_to_bundle (repositories.py) surface them on
  every read path. Try/except guard for pre-013 DB shape so a
  connection opened mid-rollout does not crash.

Manager:
- installEnvelope grows optional `origin: { post_slug, version_number }`
  in InstallOptions. When set, stamps every module + bundle in the
  selection before the commit roundtrip.
- Host bridge in main.ts threads origin from `install(envelope, opts)`
  through to installEnvelope. ABI stays at 1 (additive).
- New communityUpdateStore walks the library snapshot, batch-fetches
  each unique community post (deduped, 8-wide concurrency), surfaces
  rows whose installed version is behind the post's
  latest_version_number. Network failures clear the store silently
  so an offline session does not render misleading "v2 available"
  pills.
- AppLayout kicks one check after the initial library fetch
  resolves. No background polling.
- ModuleListView renders two distinct pills per row: a static
  "community" indicator on origin-stamped rows, and a separate
  warn-coloured "v{N}" pill when the update store has an entry.

Migration tests updated for the new schema head + bundle/module
column sets.
2026-06-04 17:21:04 +03:00
DumiFlex 65080db689 feat(templates): add prompt template table, repository + REST API
Introduces a persisted prompt-template library: migration 012 creates the
templates table (name/description/category_id FK/tags/is_favorite/
template_string), TemplateRepository provides CRUD with partial-update
sentinel semantics, and wp_api/templates.py exposes list/get/create/update/
delete/favorite with FK violations mapped to 400. Bumps the migration-head
version assertions to 12.
2026-05-27 00:25:52 +03:00
DumiFlex 98730bc70b feat: polish branch — pair badges, bundle cascade, light theme, debug viewer, cleaner
Constraint pair badges:

- engine: via-nested carrier detection (computePairingsFull scans downstream wildcards' options for the target uuid; first match claims the slot as a CARRIER, sender + carrier share a PairingVia)

- PairBadge.vue: unified chip + Teleport popover for sender / direct / carrier / option surfaces. Carrier variant collapses inbound via-pairs into a single solid chip. Pair color hashed from senderRowKey+targetUuid so distinct senders avoid 8-slot palette collisions

- Threads viaOptionPairs from ContextWidget through ModuleEditModal -> WildcardInstanceModal -> PoolSection -> OptionRow for trailing chip in option value cells

Other polish:

- Bundle MISSING badge, push-to-library, cascade restore, fork-of-missing rebind, sidebar live counts, drift-store guard

- DebugViewer: RichTextPreview chips, library + preview-cache uuid resolve, constraint trace status 'never fired'

- Light theme: refchip/optionrow tokens, constraint matrix axes, kind colors, picker overlay

- Cleaner widget polish, autoheight, blocklist baseline, dangling_var post-strip

- Nested ref syntax #name segment, autocomplete dedupe by opts count + uuid, broken refs cached-name fallback

- Cascade: cleanup as opt-out checkbox, bundle delete scan + fixer, multi-root regression fix

- Migration 011 backfill ref names; test_migration_010 reset versions >=10 to coexist with 011
2026-05-26 11:21:05 +03:00
DumiFlex c0d42aee5d feat(migrations): 010 backfill option ids and migrate exception schema 2026-05-24 06:27:02 +03:00
DumiFlex 913e3280e9 feat(engine): cascade_undo table migration 2026-05-24 00:55:40 +03:00
DumiFlex d0298fc6e3 fix(db): backfill NULL module fingerprints on next open
Migration 006 added the snapshot_fingerprint column but only populated
it on subsequent writes — every pre-006 row stayed NULL. The collision
detector correctly handles NULL as `exists-unknown` (amber EXISTING
badge), but a freshly exported-then-reimported untouched row still
showed EXISTING when content was byte-identical. Backfilling on first
open turns those into silent-skip (no badge), removing the visual noise.

Pure read+UPDATE — no schema change, no version bump on rows.
2026-05-23 22:00:31 +03:00
DumiFlex 17386f7990 feat(engine): atomic import commit + snapshot undo with 7-bucket dispatch
Adds engine/importer.py exposing commit_import / get_undo_entry /
undo_import. The committer applies a classified adds/replaces/renames
payload inside a single sqlite transaction, dispatching by `kind` to
the modules table (5 module types), bundles, or module_categories.
Module writes recompute snapshot_fingerprint; bundle writes recompute
payload_hash; categories use case-insensitive name-based merge
(matching the legacy import_bundle handler).

Every successful commit persists an import_undo row with enough
metadata (imported_records, replaced_snapshots, rename_map) to
reverse the commit. undo_import restores replaced rows bit-for-bit
(preserving original version + payload_hash + timestamps) and
deletes inserted rows, then evicts the undo entry.

Migration 007 creates the import_undo metadata table. Adds
17 importer tests covering adds across all 7 buckets, replace
snapshotting, rename with old→new mapping, collision rollback,
and undo restore/delete paths.
2026-05-22 21:52:11 +03:00
DumiFlex fb4ae039d0 feat(engine): backfill snapshot_fingerprint on module create/update 2026-05-22 17:50:11 +03:00
DumiFlex 2f0e95db20 feat(db): migration 005 — bundles table for library-tracked groupings 2026-05-11 02:41:32 +03:00
DumiFlex 6921606570 feat(context): module picker + lock/internal + card indicators (#8)
Phase 5.5.4 (SPA library picker for ContextWidget) plus QA-driven polish.

* New ModulePickerModal with search, kind tabs, single + multi-pick, and
  compact filter UX (favorites/hide-added inline; category + tag chips
  behind popover with active-count badge).
* ModuleEditModal kind-aware body — wildcard option editor with
  enable/disable, weight overrides, mode picker (random/subset/pinned),
  sub-category multi-select.
* Lock + internal per-instance overrides ported and adapted: locked_seed
  derives a per-wildcard rng via sha256(seed:binding); internal hides
  bindings from the public PIPELINE_CONTEXT payload.
* Card indicators: modified dot (option-pool overrides), missing-from-
  library dot (polled hash check), in-card lock + internal toggles.
* RNG-faithful assembler preview via new /wp/api/preview/resolve; header
  shows "PREVIEW · 42". UNRESOLVED chips clickable to strip $var.
* Bulk collapse/expand toggle, focus toast w/ pulsing border overlay
  that tracks node bounds, friendly @name substitution in modal.
* Backend: new /wp/api/modules/import-from-workflow endpoint preserves
  uuid; engine emits per-module effective seeds via WP_Context UI
  payload for authoritative lock-toggle defaults.
2026-04-30 00:53:13 +03:00
DumiFlex d9c6ce758d test(db): expand migration 003 coverage + document partial-apply limit 2026-04-28 12:16:22 +03:00
DumiFlex 07e4df007c feat(db): add indexed uuid column to modules with backfill 2026-04-28 12:04:16 +03:00
DumiFlex 2d89a7391d feat(engine): scaffold combine/derivation/constraint/pipeline module types 2026-04-27 03:54:13 +03:00
DumiFlex cac9815026 feat(db): sqlite library foundation + migration runner 2026-04-26 15:26:59 +03:00