feat: estructura base OpenSpec + Skills + arquitectura limpia Capacitor+Dextie
This commit is contained in:
97
skills/toldo-architecture-solid/SKILL.md
Normal file
97
skills/toldo-architecture-solid/SKILL.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Agent Skill: toldo-architecture-solid
|
||||
|
||||
## Metadata
|
||||
```yaml
|
||||
name: toldo-architecture-solid
|
||||
description: Guía para mantener arquitectura limpia con SOLID, separación lógica/presentación, componentes pequeños/reutilizables y DI. Aplicar estrictamente en este proyecto.
|
||||
```
|
||||
|
||||
## When to use
|
||||
Use this skill when:
|
||||
- Creating new packages/modules
|
||||
- Designing components or services
|
||||
- Reviewing code for architectural violations
|
||||
- Refactoring to improve structure
|
||||
- Deciding where code should live
|
||||
|
||||
## Architecture Layers
|
||||
|
||||
### Layer Responsibilities
|
||||
| Layer | Location | Responsibility | Can Import | Cannot Import |
|
||||
|-------|----------|----------------|------------|---------------|
|
||||
| **Domain** | `packages/core/` | Entities, value objects, business rules, repository interfaces | Nothing (pure TS) | data, services, ui, apps, @capacitor/*, frameworks |
|
||||
| **Infrastructure/Data** | `packages/data/` | Repository implementations, DB schema/migrations, external adapters | core/ | services, ui, apps, @capacitor/* in business logic |
|
||||
| **Application/Services** | `packages/services/` | Use cases, orchestration, application logic | core/, data/ | ui, apps (business logic), @capacitor/* directly |
|
||||
| **Presentation/UI** | `packages/ui/` | Reusable UI components, design system | core/, services/ via interfaces | data/ directly, @capacitor/* directly |
|
||||
| **App/Composition** | `apps/toldo-app/` | Composition root, routing, DI setup, Capacitor integration | All packages | - (can import anything, but keep orchestration here) |
|
||||
|
||||
## SOLID Checklist
|
||||
|
||||
### Single Responsibility Principle (SRP)
|
||||
- [ ] Each class/function does one thing
|
||||
- [ ] Components have single purpose
|
||||
- [ ] Services handle one domain area or specific use case
|
||||
- [ ] Files are focused (< ~200 lines ideally, < 150 for components)
|
||||
|
||||
### Open/Closed Principle (OCP)
|
||||
- [ ] Use interfaces for extensibility
|
||||
- [ ] Prefer composition over inheritance
|
||||
- [ ] New features via new classes/functions, not modifying existing tested code
|
||||
|
||||
### Liskov Substitution Principle (LSP)
|
||||
- [ ] Implementations are substitutable for interfaces
|
||||
- [ ] No breaking contracts
|
||||
|
||||
### Interface Segregation Principle (ISP)
|
||||
- [ ] Small, focused interfaces
|
||||
- [ ] Repositories have specific methods, not "god" interfaces
|
||||
- [ ] Clients don't depend on unused methods
|
||||
|
||||
### Dependency Inversion Principle (DIP)
|
||||
- [ ] Core defines interfaces (ports)
|
||||
- [ ] Data/services implement/adapt (adapters)
|
||||
- [ ] Depend on abstractions, inject dependencies
|
||||
- [ ] No `new` of concrete implementations deep in business logic
|
||||
|
||||
## Component Guidelines
|
||||
|
||||
### Component Rules
|
||||
- **Small and focused**: One responsibility per component
|
||||
- **Presentation-first**: Minimal logic, delegate to composables/services
|
||||
- **Props down, events up**: Clear data flow
|
||||
- **Reusable**: Generic where possible, specific where needed
|
||||
- **No business rules**: Calculations/validations belong in core/services
|
||||
- **No direct DB access**: Use services through DI/composition
|
||||
- **No direct Capacitor imports**: Go through adapters/services
|
||||
|
||||
### Composables/Hooks
|
||||
- Extract reusable logic to composables/hooks
|
||||
- Keep UI concerns separate from business logic
|
||||
- Make testable in isolation
|
||||
|
||||
## Anti-Patterns to Avoid
|
||||
|
||||
- ❌ Importing Dexie directly in UI components
|
||||
- ❌ Importing @capacitor/* in core/services/ui packages
|
||||
- ❌ Business logic in Vue/React/Svelte components
|
||||
- ❌ God classes/components doing too much
|
||||
- ❌ Circular dependencies
|
||||
- ❌ `any` types
|
||||
- ❌ Tight coupling to frameworks in domain
|
||||
|
||||
## Review Checklist
|
||||
|
||||
Before committing:
|
||||
- [ ] Dependencies follow layer rules
|
||||
- [ ] Component < ~150 lines
|
||||
- [ ] No business logic in presentation
|
||||
- [ ] Uses interfaces, not concrete classes
|
||||
- [ ] Tested appropriately
|
||||
- [ ] TypeScript strict compliant
|
||||
- [ ] Follows naming conventions
|
||||
- [ ] No circular deps
|
||||
|
||||
## Resources
|
||||
- See `openspec/changes/add-toldo-poc-capacitor/design.md` for full architecture
|
||||
- Follow dependency rules strictly
|
||||
- When in doubt, put logic in core/ or services/, keep UI dumb
|
||||
127
skills/toldo-domain-modeling/SKILL.md
Normal file
127
skills/toldo-domain-modeling/SKILL.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Agent Skill: toldo-domain-modeling
|
||||
|
||||
## Metadata
|
||||
```yaml
|
||||
name: toldo-domain-modeling
|
||||
description: Modelado de dominio CESTA (productos por productor, inventario, ventas, gastos, ajustes con motivo, cuadre QR/efectivo, reportes). Seguir reglas de negocio estrictamente.
|
||||
```
|
||||
|
||||
## When to use
|
||||
Use this skill when:
|
||||
- Modeling entities, value objects, or aggregates
|
||||
- Implementing business rules
|
||||
- Writing domain logic
|
||||
- Creating use cases related to CESTA flows
|
||||
- Validating invariants
|
||||
|
||||
## Core Domain Concepts
|
||||
|
||||
### Products are unique per producer
|
||||
**Critical Rule**: Same product name from different producers = DIFFERENT products.
|
||||
|
||||
```typescript
|
||||
// CORRECT
|
||||
Producto { productorId: "A", nombrePresentacion: "Café molido 250gr Santa Ana" }
|
||||
Producto { productorId: "B", nombrePresentacion: "Café molido 250gr Familia Gonzalez" }
|
||||
|
||||
// WRONG - same entity for different producers
|
||||
```
|
||||
|
||||
Uniqueness constraint: `(productorId, nombrePresentacion)`
|
||||
|
||||
### Adjustments require reason
|
||||
**Critical Rule**: ALL adjustments MUST have a non-empty `motivo`.
|
||||
|
||||
- Adjustments to inventory, sales, cash discrepancies, QR discrepancies all require reason
|
||||
- Adjustments are tracked separately
|
||||
- Must appear marked as "modificado/cambiado" in reports
|
||||
- Store `cantidadAnterior`, `cantidadNueva`, `diferencia`, `motivo`
|
||||
|
||||
### Inventory Management
|
||||
- **Initial inventory**: Should match previous day's final inventory
|
||||
- **Differences**: If different, record difference and use real inventory
|
||||
- **Week restocks**: Register with producto, cantidad, recibidoPor (person who received)
|
||||
- **End-of-day**: Should match initial inventory minus sold products
|
||||
|
||||
### Sales
|
||||
- Lines with: productoId, cantidad, precioUnitario, formaPago (EFECTIVO | QR)
|
||||
- Track separately for cash and QR reconciliation
|
||||
- Each sale line has subtotal
|
||||
|
||||
### Expenses (Gastos)
|
||||
- Track during day: tape, pens, bags, transportation, etc.
|
||||
- Include in reconciliation
|
||||
|
||||
### Cash Reconciliation (Cuadre)
|
||||
```typescript
|
||||
saldoEsperadoEfectivo = baseInicial + totalVentasEfectivo - totalGastosEfectivo
|
||||
descuadreEfectivo = saldoRealEfectivo - saldoEsperadoEfectivo (or vice versa, track clearly)
|
||||
totalVentas = totalVentasEfectivo + totalVentasQR
|
||||
```
|
||||
|
||||
- Separate cash and QR
|
||||
- Compare QR registrado vs QR banco
|
||||
- Track discrepancies
|
||||
|
||||
### Reports by Producer
|
||||
Must include:
|
||||
1. Inventory differences vs previous day
|
||||
2. Initial inventory (real)
|
||||
3. Inventory movements from sales (salidas)
|
||||
4. Sales value for producer's products
|
||||
5. Total per producer
|
||||
|
||||
### Consolidated Reports
|
||||
By date range, week, or month aggregating all data.
|
||||
|
||||
## Value Objects
|
||||
|
||||
Prefer value objects for primitives that have business meaning:
|
||||
|
||||
```typescript
|
||||
// Money value object
|
||||
class Money {
|
||||
constructor(private readonly cents: number) {}
|
||||
add(other: Money): Money { ... }
|
||||
subtract(other: Money): Money { ... }
|
||||
// ...
|
||||
}
|
||||
|
||||
// Quantity
|
||||
class Quantity {
|
||||
constructor(private readonly value: number) {}
|
||||
// ensure non-negative, etc.
|
||||
}
|
||||
```
|
||||
|
||||
## Domain Invariants
|
||||
|
||||
Enforce these invariants:
|
||||
- [ ] Producto has valid productorId
|
||||
- [ ] Ajuste always has non-empty motivo
|
||||
- [ ] Venta has at least one line
|
||||
- [ ] Cuadre calculations are consistent
|
||||
- [ ] Inventory movements are traceable
|
||||
- [ ] Payment methods are only EFECTIVO or QR
|
||||
|
||||
## TDD for Domain
|
||||
**Always write tests first for domain logic:**
|
||||
1. Write failing test for rule/invariant
|
||||
2. Implement minimal code to pass
|
||||
3. Refactor
|
||||
4. Repeat
|
||||
|
||||
Domain tests should be pure, fast, no I/O, no framework dependencies.
|
||||
|
||||
## Common Pitfalls
|
||||
- ❌ Treating same product name as same product across producers
|
||||
- ❌ Allowing adjustments without motivo
|
||||
- ❌ Mixing cash and QR totals
|
||||
- ❌ Not tracking receivedBy for week restocks
|
||||
- ❌ Forgetting to mark adjustments as changed in reports
|
||||
- ❌ Losing traceability of modifications
|
||||
|
||||
## Resources
|
||||
- See `openspec/changes/add-toldo-poc-capacitor/specs/toldo-poc/spec.md` for all scenarios
|
||||
- See `design.md` for full entity definitions
|
||||
- Follow all business rules strictly
|
||||
160
skills/toldo-offline-export-import/SKILL.md
Normal file
160
skills/toldo-offline-export-import/SKILL.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# Agent Skill: toldo-offline-export-import
|
||||
|
||||
## Metadata
|
||||
```yaml
|
||||
name: toldo-offline-export-import
|
||||
description: Exportar/importar JSON versionado, migraciones de esquema, validación e integridad para uso offline/APK. Funciona sin red.
|
||||
```
|
||||
|
||||
## When to use
|
||||
Use this skill when:
|
||||
- Implementing export functionality
|
||||
- Implementing import functionality
|
||||
- Handling schema migrations
|
||||
- Versioning data formats
|
||||
- Ensuring data portability between devices
|
||||
|
||||
## Export Format (Versioned JSON)
|
||||
|
||||
```typescript
|
||||
interface ExportData {
|
||||
schemaVersion: number; // Database/schema version
|
||||
appVersion: string; // App version
|
||||
exportedAt: string; // ISO 8601 timestamp
|
||||
data: {
|
||||
productores: Productor[];
|
||||
productos: Producto[];
|
||||
movimientosInventario: MovimientoInventario[];
|
||||
ventas: Venta[];
|
||||
ventaLineas: VentaLinea[];
|
||||
gastos: Gasto[];
|
||||
ajustes: Ajuste[];
|
||||
cuadres: CuadreCaja[];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Export Requirements
|
||||
- [ ] Include all entities
|
||||
- [ ] Use current schemaVersion
|
||||
- [ ] Include appVersion
|
||||
- [ ] Include exportedAt timestamp
|
||||
- [ ] Pretty print or compact JSON (both fine)
|
||||
- [ ] Filename suggestion: `toldo-export-YYYY-MM-DD-HHMMSS.json`
|
||||
- [ ] Works offline (no network calls)
|
||||
|
||||
## Import Requirements
|
||||
- [ ] Validate JSON structure
|
||||
- [ ] Check schemaVersion compatibility
|
||||
- [ ] Migrate data if schemaVersion is older (supported migration path)
|
||||
- [ ] Reject if schemaVersion is newer and no migration path (with clear message)
|
||||
- [ ] Validate required fields
|
||||
- [ ] Handle errors gracefully with user-friendly messages
|
||||
- [ ] Option to backup current data before import? (recommended)
|
||||
- [ ] Works offline
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
```typescript
|
||||
function migrateExport(exportData: ExportData): ExportData {
|
||||
let data = { ...exportData };
|
||||
|
||||
// Migrate from older versions
|
||||
while (data.schemaVersion < CURRENT_SCHEMA_VERSION) {
|
||||
data = migrateToNextVersion(data);
|
||||
}
|
||||
|
||||
if (data.schemaVersion > CURRENT_SCHEMA_VERSION) {
|
||||
throw new Error(
|
||||
`Incompatible schema version: ${data.schemaVersion}. ` +
|
||||
`Current app supports up to ${CURRENT_SCHEMA_VERSION}. ` +
|
||||
`Please update the app.`
|
||||
);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function migrateToNextVersion(data: ExportData): ExportData {
|
||||
// Implement version-specific migrations
|
||||
// Example: add new fields with defaults
|
||||
switch (data.schemaVersion) {
|
||||
case 1:
|
||||
// migrate v1 -> v2
|
||||
return { ...data, schemaVersion: 2, data: migratedData };
|
||||
default:
|
||||
throw new Error('Unknown schema version');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
```typescript
|
||||
function validateExport(data: unknown): data is ExportData {
|
||||
// Check structure
|
||||
if (!isObject(data)) throw new Error('Invalid export format');
|
||||
if (typeof data.schemaVersion !== 'number') throw new Error('Missing schemaVersion');
|
||||
if (typeof data.appVersion !== 'string') throw new Error('Missing appVersion');
|
||||
if (!data.exportedAt) throw new Error('Missing exportedAt');
|
||||
if (!isObject(data.data)) throw new Error('Missing data');
|
||||
|
||||
// Validate data arrays exist
|
||||
const required = ['productores', 'productos', 'ventas', 'gastos', ...];
|
||||
for (const key of required) {
|
||||
if (!Array.isArray(data.data[key])) throw new Error(`Missing or invalid ${key}`);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
## Usage in Capacitor
|
||||
|
||||
For APK/native, can use:
|
||||
- `@capacitor/filesystem` to save/load files
|
||||
- Web File API as fallback
|
||||
- Share API if available
|
||||
|
||||
Always mock in tests.
|
||||
|
||||
```typescript
|
||||
// In tests
|
||||
vi.mock('@capacitor/filesystem', () => ({
|
||||
Filesystem: {
|
||||
writeFile: vi.fn().mockResolvedValue({ uri: 'file://' }),
|
||||
readFile: vi.fn().mockResolvedValue({ data: '...' }),
|
||||
},
|
||||
}));
|
||||
```
|
||||
|
||||
## Distribution Between Devices
|
||||
|
||||
- Export on device A (tablet at market or home)
|
||||
- Transfer via USB/OTG to device B
|
||||
- Import on device B
|
||||
- All offline, no network needed
|
||||
- Works with any file manager
|
||||
|
||||
## Best Practices
|
||||
- [ ] Always version exports
|
||||
- [ ] Validate before importing
|
||||
- [ ] Provide clear error messages
|
||||
- [ ] Don't silently corrupt data
|
||||
- [ ] Test migrations with real export files
|
||||
- [ ] Keep backward compatibility when possible
|
||||
- [ ] Log migration steps for debugging
|
||||
- [ ] Consider backing up before destructive import
|
||||
|
||||
## Error Messages
|
||||
Be user-friendly in Spanish (app is for CESTA in Colombia):
|
||||
|
||||
- "Formato de archivo inválido"
|
||||
- "Versión de esquema incompatible. Actualice la aplicación."
|
||||
- "Error al leer el archivo"
|
||||
- "Datos importados correctamente"
|
||||
|
||||
## Resources
|
||||
- See `openspec/changes/add-toldo-poc-capacitor/specs/toldo-poc/spec.md` for scenarios
|
||||
- JSON versioned format is critical for long-term maintainability
|
||||
- Test import/export round-trip
|
||||
175
skills/toldo-tdd-vitest/SKILL.md
Normal file
175
skills/toldo-tdd-vitest/SKILL.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# Agent Skill: toldo-tdd-vitest
|
||||
|
||||
## Metadata
|
||||
```yaml
|
||||
name: toldo-tdd-vitest
|
||||
description: Workflow TDD con Vitest para Vite+Capacitor. Mock de plugins, fake-indexeddb, Testing Library. Ciclo Red-Green-Refactor estricto.
|
||||
```
|
||||
|
||||
## When to use
|
||||
Use this skill when:
|
||||
- Writing tests
|
||||
- Implementing features with TDD
|
||||
- Setting up test infrastructure
|
||||
- Mocking dependencies (Capacitor plugins, Dexie)
|
||||
- Testing components
|
||||
|
||||
## TDD Workflow (Red-Green-Refactor)
|
||||
|
||||
### For Domain/Core (MANDATORY TDD)
|
||||
1. **RED**: Write a failing test that describes the behavior
|
||||
2. **GREEN**: Write minimal code to make the test pass
|
||||
3. **REFACTOR**: Improve code while keeping tests green
|
||||
4. **REPEAT**: Next test case
|
||||
|
||||
### For Data/Services
|
||||
Write tests, can use mocks/fake DB. Still prefer test-first when possible.
|
||||
|
||||
### For UI
|
||||
Test behavior and user interactions, not implementation details.
|
||||
|
||||
## Setup
|
||||
|
||||
### Dependencies
|
||||
```json
|
||||
{
|
||||
"devDependencies": {
|
||||
"vitest": "^1.0.0",
|
||||
"jsdom": "^23.0.0",
|
||||
"@testing-library/vue": "^8.0.0", // or @testing-library/react/svelte
|
||||
"@testing-library/user-event": "^14.0.0",
|
||||
"fake-indexeddb": "^5.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### vitest.config.ts
|
||||
```typescript
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
setupFiles: ['./tests/setup.ts'],
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@core': path.resolve(__dirname, '../../packages/core/src'),
|
||||
'@data': path.resolve(__dirname, '../../packages/data/src'),
|
||||
'@services': path.resolve(__dirname, '../../packages/services/src'),
|
||||
'@ui': path.resolve(__dirname, '../../packages/ui/src'),
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### tests/setup.ts
|
||||
```typescript
|
||||
import 'fake-indexeddb/auto';
|
||||
```
|
||||
|
||||
## Mocking Capacitor Plugins
|
||||
|
||||
**NEVER** test against real Capacitor plugins. Always mock.
|
||||
|
||||
```typescript
|
||||
import { vi } from 'vitest';
|
||||
|
||||
vi.mock('@capacitor/core', () => ({
|
||||
Capacitor: {
|
||||
isNativePlatform: () => false,
|
||||
getPlatform: () => 'web',
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@capacitor/filesystem', () => ({
|
||||
Filesystem: {
|
||||
writeFile: vi.fn(),
|
||||
readFile: vi.fn(),
|
||||
},
|
||||
}));
|
||||
```
|
||||
|
||||
## Testing with Dexie (fake-indexeddb)
|
||||
|
||||
```typescript
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { db } from '../db';
|
||||
|
||||
describe('ProductorRepository', () => {
|
||||
beforeEach(async () => {
|
||||
await db.delete();
|
||||
await db.open();
|
||||
});
|
||||
|
||||
it('should create productor', async () => {
|
||||
// test...
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Testing UI Components
|
||||
|
||||
Use @testing-library best practices:
|
||||
- Query by role, label, text (accessible queries)
|
||||
- Use userEvent for interactions
|
||||
- Test behavior, not implementation
|
||||
- Avoid testing internal state
|
||||
|
||||
```typescript
|
||||
import { render, screen } from '@testing-library/vue';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import Button from './Button.vue';
|
||||
|
||||
it('calls onClick when clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onClick = vi.fn();
|
||||
render(Button, { props: { onClick } });
|
||||
|
||||
await user.click(screen.getByRole('button'));
|
||||
expect(onClick).toHaveBeenCalled();
|
||||
});
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
# Run tests in watch mode (TDD)
|
||||
npm test -- --watch
|
||||
|
||||
# Run all tests
|
||||
npm test
|
||||
|
||||
# Run with coverage
|
||||
npm test -- --coverage
|
||||
|
||||
# Run specific file
|
||||
npm test path/to/file.test.ts
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- **Core tests**: Pure unit tests, no mocks needed, fast
|
||||
- **Data tests**: Use fake-indexeddb, test in isolation
|
||||
- **Services tests**: Mock repositories
|
||||
- **UI tests**: Use Testing Library, mock services/composables
|
||||
- **Capacitor**: Always mock in unit/integration tests
|
||||
- **No tests in APK/emulator for initial development** - web tests are sufficient
|
||||
- **Keep tests fast** - if slow, something is wrong
|
||||
- **One assertion concept per test** when possible
|
||||
|
||||
## TDD Checklist
|
||||
- [ ] Test written before implementation (for core)
|
||||
- [ ] Test fails initially (RED)
|
||||
- [ ] Minimal implementation makes it pass (GREEN)
|
||||
- [ ] Code refactored (REFACTOR)
|
||||
- [ ] All tests still pass
|
||||
- [ ] Test is meaningful and tests behavior
|
||||
|
||||
## Resources
|
||||
- [Vitest Docs](https://vitest.dev/)
|
||||
- [Testing Library](https://testing-library.com/)
|
||||
- [fake-indexeddb](https://github.com/dumbmatter/fakeIndexedDB)
|
||||
- Follow Red-Green-Refactor strictly for domain logic
|
||||
Reference in New Issue
Block a user