[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M79WPSWM8FY20RADE8EK7AWR":3,"skill-same:01M79WPSWM8FY20RADE8EK7AWR":47},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":24,"commitSha":25,"skillDirectory":26,"forksCount":27,"repositoryHtmlUrl":28,"repositoryHomepageUrl":6,"ownerHtmlUrl":29,"ownerBlogUrl":30,"ownerDescription":31,"updatedAt":32,"install":33,"resources":46},"01M79WPSWM8FY20RADE8EK7AWR","database-performance",null,"Database access patterns for performance. Separate read\u002Fwrite models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.","Blind-Striker\u002Fagent-skills-and-plugins","Blind-Striker","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F1965259?v=4","01M79WFXTE5PH0C1VB7B54ANMN","Deniz İrgin","01M79WFXV5KZ5GST1K8G035DM5",1,"MIT","https:\u002F\u002Fgithub.com\u002FBlind-Striker\u002Fagent-skills-and-plugins\u002Fblob\u002Fmaster\u002Fcodex\u002Fdeniz-dotnet-general\u002Fskills\u002Fdatabase-performance\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: database-performance\ndescription: Database access patterns for performance. Separate read\u002Fwrite\n  models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do\n  application-side joins. Works with EF Core and Dapper.\n---\n\n# Database Performance Patterns\n\n## When to Use This Skill\n\nUse this skill when:\n- Designing data access layers\n- Optimizing slow database queries\n- Choosing between EF Core and Dapper\n- Avoiding common performance pitfalls\n\n---\n\n## Core Principles\n\n1. **Separate read and write models** - Don't use the same types for both\n2. **Think in batches** - Avoid N+1 queries\n3. **Only retrieve what you need** - No SELECT *\n4. **Apply row limits** - Always have a configurable Take\u002FLimit\n5. **Do joins in SQL** - Never in application code\n6. **AsNoTracking for reads** - EF Core change tracking is expensive\n\n---\n\n## Read\u002FWrite Model Separation (CQRS Pattern)\n\n**Read and write models are fundamentally different - they have different shapes, columns, and purposes.** Don't create a single \"User\" entity and reuse it everywhere.\n\n- **Read models** are denormalized, optimized for query efficiency, and return multiple projection types (UserProfile, UserSummary, UserDetailForAdmin)\n- **Write models** are normalized, validation-focused, and accept strongly-typed commands (CreateUserCommand, UpdateUserCommand)\n\n### Architecture\n\n```\nsrc\u002F\n  MyApp.Data\u002F\n    Users\u002F\n      # Read side - multiple optimized projections\n      IUserReadStore.cs\n      PostgresUserReadStore.cs\n\n      # Write side - command handlers\n      IUserWriteStore.cs\n      PostgresUserWriteStore.cs\n\n      # Read DTOs - lightweight, denormalized\n      UserProfile.cs\n      UserSummary.cs\n\n      # Write commands - validation-focused\n      CreateUserCommand.cs\n      UpdateUserCommand.cs\n    Orders\u002F\n      IOrderReadStore.cs\n      IOrderWriteStore.cs\n      (similar structure...)\n```\n\n### Read Store Interface\n\n```csharp\n\u002F\u002F Read models: Multiple specialized projections optimized for different use cases\npublic interface IUserReadStore\n{\n    \u002F\u002F Returns detailed profile for single-user view\n    Task\u003CUserProfile?> GetByIdAsync(UserId id, CancellationToken ct = default);\n\n    \u002F\u002F Returns lightweight info for lookups\n    Task\u003CUserProfile?> GetByEmailAsync(EmailAddress email, CancellationToken ct = default);\n\n    \u002F\u002F Returns paginated summaries - only what the list view needs\n    Task\u003CIReadOnlyList\u003CUserSummary>> GetAllAsync(int limit, UserId? cursor = null, CancellationToken ct = default);\n\n    \u002F\u002F Boolean query - no entity needed\n    Task\u003Cbool> EmailExistsAsync(EmailAddress email, CancellationToken ct = default);\n}\n```\n\n### Write Store Interface\n\n```csharp\n\u002F\u002F Write model: Accepts strongly-typed commands, minimal return values\npublic interface IUserWriteStore\n{\n    \u002F\u002F Returns only the created ID - caller doesn't need the full entity\n    Task\u003CUserId> CreateAsync(CreateUserCommand command, CancellationToken ct = default);\n\n    \u002F\u002F Update validates command, returns void (success or throws)\n    Task UpdateAsync(UserId id, UpdateUserCommand command, CancellationToken ct = default);\n\n    \u002F\u002F Delete is simple and explicit\n    Task DeleteAsync(UserId id, CancellationToken ct = default);\n}\n```\n\n**Key structural differences illustrated:**\n- Read store returns multiple different DTOs (UserProfile, UserSummary, bool flag)\n- Write store returns minimal data (just UserId on create) or void\n- Read queries are stateless projections - no tracking needed\n- Write operations focus on command validation, not retrieving data afterwards\n- Different databases\u002Ftables can back read vs write (eventual consistency pattern)\n\n---\n\n## Always Apply Row Limits\n\n**Never return unbounded result sets.** Every read method should have a configurable limit.\n\n### Pattern: Limit Parameter\n\n```csharp\npublic interface IOrderReadStore\n{\n    \u002F\u002F Limit is required, not optional\n    Task\u003CIReadOnlyList\u003COrderSummary>> GetByCustomerAsync(\n        CustomerId customerId,\n        int limit,\n        OrderId? cursor = null,\n        CancellationToken ct = default);\n}\n\n\u002F\u002F Implementation\npublic async Task\u003CIReadOnlyList\u003COrderSummary>> GetByCustomerAsync(\n    CustomerId customerId,\n    int limit,\n    OrderId? cursor = null,\n    CancellationToken ct = default)\n{\n    await using var connection = await _dataSource.OpenConnectionAsync(ct);\n\n    const string sql = \"\"\"\n        SELECT id, customer_id, total, status, created_at\n        FROM orders\n        WHERE customer_id = @CustomerId\n        AND (@Cursor IS NULL OR created_at \u003C (SELECT created_at FROM orders WHERE id = @Cursor))\n        ORDER BY created_at DESC\n        LIMIT @Limit\n        \"\"\";\n\n    var rows = await connection.QueryAsync\u003COrderRow>(sql, new\n    {\n        CustomerId = customerId.Value,\n        Cursor = cursor?.Value,\n        Limit = limit\n    });\n\n    return rows.Select(r => r.ToOrderSummary()).ToList();\n}\n```\n\n### EF Core with Pagination\n\n```csharp\npublic async Task\u003CPaginatedList\u003COrderSummary>> GetOrdersAsync(\n    CustomerId customerId,\n    Paginator paginator,\n    CancellationToken ct = default)\n{\n    var query = _context.Orders\n        .AsNoTracking()\n        .Where(o => o.CustomerId == customerId.Value)\n        .OrderByDescending(o => o.CreatedAt);\n\n    var totalCount = await query.CountAsync(ct);\n\n    var orders = await query\n        .Skip((paginator.PageNumber - 1) * paginator.PageSize)\n        .Take(paginator.PageSize)  \u002F\u002F Always limit!\n        .Select(o => new OrderSummary(\n            new OrderId(o.Id),\n            o.Total,\n            o.Status,\n            o.CreatedAt))\n        .ToListAsync(ct);\n\n    return new PaginatedList\u003COrderSummary>(\n        orders,\n        totalCount,\n        paginator.PageSize,\n        paginator.PageNumber);\n}\n```\n\n### Prefer keyset over offset on deep pages\n\n`Skip`\u002F`Take` still scans and discards the skipped rows, so latency grows with the page number.\nPage by the last key seen instead:\n\n```csharp\ndb.Orders.Where(o => o.Id > lastSeenId).OrderBy(o => o.Id).Take(pageSize);\n```\n\nOrder by a unique, stable, indexed key, adding tie-breakers when the sort column is not unique.\nA composite cursor needs a lexicographic predicate, not independent bounds on each key: a descending\n`(CreatedAt, Id)` page continues with `CreatedAt \u003C lastDate || (CreatedAt == lastDate && Id \u003C lastId)`.\nKeep every ordering key with its direction, and add a secondary key with `ThenBy`, because a second\n`OrderBy` replaces the first.\nKeyset pages by the last key rather than a page number, so it changes the method's inputs; when a\nfixed signature rules out an in-place switch, still flag the deep-offset scan and recommend keyset.\n\n**Verify:** page latency stays roughly constant from early to deep pages.\n\n---\n\n## Keep Predicates Sargable\n\nAn index can only be used when the indexed column appears **bare** on one side of the comparison.\nWrapping it in a function or arithmetic — `CreatedAt.Year == y`, `ToLower(Name) == n`,\n`Price * 1.1 > x`, or a leading-wildcard `LIKE '%foo'` — forces a per-row computation that an\nordinary column index cannot satisfy, so the query usually **cannot seek even though the index\nexists**, and another ordinary index does not repair the expression. Do not assert a full-table scan\nfrom the LINQ alone: provider translations, computed or expression indexes, and other predicates can\nchange the plan. Rewrite the predicate so the column stays bare, usually as a half-open range:\n\n```csharp\n\u002F\u002F Non-sargable: a year is computed for every row instead of comparing the indexed date\ndb.Logs.Where(l => l.CreatedAt.Year == year);\n\n\u002F\u002F Sargable: a bare-column range an ordinary date index can seek\nvar start = new DateTime(year, 1, 1);\ndb.Logs.Where(l => l.CreatedAt >= start && l.CreatedAt \u003C start.AddYears(1));\n```\n\nThe same rule covers several common shapes:\n\n- **Case-insensitive text** — compare a stored normalized column, or use a compatible column\n  collation, instead of `ToLower(...)`\u002F`ToUpper(...)`. Relational providers generally do not\n  translate the `StringComparison` overloads, and current EF Core throws for such a predicate\n  rather than filtering on the client.\n- **Computed expressions** — compare against the precomputed constant, not `column * k > x`.\n- **Converting the column to another type** — a predicate over `column.ToString()` applies a\n  function to every row and may not translate to SQL at all; current EF Core then reports a\n  translation failure rather than silently evaluating it on the client. Filter on the typed column\n  with a real comparison or range.\n- **Substring search** — `name.Contains(term)` becomes an unanchored `LIKE '%term%'` that an\n  ordinary B-tree index cannot seek; a trailing wildcard (`name.StartsWith(term)` → `'term%'`) can.\n  Neither prefix matching nor token-based full-text search is a drop-in replacement for arbitrary\n  substring matching: keep the required search mode or state the change, and on large tables\n  consider a provider-appropriate substring index or search design.\n\nThis is a SQL-level rule, not an EF Core one: the same predicate written in Dapper scans for the same\nreason.\n\n**Verify:** the plan shows a seek instead of a scan and duration drops. If the column genuinely has\nno index, add one — but only after the predicate is sargable.\n\n---\n\n## AsNoTracking for Read Queries\n\nEF Core's change tracking is expensive. Disable it for read-only queries.\n\n```csharp\n\u002F\u002F DO: Disable tracking for reads\nvar users = await _context.Users\n    .AsNoTracking()\n    .Where(u => u.IsActive)\n    .ToListAsync();\n\n\u002F\u002F DON'T: Track entities you won't modify\nvar users = await _context.Users\n    .Where(u => u.IsActive)\n    .ToListAsync();  \u002F\u002F Change tracking enabled - wasteful\n```\n\n### Configure Default Behavior\n\n```csharp\n\u002F\u002F For read-heavy applications, consider this in DbContext\nprotected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)\n{\n    optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);\n}\n```\n\nThen explicitly enable tracking when needed:\n\n```csharp\nvar user = await _context.Users\n    .AsTracking()  \u002F\u002F Explicit - we intend to modify\n    .FirstOrDefaultAsync(u => u.Id == userId);\n```\n\n---\n\n## Avoid N+1 Queries\n\nThe N+1 problem: fetching a list, then querying for each item's related data.\n\n### The Problem\n\n```csharp\n\u002F\u002F BAD: N+1 queries\nvar orders = await _context.Orders.ToListAsync();\n\nforeach (var order in orders)\n{\n    \u002F\u002F Each iteration hits the database!\n    var items = await _context.OrderItems\n        .Where(i => i.OrderId == order.Id)\n        .ToListAsync();\n}\n```\n\n### Solution 1: Include (EF Core)\n\n```csharp\n\u002F\u002F GOOD: Single query with join\nvar orders = await _context.Orders\n    .AsNoTracking()\n    .Include(o => o.Items)\n    .ToListAsync();\n```\n\n### Solution 2: Batch Query (Dapper)\n\n```csharp\n\u002F\u002F GOOD: Two queries, no N+1\nconst string sql = \"\"\"\n    SELECT id, customer_id, total FROM orders WHERE customer_id = @CustomerId;\n    SELECT oi.* FROM order_items oi\n    INNER JOIN orders o ON oi.order_id = o.id\n    WHERE o.customer_id = @CustomerId;\n    \"\"\";\n\nusing var multi = await connection.QueryMultipleAsync(sql, new { CustomerId = customerId });\nvar orders = (await multi.ReadAsync\u003COrderRow>()).ToList();\nvar items = (await multi.ReadAsync\u003COrderItemRow>()).ToList();\n\n\u002F\u002F Join in memory (acceptable - data already fetched)\nforeach (var order in orders)\n{\n    order.Items = items.Where(i => i.OrderId == order.Id).ToList();\n}\n```\n\n---\n\n## Never Do Application-Side Joins\n\n**Joins must happen in SQL, not in C#.**\n\n```csharp\n\u002F\u002F BAD: Application join - two queries, memory waste\nvar customers = await _context.Customers.ToListAsync();\nvar orders = await _context.Orders.ToListAsync();\n\nvar result = customers.Select(c => new\n{\n    Customer = c,\n    Orders = orders.Where(o => o.CustomerId == c.Id).ToList()  \u002F\u002F O(n*m) in memory!\n});\n\n\u002F\u002F GOOD: SQL join - single query\nvar result = await _context.Customers\n    .AsNoTracking()\n    .Include(c => c.Orders)\n    .ToListAsync();\n\n\u002F\u002F GOOD: Explicit join (Dapper)\nconst string sql = \"\"\"\n    SELECT c.id, c.name, COUNT(o.id) as order_count\n    FROM customers c\n    LEFT JOIN orders o ON c.id = o.customer_id\n    GROUP BY c.id, c.name\n    \"\"\";\n```\n\n---\n\n## Avoid Cartesian Explosions\n\nMultiple `Include` calls can cause Cartesian products.\n\n```csharp\n\u002F\u002F DANGEROUS: Can explode into millions of rows\nvar product = await _context.Products\n    .Include(p => p.Reviews)      \u002F\u002F 100 reviews\n    .Include(p => p.Images)       \u002F\u002F 20 images\n    .Include(p => p.Categories)   \u002F\u002F 5 categories\n    .FirstOrDefaultAsync(p => p.Id == id);\n\u002F\u002F Result: 100 * 20 * 5 = 10,000 rows transferred!\n```\n\n### Solution: Split Queries\n\n```csharp\n\u002F\u002F GOOD: Multiple queries, no Cartesian explosion\nvar product = await _context.Products\n    .AsSplitQuery()\n    .Include(p => p.Reviews)\n    .Include(p => p.Images)\n    .Include(p => p.Categories)\n    .FirstOrDefaultAsync(p => p.Id == id);\n\u002F\u002F Result: 4 separate queries, ~125 rows total\n```\n\n### Solution: Explicit Projection\n\n```csharp\n\u002F\u002F BEST: Only fetch what you need\nvar product = await _context.Products\n    .AsNoTracking()\n    .Where(p => p.Id == id)\n    .Select(p => new ProductDetail(\n        p.Id,\n        p.Name,\n        p.Description,\n        p.Reviews.OrderByDescending(r => r.CreatedAt).Take(10).ToList(),\n        p.Images.Take(5).ToList(),\n        p.Categories.Select(c => c.Name).ToList()))\n    .FirstOrDefaultAsync();\n```\n\n---\n\n## Constrain Column Sizes\n\nDefine maximum lengths in your EF Core model to prevent oversized data.\n\n```csharp\npublic class UserConfiguration : IEntityTypeConfiguration\u003CUser>\n{\n    public void Configure(EntityTypeBuilder\u003CUser> builder)\n    {\n        builder.Property(u => u.Email)\n            .HasMaxLength(254)  \u002F\u002F RFC 5321 limit\n            .IsRequired();\n\n        builder.Property(u => u.Name)\n            .HasMaxLength(100)\n            .IsRequired();\n\n        builder.Property(u => u.Bio)\n            .HasMaxLength(500);\n\n        \u002F\u002F For truly large content, use text type explicitly\n        builder.Property(u => u.Notes)\n            .HasColumnType(\"text\");\n    }\n}\n```\n\n---\n\n## Don't Build Generic Repositories\n\nGeneric repositories hide query complexity and make optimization difficult.\n\n```csharp\n\u002F\u002F BAD: Generic repository\npublic interface IRepository\u003CT>\n{\n    Task\u003CT?> GetByIdAsync(int id);\n    Task\u003CIEnumerable\u003CT>> GetAllAsync();  \u002F\u002F No limit!\n    Task\u003CIEnumerable\u003CT>> FindAsync(Expression\u003CFunc\u003CT, bool>> predicate);  \u002F\u002F Can't optimize\n}\n\n\u002F\u002F GOOD: Purpose-built read stores\npublic interface IOrderReadStore\n{\n    Task\u003COrderDetail?> GetByIdAsync(OrderId id, CancellationToken ct = default);\n    Task\u003CIReadOnlyList\u003COrderSummary>> GetByCustomerAsync(CustomerId id, int limit, CancellationToken ct = default);\n    Task\u003CIReadOnlyList\u003COrderSummary>> GetPendingAsync(int limit, CancellationToken ct = default);\n}\n```\n\n**Problems with generic repositories:**\n- Can't optimize specific queries\n- No way to enforce limits\n- Hide N+1 problems\n- Make it easy to fetch too much data\n- Encourage lazy thinking about data access\n\n---\n\n## Dapper for Read-Heavy Workloads\n\nFor complex read queries, Dapper with explicit SQL is often cleaner and faster.\n\n```csharp\npublic sealed class PostgresUserReadStore : IUserReadStore\n{\n    private readonly NpgsqlDataSource _dataSource;\n\n    public PostgresUserReadStore(NpgsqlDataSource dataSource)\n    {\n        _dataSource = dataSource;\n    }\n\n    public async Task\u003CUserProfile?> GetByIdAsync(UserId id, CancellationToken ct = default)\n    {\n        await using var connection = await _dataSource.OpenConnectionAsync(ct);\n\n        const string sql = \"\"\"\n            SELECT id, email, name, bio, created_at\n            FROM users\n            WHERE id = @Id\n            \"\"\";\n\n        var row = await connection.QuerySingleOrDefaultAsync\u003CUserRow>(\n            sql, new { Id = id.Value });\n\n        return row?.ToUserProfile();\n    }\n\n    \u002F\u002F Internal row type for Dapper mapping\n    private sealed class UserRow\n    {\n        public Guid id { get; set; }\n        public string email { get; set; } = null!;\n        public string name { get; set; } = null!;\n        public string? bio { get; set; }\n        public DateTime created_at { get; set; }\n\n        public UserProfile ToUserProfile() => new(\n            Id: new UserId(id),\n            Email: new EmailAddress(email),\n            Name: new PersonName(name),\n            Bio: bio,\n            CreatedAt: new DateTimeOffset(created_at, TimeSpan.Zero));\n    }\n}\n```\n\n---\n\n## When to Use EF Core vs Dapper\n\n| Scenario | Recommendation |\n|----------|---------------|\n| Simple CRUD | EF Core |\n| Complex read queries | Dapper |\n| Writes with validation | EF Core |\n| Bulk operations | Dapper or raw SQL |\n| Reporting\u002Fanalytics | Dapper |\n| Domain-heavy writes | EF Core |\n\nYou can use both in the same project - EF Core for writes, Dapper for reads.\n\n---\n\n## Quick Reference\n\n| Anti-Pattern | Solution |\n|--------------|----------|\n| No row limit | Add `limit` parameter to every read method |\n| SELECT * | Project only needed columns |\n| N+1 queries | Use Include or batch queries |\n| Application joins | Do joins in SQL |\n| Cartesian explosion | Use AsSplitQuery or projection |\n| Tracking read-only data | Use AsNoTracking |\n| Generic repository | Purpose-built read\u002Fwrite stores |\n| Unbounded strings | Configure MaxLength in model |\n\n---\n\n## Resources\n\n- **EF Core Performance**: https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fef\u002Fcore\u002Fperformance\u002F\n- **Dapper**: https:\u002F\u002Fgithub.com\u002FDapperLib\u002FDapper\n- **AsSplitQuery**: https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fef\u002Fcore\u002Fquerying\u002Fsingle-split-queries\n","# Database Performance Patterns\n\n## When to Use This Skill\n\nUse this skill when:\n- Designing data access layers\n- Optimizing slow database queries\n- Choosing between EF Core and Dapper\n- Avoiding common performance pitfalls\n\n---\n\n## Core Principles\n\n1. **Separate read and write models** - Don't use the same types for both\n2. **Think in batches** - Avoid N+1 queries\n3. **Only retrieve what you need** - No SELECT *\n4. **Apply row limits** - Always have a configurable Take\u002FLimit\n5. **Do joins in SQL** - Never in application code\n6. **AsNoTracking for reads** - EF Core change tracking is expensive\n\n---\n\n## Read\u002FWrite Model Separation (CQRS Pattern)\n\n**Read and write models are fundamentally different - they have different shapes, columns, and purposes.** Don't create a single \"User\" entity and reuse it everywhere.\n\n- **Read models** are denormalized, optimized for query efficiency, and return multiple projection types (UserProfile, UserSummary, UserDetailForAdmin)\n- **Write models** are normalized, validation-focused, and accept strongly-typed commands (CreateUserCommand, UpdateUserCommand)\n\n### Architecture\n\n```\nsrc\u002F\n  MyApp.Data\u002F\n    Users\u002F\n      # Read side - multiple optimized projections\n      IUserReadStore.cs\n      PostgresUserReadStore.cs\n\n      # Write side - command handlers\n      IUserWriteStore.cs\n      PostgresUserWriteStore.cs\n\n      # Read DTOs - lightweight, denormalized\n      UserProfile.cs\n      UserSummary.cs\n\n      # Write commands - validation-focused\n      CreateUserCommand.cs\n      UpdateUserCommand.cs\n    Orders\u002F\n      IOrderReadStore.cs\n      IOrderWriteStore.cs\n      (similar structure...)\n```\n\n### Read Store Interface\n\n```csharp\n\u002F\u002F Read models: Multiple specialized projections optimized for different use cases\npublic interface IUserReadStore\n{\n    \u002F\u002F Returns detailed profile for single-user view\n    Task\u003CUserProfile?> GetByIdAsync(UserId id, CancellationToken ct = default);\n\n    \u002F\u002F Returns lightweight info for lookups\n    Task\u003CUserProfile?> GetByEmailAsync(EmailAddress email, CancellationToken ct = default);\n\n    \u002F\u002F Returns paginated summaries - only what the list view needs\n    Task\u003CIReadOnlyList\u003CUserSummary>> GetAllAsync(int limit, UserId? cursor = null, CancellationToken ct = default);\n\n    \u002F\u002F Boolean query - no entity needed\n    Task\u003Cbool> EmailExistsAsync(EmailAddress email, CancellationToken ct = default);\n}\n```\n\n### Write Store Interface\n\n```csharp\n\u002F\u002F Write model: Accepts strongly-typed commands, minimal return values\npublic interface IUserWriteStore\n{\n    \u002F\u002F Returns only the created ID - caller doesn't need the full entity\n    Task\u003CUserId> CreateAsync(CreateUserCommand command, CancellationToken ct = default);\n\n    \u002F\u002F Update validates command, returns void (success or throws)\n    Task UpdateAsync(UserId id, UpdateUserCommand command, CancellationToken ct = default);\n\n    \u002F\u002F Delete is simple and explicit\n    Task DeleteAsync(UserId id, CancellationToken ct = default);\n}\n```\n\n**Key structural differences illustrated:**\n- Read store returns multiple different DTOs (UserProfile, UserSummary, bool flag)\n- Write store returns minimal data (just UserId on create) or void\n- Read queries are stateless projections - no tracking needed\n- Write operations focus on command validation, not retrieving data afterwards\n- Different databases\u002Ftables can back read vs write (eventual consistency pattern)\n\n---\n\n## Always Apply Row Limits\n\n**Never return unbounded result sets.** Every read method should have a configurable limit.\n\n### Pattern: Limit Parameter\n\n```csharp\npublic interface IOrderReadStore\n{\n    \u002F\u002F Limit is required, not optional\n    Task\u003CIReadOnlyList\u003COrderSummary>> GetByCustomerAsync(\n        CustomerId customerId,\n        int limit,\n        OrderId? cursor = null,\n        CancellationToken ct = default);\n}\n\n\u002F\u002F Implementation\npublic async Task\u003CIReadOnlyList\u003COrderSummary>> GetByCustomerAsync(\n    CustomerId customerId,\n    int limit,\n    OrderId? cursor = null,\n    CancellationToken ct = default)\n{\n    await using var connection = await _dataSource.OpenConnectionAsync(ct);\n\n    const string sql = \"\"\"\n        SELECT id, customer_id, total, status, created_at\n        FROM orders\n        WHERE customer_id = @CustomerId\n        AND (@Cursor IS NULL OR created_at \u003C (SELECT created_at FROM orders WHERE id = @Cursor))\n        ORDER BY created_at DESC\n        LIMIT @Limit\n        \"\"\";\n\n    var rows = await connection.QueryAsync\u003COrderRow>(sql, new\n    {\n        CustomerId = customerId.Value,\n        Cursor = cursor?.Value,\n        Limit = limit\n    });\n\n    return rows.Select(r => r.ToOrderSummary()).ToList();\n}\n```\n\n### EF Core with Pagination\n\n```csharp\npublic async Task\u003CPaginatedList\u003COrderSummary>> GetOrdersAsync(\n    CustomerId customerId,\n    Paginator paginator,\n    CancellationToken ct = default)\n{\n    var query = _context.Orders\n        .AsNoTracking()\n        .Where(o => o.CustomerId == customerId.Value)\n        .OrderByDescending(o => o.CreatedAt);\n\n    var totalCount = await query.CountAsync(ct);\n\n    var orders = await query\n        .Skip((paginator.PageNumber - 1) * paginator.PageSize)\n        .Take(paginator.PageSize)  \u002F\u002F Always limit!\n        .Select(o => new OrderSummary(\n            new OrderId(o.Id),\n            o.Total,\n            o.Status,\n            o.CreatedAt))\n        .ToListAsync(ct);\n\n    return new PaginatedList\u003COrderSummary>(\n        orders,\n        totalCount,\n        paginator.PageSize,\n        paginator.PageNumber);\n}\n```\n\n### Prefer keyset over offset on deep pages\n\n`Skip`\u002F`Take` still scans and discards the skipped rows, so latency grows with the page number.\nPage by the last key seen instead:\n\n```csharp\ndb.Orders.Where(o => o.Id > lastSeenId).OrderBy(o => o.Id).Take(pageSize);\n```\n\nOrder by a unique, stable, indexed key, adding tie-breakers when the sort column is not unique.\nA composite cursor needs a lexicographic predicate, not independent bounds on each key: a descending\n`(CreatedAt, Id)` page continues with `CreatedAt \u003C lastDate || (CreatedAt == lastDate && Id \u003C lastId)`.\nKeep every ordering key with its direction, and add a secondary key with `ThenBy`, because a second\n`OrderBy` replaces the first.\nKeyset pages by the last key rather than a page number, so it changes the method's inputs; when a\nfixed signature rules out an in-place switch, still flag the deep-offset scan and recommend keyset.\n\n**Verify:** page latency stays roughly constant from early to deep pages.\n\n---\n\n## Keep Predicates Sargable\n\nAn index can only be used when the indexed column appears **bare** on one side of the comparison.\nWrapping it in a function or arithmetic — `CreatedAt.Year == y`, `ToLower(Name) == n`,\n`Price * 1.1 > x`, or a leading-wildcard `LIKE '%foo'` — forces a per-row computation that an\nordinary column index cannot satisfy, so the query usually **cannot seek even though the index\nexists**, and another ordinary index does not repair the expression. Do not assert a full-table scan\nfrom the LINQ alone: provider translations, computed or expression indexes, and other predicates can\nchange the plan. Rewrite the predicate so the column stays bare, usually as a half-open range:\n\n```csharp\n\u002F\u002F Non-sargable: a year is computed for every row instead of comparing the indexed date\ndb.Logs.Where(l => l.CreatedAt.Year == year);\n\n\u002F\u002F Sargable: a bare-column range an ordinary date index can seek\nvar start = new DateTime(year, 1, 1);\ndb.Logs.Where(l => l.CreatedAt >= start && l.CreatedAt \u003C start.AddYears(1));\n```\n\nThe same rule covers several common shapes:\n\n- **Case-insensitive text** — compare a stored normalized column, or use a compatible column\n  collation, instead of `ToLower(...)`\u002F`ToUpper(...)`. Relational providers generally do not\n  translate the `StringComparison` overloads, and current EF Core throws for such a predicate\n  rather than filtering on the client.\n- **Computed expressions** — compare against the precomputed constant, not `column * k > x`.\n- **Converting the column to another type** — a predicate over `column.ToString()` applies a\n  function to every row and may not translate to SQL at all; current EF Core then reports a\n  translation failure rather than silently evaluating it on the client. Filter on the typed column\n  with a real comparison or range.\n- **Substring search** — `name.Contains(term)` becomes an unanchored `LIKE '%term%'` that an\n  ordinary B-tree index cannot seek; a trailing wildcard (`name.StartsWith(term)` → `'term%'`) can.\n  Neither prefix matching nor token-based full-text search is a drop-in replacement for arbitrary\n  substring matching: keep the required search mode or state the change, and on large tables\n  consider a provider-appropriate substring index or search design.\n\nThis is a SQL-level rule, not an EF Core one: the same predicate written in Dapper scans for the same\nreason.\n\n**Verify:** the plan shows a seek instead of a scan and duration drops. If the column genuinely has\nno index, add one — but only after the predicate is sargable.\n\n---\n\n## AsNoTracking for Read Queries\n\nEF Core's change tracking is expensive. Disable it for read-only queries.\n\n```csharp\n\u002F\u002F DO: Disable tracking for reads\nvar users = await _context.Users\n    .AsNoTracking()\n    .Where(u => u.IsActive)\n    .ToListAsync();\n\n\u002F\u002F DON'T: Track entities you won't modify\nvar users = await _context.Users\n    .Where(u => u.IsActive)\n    .ToListAsync();  \u002F\u002F Change tracking enabled - wasteful\n```\n\n### Configure Default Behavior\n\n```csharp\n\u002F\u002F For read-heavy applications, consider this in DbContext\nprotected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)\n{\n    optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);\n}\n```\n\nThen explicitly enable tracking when needed:\n\n```csharp\nvar user = await _context.Users\n    .AsTracking()  \u002F\u002F Explicit - we intend to modify\n    .FirstOrDefaultAsync(u => u.Id == userId);\n```\n\n---\n\n## Avoid N+1 Queries\n\nThe N+1 problem: fetching a list, then querying for each item's related data.\n\n### The Problem\n\n```csharp\n\u002F\u002F BAD: N+1 queries\nvar orders = await _context.Orders.ToListAsync();\n\nforeach (var order in orders)\n{\n    \u002F\u002F Each iteration hits the database!\n    var items = await _context.OrderItems\n        .Where(i => i.OrderId == order.Id)\n        .ToListAsync();\n}\n```\n\n### Solution 1: Include (EF Core)\n\n```csharp\n\u002F\u002F GOOD: Single query with join\nvar orders = await _context.Orders\n    .AsNoTracking()\n    .Include(o => o.Items)\n    .ToListAsync();\n```\n\n### Solution 2: Batch Query (Dapper)\n\n```csharp\n\u002F\u002F GOOD: Two queries, no N+1\nconst string sql = \"\"\"\n    SELECT id, customer_id, total FROM orders WHERE customer_id = @CustomerId;\n    SELECT oi.* FROM order_items oi\n    INNER JOIN orders o ON oi.order_id = o.id\n    WHERE o.customer_id = @CustomerId;\n    \"\"\";\n\nusing var multi = await connection.QueryMultipleAsync(sql, new { CustomerId = customerId });\nvar orders = (await multi.ReadAsync\u003COrderRow>()).ToList();\nvar items = (await multi.ReadAsync\u003COrderItemRow>()).ToList();\n\n\u002F\u002F Join in memory (acceptable - data already fetched)\nforeach (var order in orders)\n{\n    order.Items = items.Where(i => i.OrderId == order.Id).ToList();\n}\n```\n\n---\n\n## Never Do Application-Side Joins\n\n**Joins must happen in SQL, not in C#.**\n\n```csharp\n\u002F\u002F BAD: Application join - two queries, memory waste\nvar customers = await _context.Customers.ToListAsync();\nvar orders = await _context.Orders.ToListAsync();\n\nvar result = customers.Select(c => new\n{\n    Customer = c,\n    Orders = orders.Where(o => o.CustomerId == c.Id).ToList()  \u002F\u002F O(n*m) in memory!\n});\n\n\u002F\u002F GOOD: SQL join - single query\nvar result = await _context.Customers\n    .AsNoTracking()\n    .Include(c => c.Orders)\n    .ToListAsync();\n\n\u002F\u002F GOOD: Explicit join (Dapper)\nconst string sql = \"\"\"\n    SELECT c.id, c.name, COUNT(o.id) as order_count\n    FROM customers c\n    LEFT JOIN orders o ON c.id = o.customer_id\n    GROUP BY c.id, c.name\n    \"\"\";\n```\n\n---\n\n## Avoid Cartesian Explosions\n\nMultiple `Include` calls can cause Cartesian products.\n\n```csharp\n\u002F\u002F DANGEROUS: Can explode into millions of rows\nvar product = await _context.Products\n    .Include(p => p.Reviews)      \u002F\u002F 100 reviews\n    .Include(p => p.Images)       \u002F\u002F 20 images\n    .Include(p => p.Categories)   \u002F\u002F 5 categories\n    .FirstOrDefaultAsync(p => p.Id == id);\n\u002F\u002F Result: 100 * 20 * 5 = 10,000 rows transferred!\n```\n\n### Solution: Split Queries\n\n```csharp\n\u002F\u002F GOOD: Multiple queries, no Cartesian explosion\nvar product = await _context.Products\n    .AsSplitQuery()\n    .Include(p => p.Reviews)\n    .Include(p => p.Images)\n    .Include(p => p.Categories)\n    .FirstOrDefaultAsync(p => p.Id == id);\n\u002F\u002F Result: 4 separate queries, ~125 rows total\n```\n\n### Solution: Explicit Projection\n\n```csharp\n\u002F\u002F BEST: Only fetch what you need\nvar product = await _context.Products\n    .AsNoTracking()\n    .Where(p => p.Id == id)\n    .Select(p => new ProductDetail(\n        p.Id,\n        p.Name,\n        p.Description,\n        p.Reviews.OrderByDescending(r => r.CreatedAt).Take(10).ToList(),\n        p.Images.Take(5).ToList(),\n        p.Categories.Select(c => c.Name).ToList()))\n    .FirstOrDefaultAsync();\n```\n\n---\n\n## Constrain Column Sizes\n\nDefine maximum lengths in your EF Core model to prevent oversized data.\n\n```csharp\npublic class UserConfiguration : IEntityTypeConfiguration\u003CUser>\n{\n    public void Configure(EntityTypeBuilder\u003CUser> builder)\n    {\n        builder.Property(u => u.Email)\n            .HasMaxLength(254)  \u002F\u002F RFC 5321 limit\n            .IsRequired();\n\n        builder.Property(u => u.Name)\n            .HasMaxLength(100)\n            .IsRequired();\n\n        builder.Property(u => u.Bio)\n            .HasMaxLength(500);\n\n        \u002F\u002F For truly large content, use text type explicitly\n        builder.Property(u => u.Notes)\n            .HasColumnType(\"text\");\n    }\n}\n```\n\n---\n\n## Don't Build Generic Repositories\n\nGeneric repositories hide query complexity and make optimization difficult.\n\n```csharp\n\u002F\u002F BAD: Generic repository\npublic interface IRepository\u003CT>\n{\n    Task\u003CT?> GetByIdAsync(int id);\n    Task\u003CIEnumerable\u003CT>> GetAllAsync();  \u002F\u002F No limit!\n    Task\u003CIEnumerable\u003CT>> FindAsync(Expression\u003CFunc\u003CT, bool>> predicate);  \u002F\u002F Can't optimize\n}\n\n\u002F\u002F GOOD: Purpose-built read stores\npublic interface IOrderReadStore\n{\n    Task\u003COrderDetail?> GetByIdAsync(OrderId id, CancellationToken ct = default);\n    Task\u003CIReadOnlyList\u003COrderSummary>> GetByCustomerAsync(CustomerId id, int limit, CancellationToken ct = default);\n    Task\u003CIReadOnlyList\u003COrderSummary>> GetPendingAsync(int limit, CancellationToken ct = default);\n}\n```\n\n**Problems with generic repositories:**\n- Can't optimize specific queries\n- No way to enforce limits\n- Hide N+1 problems\n- Make it easy to fetch too much data\n- Encourage lazy thinking about data access\n\n---\n\n## Dapper for Read-Heavy Workloads\n\nFor complex read queries, Dapper with explicit SQL is often cleaner and faster.\n\n```csharp\npublic sealed class PostgresUserReadStore : IUserReadStore\n{\n    private readonly NpgsqlDataSource _dataSource;\n\n    public PostgresUserReadStore(NpgsqlDataSource dataSource)\n    {\n        _dataSource = dataSource;\n    }\n\n    public async Task\u003CUserProfile?> GetByIdAsync(UserId id, CancellationToken ct = default)\n    {\n        await using var connection = await _dataSource.OpenConnectionAsync(ct);\n\n        const string sql = \"\"\"\n            SELECT id, email, name, bio, created_at\n            FROM users\n            WHERE id = @Id\n            \"\"\";\n\n        var row = await connection.QuerySingleOrDefaultAsync\u003CUserRow>(\n            sql, new { Id = id.Value });\n\n        return row?.ToUserProfile();\n    }\n\n    \u002F\u002F Internal row type for Dapper mapping\n    private sealed class UserRow\n    {\n        public Guid id { get; set; }\n        public string email { get; set; } = null!;\n        public string name { get; set; } = null!;\n        public string? bio { get; set; }\n        public DateTime created_at { get; set; }\n\n        public UserProfile ToUserProfile() => new(\n            Id: new UserId(id),\n            Email: new EmailAddress(email),\n            Name: new PersonName(name),\n            Bio: bio,\n            CreatedAt: new DateTimeOffset(created_at, TimeSpan.Zero));\n    }\n}\n```\n\n---\n\n## When to Use EF Core vs Dapper\n\n| Scenario | Recommendation |\n|----------|---------------|\n| Simple CRUD | EF Core |\n| Complex read queries | Dapper |\n| Writes with validation | EF Core |\n| Bulk operations | Dapper or raw SQL |\n| Reporting\u002Fanalytics | Dapper |\n| Domain-heavy writes | EF Core |\n\nYou can use both in the same project - EF Core for writes, Dapper for reads.\n\n---\n\n## Quick Reference\n\n| Anti-Pattern | Solution |\n|--------------|----------|\n| No row limit | Add `limit` parameter to every read method |\n| SELECT * | Project only needed columns |\n| N+1 queries | Use Include or batch queries |\n| Application joins | Do joins in SQL |\n| Cartesian explosion | Use AsSplitQuery or projection |\n| Tracking read-only data | Use AsNoTracking |\n| Generic repository | Purpose-built read\u002Fwrite stores |\n| Unbounded strings | Configure MaxLength in model |\n\n---\n\n## Resources\n\n- **EF Core Performance**: https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fef\u002Fcore\u002Fperformance\u002F\n- **Dapper**: https:\u002F\u002Fgithub.com\u002FDapperLib\u002FDapper\n- **AsSplitQuery**: https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fef\u002Fcore\u002Fquerying\u002Fsingle-split-queries\n",{"name":5,"description":7},"master","b30b1c1908dbc56a8184128f18803053d762ccb5","codex\u002Fdeniz-dotnet-general\u002Fskills\u002Fdatabase-performance",0,"https:\u002F\u002Fgithub.com\u002FBlind-Striker\u002Fagent-skills-and-plugins","https:\u002F\u002Fgithub.com\u002FBlind-Striker","denizirgin.com","VP of Engineering - \r\n@armutcom @homeruntech @prontopro\r\n |  Speaker \r\n@CodeFiction\r\n, http:\u002F\u002Fcodefiction.tech | Developer, gamer, blogger, maiden iron fan","2026-11-13 00:23:18",{"source":8,"skillName":5,"commands":34,"prompt":44,"zipAvailable":45},[35,38,41],{"tool":36,"command":37},"npx","npx skills add Blind-Striker\u002Fagent-skills-and-plugins --skill database-performance",{"tool":39,"command":40},"bunx","bunx skills add Blind-Striker\u002Fagent-skills-and-plugins --skill database-performance",{"tool":42,"command":43},"pnpm","pnpm dlx skills add Blind-Striker\u002Fagent-skills-and-plugins --skill database-performance","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FBlind-Striker\u002Fagent-skills-and-plugins\u002Fblob\u002Fmaster\u002Fcodex\u002Fdeniz-dotnet-general\u002Fskills\u002Fdatabase-performance\u002FSKILL.md",true,[],[48,55,62,69,76,83],{"skillNo":49,"name":50,"nameZh":6,"description":51,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":52,"categories":53,"keywords":54,"publishedAt":19},"01M79YGWBJ52PHMSTKJ3RJ3AWR","writing-tunit-tests","Write tests in TUnit — the attributes, the async assertion model, data-driven cases, lifecycle hooks, and the parallelism rules. USE FOR writing or fixing a test in a TUnit project, translating a test idea into TUnit syntax, or reviewing TUnit test code for API mistakes. DO NOT USE FOR deciding what to test, judging whether a test is any good, or running tests.","https:\u002F\u002Fgithub.com\u002FBlind-Striker\u002Fagent-skills-and-plugins\u002Fblob\u002Fmaster\u002Fskills\u002Fdeniz-dotnet-general\u002Fwriting-tunit-tests\u002FSKILL.md",[],[],{"skillNo":56,"name":57,"nameZh":6,"description":58,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":59,"categories":60,"keywords":61,"publishedAt":19},"01M79YGR3Y9RCRS6TD71D0KKP8","dotnet-walls","Walls for a .NET change in a repository without its own quality gates — pinned, ephemeral analyzer, format, clone and scanner commands for the verify step, their identity ratchet, and the build traps. Use during a walls preflight, or when a worker's verify step needs .NET quality gates.","https:\u002F\u002Fgithub.com\u002FBlind-Striker\u002Fagent-skills-and-plugins\u002Fblob\u002Fmaster\u002Fskills\u002Fdeniz-dotnet-general\u002Fdotnet-walls\u002FSKILL.md",[],[],{"skillNo":63,"name":64,"nameZh":6,"description":65,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":66,"categories":67,"keywords":68,"publishedAt":19},"01M79YGFNJBST7D803NQW3SBYM","csharp-15","Write, review and modernize C# 15 (.NET 11) code correctly. Covers every C# 15 feature - collection expression arguments (`[with(...), ...]`), union types (`union`, `[Union]`, `IUnion`, custom unions), closed class hierarchies (`closed`), extension indexers, labeled `break`\u002F`continue`, non-virtual static interface members on runtimes without default interface methods, and the preview memory-safety model (`unsafe(expr)`, `safe`, requires-unsafe members). Use when code targets net11.0 or LangVersion 15\u002Fpreview, when the user mentions any of these features, when replacing marker interfaces, OneOf-style results, `goto`\u002Fflag loop exits or capacity\u002Fcomparer boilerplate, or when the build reports CS8121, CS8509, CS8655, CS8510, CS0121\u002FCS0411 on a `with(...)` element, CS9370, CS9380 or CS9363.","https:\u002F\u002Fgithub.com\u002FBlind-Striker\u002Fagent-skills-and-plugins\u002Fblob\u002Fmaster\u002Fskills\u002Fdeniz-dotnet-general\u002Fcsharp-15\u002FSKILL.md",[],[],{"skillNo":70,"name":71,"nameZh":6,"description":72,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":73,"categories":74,"keywords":75,"publishedAt":19},"01M79YGAV031473HSX6FN87ENH","verified-waves","Run one task as verified multi-agent waves. Plans the fan-out, sends findings to fresh skeptics, keeps a ledger, and reports N of M done; small tasks stay solo.","https:\u002F\u002Fgithub.com\u002FBlind-Striker\u002Fagent-skills-and-plugins\u002Fblob\u002Fmaster\u002Fskills\u002Fdeniz-coordination\u002Fverified-waves\u002FSKILL.md",[],[],{"skillNo":77,"name":78,"nameZh":6,"description":79,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":80,"categories":81,"keywords":82,"publishedAt":19},"01M79YG6QXNND4WRPP3GATSY8J","harness","The bundled dispatch harness, which runs a task file on worker models through OpenCode or Codex, fences their writes, escalates their asks to the operator, settles each result from git and its verify commands, and records every step in a ledger. Its commands, task file, modes and write fence, configuration, runs and their recovery, ledger and exit codes.","https:\u002F\u002Fgithub.com\u002FBlind-Striker\u002Fagent-skills-and-plugins\u002Fblob\u002Fmaster\u002Fskills\u002Fdeniz-coordination\u002Fharness\u002FSKILL.md",[],[],{"skillNo":84,"name":85,"nameZh":6,"description":86,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":89,"publishedAt":19},"01M79YG2P5S91SDTA5912XJ4FV","delegating-to-worker-models","Use when you hand off implementation, a plan, research a decision will rely on, or a review, and you brief the worker and check what comes back. A subagent that searches, reads in parallel, or keeps your context small is ordinary tool use. Covers what stays with you, the mechanism, assigning work by strength, the walls preflight, prompts without contradictions, the worker contract and model profiles, evidence from your own run, review, and the fix-up loop.","https:\u002F\u002Fgithub.com\u002FBlind-Striker\u002Fagent-skills-and-plugins\u002Fblob\u002Fmaster\u002Fskills\u002Fdeniz-coordination\u002Fdelegating-to-worker-models\u002FSKILL.md",[],[]]