docs: add coding agent guidance from tag management implementation#62
Draft
andykais-claude wants to merge 53 commits into
Draft
docs: add coding agent guidance from tag management implementation#62andykais-claude wants to merge 53 commits into
andykais-claude wants to merge 53 commits into
Conversation
Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
- Use DROP COLUMN instead of table-rebuild for alias_tag_id removal - tag_rule uses slug-based references (source_tag_slug/target_tag_slug) - Add slug field to Tag model for fast lookups - Alias tags have zero media_reference_tag rows; add_rule migrates existing rows - Rename remove_rule to delete_rule for consistency - Add aliases and parents columns to tag search results table - Use slug instead of id for tag detail page route (/tags/[slug]) - Update all sections for slug-based design throughout Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
The polymorphic tag_rule table provided no real benefit since aliases and parents have fundamentally different logic, side-effects, validation, UI, and action methods. Separate tables and models keep each concept self-contained. Action interface updated to: Forager::tag::alias_create / alias_delete Forager::tag::parent_create / parent_delete Input schemas, models, migration, RPC, and web pages updated throughout. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
…op alias_tag_id - Add slug column to tag table (populated from tag_group.name:tag.name) - Create tag_alias table with source_tag_slug/target_tag_slug (slug-based) - Create tag_parent table with source_tag_slug/target_tag_slug (slug-based) - Rebuild tag table to remove alias_tag_id (FK constraint requires full rebuild) - Drop and recreate all triggers referencing the tag table - Update seed migration to version 9 with new schema - Update Tag model schema: add slug, remove alias_tag_id - Remove alias_tag_id usage from tag_create() and series_actions - Update migration test expectations for v9 Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Replace inline UNIQUE constraints with named indexes for both tables: - tag_alias_pair (unique), tag_alias_source, tag_alias_target - tag_parent_pair (unique), tag_parent_source, tag_parent_target These indexes will be needed for efficient slug-based lookups in the TagAlias and TagParent models. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
- TagAlias: create, delete, select_by_source, select_all_by_target, update_source_slug, update_target_slug - TagParent: create, delete, select_children, select_parents, update_source_slug, update_target_slug - Register both in models/mod.ts and add result types Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Add #select_by_slug query and wire it into #select_one_impl so tags
can be looked up by slug (e.g. Tag.select_one({slug: 'genre:adventure'})).
Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
New Zod schemas in tag_management_inputs.ts: - TagGet, TagUpdate - TagAliasCreate, TagAliasDelete - TagParentCreate, TagParentDelete Exported via inputs_parsers.ts and inputs_types.ts. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
…hods New action methods on Forager::tag: - get(slug): fetch tag detail with aliases, parents, children - update(slug, name?, group?, description?): edit tag properties, recompute slug, update tag_alias/tag_parent references - alias_create(source, target): create alias with media_reference_tag migration from source to target - alias_delete(id): remove alias - parent_create(source, target): create parent/child with cycle detection - parent_delete(id): remove parent/child Supporting changes: - Tag model: add #update query - MediaReferenceTag model: add select_all_by_tag_id query Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
- Replace TagRuleRef with result_types.Tag in TagDetail interface - Move tag_slug_format to private #format_slug method on TagActions - TagAliasCreate/TagParentCreate inputs now accept Tag structure (name+group or shorthand) instead of raw slug strings - alias_create wraps create + migration in a transaction - parent_create wraps create + media propagation in a transaction - parent_create adds parent tag to all existing media with child tag - tag.update throws if tag rules exist when renaming (name/group change) - tag.update collision error includes other tag's id - TagAlias model: field comments explaining source=alias, target=canonical - TagParent model: field comments explaining source=child, target=parent Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
- Rename #migrate_alias_tags to #apply_alias_to_existing_media - Remove #format_slug indirection, use tag_slug_format directly - alias_create now returns TagDetail (for the target/canonical tag) - parent_create now returns TagDetail (for the target/parent tag) - Extract #build_tag_detail helper shared by get/alias_create/parent_create Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
…rns TagParentResponse
Both methods now return full response structures with TagDetail for
both sides of the relationship plus the rule record itself:
- TagAliasResponse: { alias, alias_target, rule }
- TagParentResponse: { parent, child, rule }
Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Three top-level tests covering new tag management functionality: - tag alias: create with migration, get relationships, duplicate/self/chain validation, delete, and alias-with-parent overlap - tag parent: create with propagation, get relationships, self/circular validation, delete - tag update: rename name/group/description, slug collision, rename blocked by existing rules, nonexistent tag Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
- Move all tests into tag.test.ts (delete tag_management.test.ts) - Use ctx.assert.list_partial for alias/parent/children assertions - Fix alias_create/parent_create to handle missing source tags (TagAliasResponse.alias and TagParentResponse.child are now nullable) - Add test: alias migration when source+target share a media reference - Add test: multi-level circular parent detection (A->B->C, then C->A) - Add test: auto cleanup interaction with aliases - Add test: alias_create when source tag doesn't exist yet - Add test: parent_create when source tag doesn't exist yet Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
…agDetail Rename alias_target -> alias_for throughout TagDetail, TagAliasResponse, tag_actions.ts, tests, and design doc for clarity. Add docstring comments to all TagDetail, TagAliasResponse, and TagParentResponse fields describing their purpose. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Add get, update, alias_create, alias_delete, parent_create, and parent_delete to ForagerTagApi. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
New route with controller, queryparams rune, and components: - /tags/+page.svelte: page with header, search controls, results table - TagsController: extends BaseController with TagQueryParams rune - TagQueryParams: manages search state, URL sync, and RPC calls - TagSearchParams: search input, sort dropdown, order toggle - TagSearchResults: table with tag name, group, media/unread counts, created/updated dates; each tag links to /tags/[slug] Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
New /components/Datetime.svelte renders a <time> element with toLocaleDateString() display and full ISO datetime in the title attribute. Replace inline format_date helper in TagSearchResults. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
- tag_create now resolves aliases: if a tag is an alias, the canonical tag is returned instead - manage_media_tags now applies parent tags: after all tags are applied, parent rules are walked and parent tags are added to the media - series_actions uses tag_create for alias resolution - Add test suite: 'tag rules respected during media operations' with subtests for alias resolution and parent propagation during both media.create and media.update Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
- Create shared BaseQueryParams rune in $lib/runes, TagQueryParams extends it - Fix order param: wire through tag_actions.ts to Tag.select_paginated, use dynamic ASC/DESC in SQL ORDER BY clauses - Tag component: add hide_count prop to suppress media_reference_count - TagSearchResults: drop transparent on Tag chips, use hide_count, make table headers clickable to change sort column and toggle order (with sort direction indicator arrows) Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
…ination
- Restore show_group={false} on Tag chip in search results (group
column makes it redundant)
- Add Scroller-based infinite scroll to TagSearchResults
- TagQueryParams: add load_more() method that increases limit by
PAGE_SIZE and re-fetches, tracks has_more state
- Sticky table header within scroll container
Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
- TagActions.search now returns TagSearchResultItem with alias_count and parent_count fields, computed per tag from tag_alias/tag_parent - TagSearchResults table: add Aliases and Parents columns showing counts (blank when zero for cleaner display) Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Move tag count from inline text in TagSearchResults to a dedicated Footer component matching the /browse page footer style. Shows 'Total tags N' with human-readable number formatting. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Add a nav section to the left side of the Header with Browse and Tags links. The current page is highlighted (bright text, darker background), inactive pages are subtle with hover states. Uses SvelteKit page state for active detection via pathname prefix matching. Simple inline nav links chosen over hamburger/dropdown since there are only two pages — no hidden state or extra clicks needed. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Header now uses CSS grid (auto 1fr columns). SearchParams forms use display:contents so their children become direct grid items. The search bar row sits next to the nav (column 2), while the advanced filters row spans the full width (grid-column: 1 / -1). The nav stays aligned with the top row in both collapsed and expanded states. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
New route for viewing and editing a single tag's properties and relationships. Backend: - TagDetail now includes rule_id on each related tag (TagWithRuleId) so the UI can delete specific alias/parent rules Frontend (/tags/[slug]): - TagDetailController: load, save, alias_create/delete, child_create, parent_add/delete methods with auto-reload after mutations - Section 1 (Tag Info): editable name, group, description with save button. Navigates to new slug URL on rename. - Section 2 (Aliases): shows alias_for target if this tag is an alias, lists tags aliased to this one, add alias input via TagAutoCompleteInput - Section 3 (Parent/Child): lists children and parents with add/remove controls via TagAutoCompleteInput Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
…RL encoding - Rename controller.ts -> controller.svelte.ts so Svelte compiler processes $state runes (fixes rune_outside_svelte error) - Remove encodeURIComponent from all tag slug URLs — slugs only contain [a-z0-9_:] which are valid URL characters. URLs now show as /tags/medium:painting instead of /tags/medium%3Apainting Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
- Show created_at date in the Tag Info section using Datetime component - Add 'Browse media with this tag' link that navigates to /browse?tags=<slug> Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Replace generic source_tag_slug/target_tag_slug with semantically meaningful names: tag_alias table: source_tag_slug -> alias_tag_slug target_tag_slug -> alias_for_tag_slug tag_parent table: source_tag_slug -> child_tag_slug target_tag_slug -> parent_tag_slug Input schemas: source_tag/target_tag -> alias_tag/alias_for_tag (aliases) source_tag/target_tag -> child_tag/parent_tag (parents) Updated migration, seed, models, actions, and all tests. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
alias_create: source_tag/target_tag -> alias_tag/alias_for_tag parent_create: source_tag/target_tag -> child_tag/parent_tag Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
The logic was right but the descriptions were backwards. Fixed: - TagParent model: child_tag_slug is the tag that triggers parent inclusion - TagDetail: children = when a child is applied, this parent is also applied - TagDetail: parents = when this tag is applied, the parent is also applied - UI labels updated to match the correct semantics Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Migration v11: rebuild media_reference_tag with tag_alias_id and tag_parent_id nullable FK columns referencing tag_alias and tag_parent. Model changes: - MediaReferenceTag: add tag_alias_id/tag_parent_id to schema and create - Add delete_by_tag_alias_id and delete_by_tag_parent_id queries New media_add_tag helper in base.ts: - Replaces tag_create + MediaReferenceTag.get_or_create pattern everywhere - Resolves aliases (sets tag_alias_id on the created row) - Walks parent chain and adds parent tags (sets tag_parent_id on each) Action changes: - alias_create: passes alias rule ID to #apply_alias_to_existing_media - alias_delete: deletes media_reference_tag rows by tag_alias_id first - parent_create: passes parent rule ID to #apply_parent_to_existing_media - parent_delete: deletes media_reference_tag rows by tag_parent_id first Tests: - alias_delete undoes media_reference_tag changes - parent_delete undoes media_reference_tag changes - alias_delete does not remove manually-added tags - media.create with alias tracks tag_alias_id for undo - media.update with parent tracks tag_parent_id for undo Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
- Add comment that parent tag resolution does not resolve aliases - Wrap alias_create, alias_delete, parent_create, parent_delete in transactions for atomicity - Pass whole tag_alias/tag_parent records to #apply_alias_to_existing_media and #apply_parent_to_existing_media instead of positional args - Add PRAGMA foreign_key_check before COMMIT in migration v11 Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
…ngth is needed Add count_by_alias, count_by_alias_for to TagAlias model. Add count_children, count_parents to TagParent model. Replace list-then-count patterns in TagActions: - tag.search: use count queries for alias_count and parent_count - tag.update: use count queries to check for existing rules Remove unused listing methods: - TagAlias.select_all_by_alias_for (replaced by count_by_alias_for) - TagParent.select_children (replaced by count_children) Retain listing methods still needed for iteration: - TagParent.select_parents (used in BFS parent chain walking) - TagAlias.select_by_alias (used for alias resolution and error messages) Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
…leting alias_delete now reverses the alias migration: media_reference_tag rows that were created by the alias rule are moved back to point at the original alias tag (with tag_alias_id cleared), rather than being deleted entirely. This properly undoes the alias — media that was tagged with the alias before the rule was created gets its original tag association restored. Added select_all_by_tag_alias_id query to MediaReferenceTag model. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Replace onMount with $effect watching page.params.slug so the tag detail reloads when clicking tag links within the detail page. onMount only fires once on initial mount, so client-side navigations to a different /tags/[slug] were changing the URL but not reloading the data. Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
Use {#key slug} to destroy and recreate the entire page content when
the slug param changes. The controller is created fresh inside the
keyed block via {@const}, so all state (detail, draft, inputs) resets.
Moved input state (alias_input, child_input, parent_input) into the
controller so they're part of the fresh instance. Controller now
accepts slug in the constructor and loads immediately.
Co-authored-by: andykais-claude <andykais-claude@users.noreply.github.com>
… tags" This reverts commit 9d28cf1.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lessons learned from the tag management feature PR (#56) and its follow-up fix PR (#61), documented for future coding agents.
Changes
AGENTS.md
export constpattern, now uses the decorator class pattern actually used in the codebase)DROP COLUMNwith FK constraints,PRAGMA foreign_keysin transactions, trigger rebuilds when renaming tables, seed schema matching for the migration diff testsource/target)delete_unreferenced+ rule references)COUNTquery performance tippackages/core/.cursor/rules/core-patterns.md (new)
media_add_taghelper documentation (alias resolution + parent propagation)tag_alias_id/tag_parent_idtracking)PaginationVarspackages/web/.cursor/rules/web-patterns.md (new)
.svelte.tsextension requirement for runes (therune_outside_sveltegotcha){#key}pattern for page param reactivity (vsonMountvs$effect)display: contentsCSS pattern for grid-through-slot layoutsencodeURIComponenttag slugs