feat(F-006): users profile, addresses and RBAC
- users module: profile + address CRUD behind use cases (users_profiles, users_addresses) - roles customer/admin on identity_users; role resolved from DB per request - shared auth contract (Authenticate, requireRole, requireOwnerOrAdmin) injected from composition root; users never imports identity - authorization runs before existence checks; address SQL scoped by user_id - @fastify/cookie registered once at app root (cross-module) - migrations 003_identity_roles + 004_users (reversible) - no new npm dependencies; tests: unit 52, integration 22 Gates: reviewer/security/qa APPROVED; verify.sh green
This commit is contained in:
32
work/artifacts/F-006/architect.md
Normal file
32
work/artifacts/F-006/architect.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Architect — F-006 Users: profile, addresses, RBAC
|
||||
|
||||
done -> work/artifacts/F-006/architect.md
|
||||
|
||||
## Deliverables
|
||||
- specs/F-006-users-rbac/SPEC.md, DESIGN.md, TASKS.md, TESTS.md
|
||||
|
||||
## Key decisions
|
||||
1. **Cross-module auth por inyección, no por imports**: R1 prohíbe que `users` importe
|
||||
`identity`. identity exporta `createSessionAuthenticator` por su index; el
|
||||
composition root lo crea y lo inyecta en `registerUsersRoutes`. El contrato
|
||||
(`CurrentUser`, `Authenticate`, `requireRole`) vive en `src/shared/auth.ts`.
|
||||
2. **Role en identity_users** (migración 003): fuente única de verdad de autorización,
|
||||
viaja con el usuario autenticado, imposible de escalar vía API (register siempre
|
||||
crea 'customer'; solo DB/migración cambia roles).
|
||||
3. **Authz antes que existencia**: no-owner recibe 403 sin importar si el recurso
|
||||
existe (AC1 literal; sin enumeración).
|
||||
4. **Tablas users_profiles / users_addresses** con FK a identity_users solo como
|
||||
integridad de schema; queries runtime tocan únicamente tablas `users_*` (regla de
|
||||
prefijo intacta).
|
||||
5. **Admin endpoint dentro de users**: GET /users lista profiles (tabla propia).
|
||||
Nada obliga a users a leer identity_users en runtime.
|
||||
6. **Cero dependencias nuevas**.
|
||||
|
||||
## Security posture
|
||||
- Session resolution server-side (hash de token, expiración y revocación en SQL).
|
||||
- Owner-or-admin en cada ruta; admin-only con requireRole.
|
||||
- Role jamás proviene del cliente: sale del JOIN sessions+users en DB.
|
||||
|
||||
## Risks
|
||||
- Profiles lazy (no auto-create en register): GET /users lista solo profiles
|
||||
existentes. Aceptado para el slice; documentado.
|
||||
29
work/artifacts/F-006/documenter.md
Normal file
29
work/artifacts/F-006/documenter.md
Normal file
@@ -0,0 +1,29 @@
|
||||
done -> work/artifacts/F-006/documenter.md
|
||||
|
||||
# F-006 — Documentation
|
||||
|
||||
Agent: documenter
|
||||
Stage: document
|
||||
Feature: F-006
|
||||
|
||||
## Updated
|
||||
|
||||
- `project/README.md`:
|
||||
- Authentication table: register/login responses now include `role`.
|
||||
- New section "Users and RBAC (users module)": endpoint table with access
|
||||
rules, 401/403/404 semantics, anti-enumeration note, SQL scoping note,
|
||||
`GET /users` listing semantics, and the injected-`Authenticate` boundary
|
||||
contract.
|
||||
- Layout: `users/` module added to the tree.
|
||||
- HTTP contract: auth codes now include `UNAUTHORIZED` (401) and
|
||||
`FORBIDDEN` (403).
|
||||
- Specs `specs/F-006-users-rbac/` (SPEC/DESIGN/TASKS/TESTS) were authored in
|
||||
the design stage and match the delivered implementation, except the
|
||||
documented deviation noted in `work/artifacts/F-006/implementer.md`
|
||||
(ownership unit tests merged into `shared/tests/auth.test.ts`).
|
||||
|
||||
## Not needed
|
||||
|
||||
- `spec/tech.md`: no new dependencies this ticket.
|
||||
- `spec/roadmap.md`: F-006 already listed in Wave 1; no status field tracked
|
||||
there.
|
||||
105
work/artifacts/F-006/implementer.md
Normal file
105
work/artifacts/F-006/implementer.md
Normal file
@@ -0,0 +1,105 @@
|
||||
done -> work/artifacts/F-006/implementer.md
|
||||
|
||||
# F-006 — Users: profile, addresses, RBAC (Build)
|
||||
|
||||
Agent: implementer
|
||||
Stage: build
|
||||
Feature: F-006
|
||||
|
||||
## Scope delivered
|
||||
|
||||
Profile + address management with owner-or-admin authorization, on top of the
|
||||
F-005 identity foundation. No new npm dependencies were added.
|
||||
|
||||
## Files
|
||||
|
||||
### Migrations (immutable; new migrations only)
|
||||
- `project/migrations/003_identity_roles.js` — adds `identity_users.role`
|
||||
(`customer` | `admin`, default `customer`, NOT NULL) + index.
|
||||
- `project/migrations/004_users.js` — `users_profiles` (PK `user_id` FK →
|
||||
`identity_users`, `display_name`, `phone`, timestamps) and
|
||||
`users_addresses` (`id` uuid PK, `user_id` FK ON DELETE CASCADE, address
|
||||
fields, `is_default`, timestamps, `user_id` index).
|
||||
|
||||
### Shared auth contract
|
||||
- `project/src/shared/auth.ts` — `Role`, `CurrentUser`, `Authenticate`,
|
||||
`requireRole`, `requireOwnerOrAdmin`. Shared (not module-owned) so both
|
||||
identity and users use one authorization vocabulary without cross-module
|
||||
imports.
|
||||
|
||||
### Identity additions
|
||||
- `project/src/modules/identity/infrastructure/session-authenticator.ts` —
|
||||
resolves the session cookie into `CurrentUser` from the DB (expiry +
|
||||
revocation enforced in SQL). Exported through the module index so the
|
||||
composition root can inject it.
|
||||
- Role added to `User`, `PgUserRepository` (SELECT/RETURNING `role`), and the
|
||||
register/login responses.
|
||||
|
||||
### Users module (hexagonal)
|
||||
- `domain/profile.ts`, `domain/address.ts`, `domain/ports.ts`
|
||||
- `application/profile-use-cases.ts`, `application/address-use-cases.ts`
|
||||
- `infrastructure/pg-profile-repository.ts`,
|
||||
`infrastructure/pg-address-repository.ts`
|
||||
- `api/users.routes.ts` — registers the endpoints below.
|
||||
- `index.ts` — public API.
|
||||
|
||||
### Composition root
|
||||
- `project/src/app/build-app.ts` — registers `@fastify/cookie` once at root
|
||||
(cross-module infrastructure), wires identity routes, builds the
|
||||
`Authenticate` from identity and injects it into the users routes.
|
||||
|
||||
### Tests
|
||||
- `project/src/shared/tests/auth.test.ts` (unit)
|
||||
- `project/src/app/tests/users.itest.ts` (integration)
|
||||
- `project/src/infrastructure/db/tests/migrations.itest.ts` — full rollback now
|
||||
asserts `users_addresses`, `users_profiles`, identity and baseline all drop.
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Method | Path | Auth |
|
||||
|--------|------|------|
|
||||
| GET | /users | admin only |
|
||||
| GET | /users/:id | owner or admin |
|
||||
| PATCH | /users/:id | owner or admin |
|
||||
| GET | /users/:id/addresses | owner or admin |
|
||||
| POST | /users/:id/addresses | owner or admin |
|
||||
| PATCH | /users/:id/addresses/:addressId | owner or admin |
|
||||
| DELETE | /users/:id/addresses/:addressId | owner or admin |
|
||||
|
||||
Authorization runs BEFORE existence checks: a non-owner gets 403 regardless of
|
||||
whether the target resource exists. Address operations are additionally scoped
|
||||
by `user_id` in SQL, so a caller cannot touch another user's address even with
|
||||
a valid id.
|
||||
|
||||
## Verification
|
||||
- `npm run lint`, `npm run lint:boundaries`, `npm run typecheck`,
|
||||
`npm run build`: clean.
|
||||
- `npm test`: 52 passed, 22 skipped (integration skipped without
|
||||
TEST_DATABASE_URL).
|
||||
- `npm run test:integration`: 22 passed against real PostgreSQL 16.
|
||||
- `./scripts/verify.sh`: green.
|
||||
- Live smoke (`PORT=3995`): register/login 201/200; PATCH own profile 200;
|
||||
GET own 200; cross-user GET 403; GET /users as customer 403; after promotion
|
||||
GET /users as admin 200; address CRUD 201/204; cross-user address 403;
|
||||
no-cookie GET 401. Smoke data removed afterwards.
|
||||
|
||||
## Deviations from TESTS.md (documented, not silent)
|
||||
- The planned `users/tests/ownership.test.ts` was merged into
|
||||
`shared/tests/auth.test.ts`, because the owner-or-admin decision lives in the
|
||||
shared contract (`shared/auth.ts`), not in the users module. Coverage is
|
||||
equivalent.
|
||||
- `@fastify/cookie` registration moved from identity's route scope to the app
|
||||
root. Fastify encapsulation meant the `request.cookies` decorator was not
|
||||
visible in the users scope (caused 500s); cookies are now cross-module
|
||||
infrastructure registered once at the composition root.
|
||||
|
||||
## Notes for gates
|
||||
- Reviewer: check the SQL column whitelist in `pg-profile-repository.upsert`
|
||||
and `pg-address-repository.update` (only hardcoded columns; values always
|
||||
parameterized), and that every users route calls `authenticate` then
|
||||
`requireOwnerOrAdmin` before touching data.
|
||||
- Security: session resolution is DB-backed (revocation + expiry in SQL); role
|
||||
is read from `identity_users` per request, so promotion/demotion is reflected
|
||||
immediately without trusting the client.
|
||||
- QA: integration suite exercises every acceptance criterion (AC1–AC4) plus the
|
||||
F-005 logout regression.
|
||||
33
work/artifacts/F-006/leader-close.json
Normal file
33
work/artifacts/F-006/leader-close.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"feature_id": "F-006",
|
||||
"agent": "leader",
|
||||
"stage": "close",
|
||||
"verdict": "APPROVED",
|
||||
"title": "Users: profile, addresses, RBAC",
|
||||
"gates": {
|
||||
"review": "APPROVED",
|
||||
"security": "APPROVED",
|
||||
"qa": "APPROVED"
|
||||
},
|
||||
"verification": {
|
||||
"lint": "clean",
|
||||
"boundaries": "52 files OK",
|
||||
"typecheck": "clean",
|
||||
"build": "clean",
|
||||
"unit_tests": "52 passed, 22 skipped",
|
||||
"integration_tests": "22 passed (PostgreSQL 16)",
|
||||
"verify_sh": "green"
|
||||
},
|
||||
"deliverables": [
|
||||
"migrations/003_identity_roles.js, migrations/004_users.js",
|
||||
"src/shared/auth.ts (Role, CurrentUser, Authenticate, requireRole, requireOwnerOrAdmin)",
|
||||
"src/modules/users/ (domain, application, infrastructure, api)",
|
||||
"identity: session-authenticator + role in model/responses",
|
||||
"app/build-app.ts: cookie plugin at root, Authenticate injection into users",
|
||||
"tests: shared/tests/auth.test.ts, app/tests/users.itest.ts, migrations.itest.ts updated"
|
||||
],
|
||||
"known_followups": [
|
||||
"GET /users lists only users with a profile row; an admin user-listing endpoint may need an identity-owned port later",
|
||||
"Role promotion is out-of-band DB operation until an admin API exists"
|
||||
]
|
||||
}
|
||||
46
work/artifacts/F-006/qa.json
Normal file
46
work/artifacts/F-006/qa.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"feature_id": "F-006",
|
||||
"agent": "qa",
|
||||
"verdict": "APPROVED",
|
||||
"acceptance_criteria": [
|
||||
{
|
||||
"criterion": "Given user A When A requests user B profile Then HTTP 403",
|
||||
"status": "PASS",
|
||||
"evidence": "users.itest.ts 'user A requesting user B profile gets 403'; live smoke: A GET B profile -> 403 FORBIDDEN envelope"
|
||||
},
|
||||
{
|
||||
"criterion": "Given customer role When admin-only endpoint called Then HTTP 403",
|
||||
"status": "PASS",
|
||||
"evidence": "users.itest.ts 'GET /users is admin-only: customer 403'; live smoke confirmed before promotion"
|
||||
},
|
||||
{
|
||||
"criterion": "Given admin role When admin-only endpoint called Then HTTP 200",
|
||||
"status": "PASS",
|
||||
"evidence": "Same itest after DB promotion returns 200 with items array; live smoke: A(admin) GET /users -> 200"
|
||||
},
|
||||
{
|
||||
"criterion": "Address CRUD works end to end for own addresses",
|
||||
"status": "PASS",
|
||||
"evidence": "users.itest.ts address CRUD test: POST 201, GET list, PATCH 200, DELETE 204, empty list after; validation 400 on missing fields; cross-user mutation 403; unknown address 404; malformed uuid 400"
|
||||
},
|
||||
{
|
||||
"criterion": "verify.sh green",
|
||||
"status": "PASS",
|
||||
"evidence": "./scripts/verify.sh: Orquestra verificado"
|
||||
}
|
||||
],
|
||||
"regression": [
|
||||
"F-005 identity flows: register/login/logout still green, including logout-kills-access regression in users.itest.ts",
|
||||
"F-002 migrations: full up creates baseline+identity+users schemas; full down drops all; node-pg-migrate status consistent",
|
||||
"Foundation-only app (no pool) build still works; health endpoint unaffected"
|
||||
],
|
||||
"suite": {
|
||||
"unit": "52 passed, 22 skipped (no TEST_DATABASE_URL path in npm test default run)",
|
||||
"integration": "22 passed against real PostgreSQL 16",
|
||||
"lint_boundaries_typecheck_build": "all clean"
|
||||
},
|
||||
"known_limitations": [
|
||||
"GET /users lists only users with an existing profile row (documented in reviewer artifact as non-blocking)",
|
||||
"Role promotion/demotion is an out-of-band DB operation in this slice; no admin API for it (scope_out: no admin UI)"
|
||||
]
|
||||
}
|
||||
35
work/artifacts/F-006/reviewer.json
Normal file
35
work/artifacts/F-006/reviewer.json
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"feature_id": "F-006",
|
||||
"agent": "reviewer",
|
||||
"verdict": "APPROVED",
|
||||
"reviewed_files": [
|
||||
"project/migrations/003_identity_roles.js",
|
||||
"project/migrations/004_users.js",
|
||||
"project/src/shared/auth.ts",
|
||||
"project/src/shared/tests/auth.test.ts",
|
||||
"project/src/modules/identity/infrastructure/session-authenticator.ts",
|
||||
"project/src/modules/identity/infrastructure/pg-user-repository.ts",
|
||||
"project/src/modules/identity/api/identity.routes.ts",
|
||||
"project/src/modules/identity/index.ts",
|
||||
"project/src/modules/users/domain/*",
|
||||
"project/src/modules/users/application/*",
|
||||
"project/src/modules/users/infrastructure/pg-profile-repository.ts",
|
||||
"project/src/modules/users/infrastructure/pg-address-repository.ts",
|
||||
"project/src/modules/users/api/users.routes.ts",
|
||||
"project/src/app/build-app.ts",
|
||||
"project/src/app/tests/users.itest.ts",
|
||||
"project/src/infrastructure/db/tests/migrations.itest.ts"
|
||||
],
|
||||
"checks": [
|
||||
"Module boundaries: users never imports identity; auth arrives by injection through shared/auth.ts (boundary lint green, 52 files)",
|
||||
"Authorization order: every users route runs authenticate -> param validation -> requireOwnerOrAdmin BEFORE any data access; non-owner always gets 403",
|
||||
"SQL ownership: address operations are scoped by user_id in every query (list/create/update/delete), so a known foreign addressId cannot be touched",
|
||||
"Dynamic UPDATE built only from a hardcoded column whitelist; all values parameterized",
|
||||
"Migrations additive and reversible: 003 adds role with CHECK + default, 004 owns users_* tables with FKs and ON DELETE CASCADE; full rollback verified in itest",
|
||||
"Acceptance criteria AC1-AC4 each exercised by a dedicated integration test; AC5 via verify.sh"
|
||||
],
|
||||
"non_blocking_observations": [
|
||||
"GET /users lists rows from users_profiles, i.e. users who have touched their profile at least once; users without a profile row are not listed. Acceptable for F-006 (AC only requires admin-only access semantics); a future admin-listing ticket should decide between an identity-owned user listing port or renaming the endpoint."
|
||||
],
|
||||
"evidence": "npm test: 52 passed; npm run test:integration: 22 passed against PostgreSQL 16; lint, boundary check, typecheck, build clean; live smoke exercised all authorization paths"
|
||||
}
|
||||
40
work/artifacts/F-006/security.json
Normal file
40
work/artifacts/F-006/security.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"feature_id": "F-006",
|
||||
"agent": "security",
|
||||
"verdict": "APPROVED",
|
||||
"threat_model": [
|
||||
{
|
||||
"vector": "SQL injection",
|
||||
"result": "mitigated",
|
||||
"evidence": "Every query parameterized ($n). The only dynamic SQL (profile upsert, address update) builds SET clauses from a hardcoded column whitelist; user input only flows into parameter values."
|
||||
},
|
||||
{
|
||||
"vector": "Privilege escalation (client sets own role)",
|
||||
"result": "mitigated",
|
||||
"evidence": "register/login schemas accept only email+password; no route writes identity_users.role. Role is returned from DB values, never echoed from input. DB CHECK constraint limits role to customer|admin."
|
||||
},
|
||||
{
|
||||
"vector": "Broken access control / IDOR",
|
||||
"result": "mitigated",
|
||||
"evidence": "Each users route runs authenticate -> requireOwnerOrAdmin before data access; address repository scopes every query by user_id, so a valid foreign addressId is unreachable. Integration tests prove cross-user reads and mutations all return 403."
|
||||
},
|
||||
{
|
||||
"vector": "Resource existence enumeration",
|
||||
"result": "mitigated",
|
||||
"evidence": "Authorization (403) is evaluated before existence checks (404) for non-owners, so a stranger cannot learn whether another user's profile exists."
|
||||
},
|
||||
{
|
||||
"vector": "Stale authorization (demotion/promotion)",
|
||||
"result": "mitigated",
|
||||
"evidence": "Session authenticator resolves role from identity_users on every request; role changes apply immediately without re-login, and no role state is trusted from the client."
|
||||
},
|
||||
{
|
||||
"vector": "Session security",
|
||||
"result": "unchanged from F-005",
|
||||
"evidence": "Opaque token in HttpOnly/SameSite=Lax cookie, SHA-256 hash stored server-side, expiry + revocation enforced in SQL. Cookie plugin moved to app root (cross-module), same policy."
|
||||
}
|
||||
],
|
||||
"dependencies_added": [],
|
||||
"notes": "No new npm dependencies; spec/tech.md unchanged. The 403-vs-404 ordering is correct for anti-enumeration. GET /users admin list returning only profiled users is not a security issue (admin-only surface).",
|
||||
"evidence": "npm run test:integration: 22 passed including forged-cookie 401, cross-user 403 battery, and logout revocation regression"
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
# Current work
|
||||
|
||||
- Active feature: none (idle)
|
||||
- Last closed: F-005 — Identity: register, login, sessions
|
||||
- Next suggested: F-006 — Users: profile, addresses, RBAC (depends on F-005, satisfied)
|
||||
- Last closed: F-006 — Users: profile, addresses, RBAC
|
||||
- Next suggested: F-007 — check `backlog/features.json` for the first `pending` ticket whose dependencies are satisfied
|
||||
- Runtime status: reset via scripts/agent_status.py
|
||||
- verify.sh: green at close
|
||||
|
||||
## F-005 closure notes
|
||||
- Identity module hexagonal: domain/application/infrastructure/api under src/modules/identity
|
||||
- Sessions server-side: cookie carries opaque 512-bit token; identity_sessions stores only SHA-256 hash
|
||||
- argon2id (OWASP params) behind PasswordHasher port; timing equalized 401 (no enumeration)
|
||||
- Rate limit: 10 consecutive failures per email -> 429 + Retry-After (15 min cooldown), in-memory behind interface
|
||||
- Cookie: HttpOnly + Secure (COOKIE_SECURE, default true) + SameSite=Lax
|
||||
- Migration 002_identity reversible; integration suite updated for full revert (count:0)
|
||||
- Gates: reviewer/security/qa APPROVED. Commits include specs, artifacts, code, docs.
|
||||
## F-006 closure notes
|
||||
- Users module hexagonal: profile + address CRUD behind use cases; tables `users_profiles`, `users_addresses`
|
||||
- Roles customer/admin on `identity_users` (migration 003); role resolved from DB on every request
|
||||
- Shared auth contract (`src/shared/auth.ts`): `Authenticate`, `requireRole`, `requireOwnerOrAdmin` — users never imports identity; composition root injects the authenticator
|
||||
- Authorization runs before existence checks (403 first, no enumeration); address SQL scoped by user_id
|
||||
- `@fastify/cookie` registered once at app root (cross-module infrastructure)
|
||||
- No new npm dependencies; README documents endpoint table, access rules, and new auth codes (UNAUTHORIZED/FORBIDDEN)
|
||||
- Gates: reviewer/security/qa APPROVED. Tests: unit 52, integration 22 (PostgreSQL 16)
|
||||
|
||||
@@ -33,3 +33,9 @@
|
||||
- Nota: review detectó falta de tests para COOKIE_SECURE; fix aplicado antes de aprobar el gate. Suite de migraciones F-002 actualizada a rollback completo (count:0) por tener ahora 2 migraciones
|
||||
- Deps nuevas: argon2, @fastify/cookie (justificadas en spec/tech.md)
|
||||
- Artefactos: work/artifacts/F-005/
|
||||
|
||||
## F-006 — Users: profile, addresses, RBAC (closed)
|
||||
- Users module: profile + address CRUD (users_profiles, users_addresses, migration 004); roles on identity_users (migration 003)
|
||||
- RBAC: owner-or-admin via shared/auth.ts injected from composition root; authz before existence checks; SQL scoped by user_id
|
||||
- Identity: session-authenticator export + role in model/responses; @fastify/cookie moved to app root
|
||||
- Zero new dependencies; tests: unit 52 + integration 22; live smoke covered 401/403/200 paths; gates APPROVED
|
||||
|
||||
@@ -1,139 +1,13 @@
|
||||
{
|
||||
"feature_id": "F-005",
|
||||
"feature_id": "F-006",
|
||||
"stage": "close",
|
||||
"agent": "leader",
|
||||
"action": "F-005 cerrada con gates aprobados",
|
||||
"action": "Cierre F-006",
|
||||
"state": "done",
|
||||
"next_agent": "leader",
|
||||
"waiting_for": "Seleccionar una feature pending y actualizar este estado",
|
||||
"updated_at": "2026-08-14T20:58:24Z",
|
||||
"updated_at": "2026-08-15T07:27:44Z",
|
||||
"timeline": [
|
||||
{
|
||||
"ts": "2026-08-14T20:39:25Z",
|
||||
"agent": "leader",
|
||||
"stage": "intake",
|
||||
"state": "done",
|
||||
"message": "Intake F-005 OK"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:39:25Z",
|
||||
"agent": "architect",
|
||||
"stage": "design",
|
||||
"state": "running",
|
||||
"message": "Diseño módulo identity (hexagonal)"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:41:17Z",
|
||||
"agent": "architect",
|
||||
"stage": "design",
|
||||
"state": "done",
|
||||
"message": "Diseño F-005 aprobado"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:41:17Z",
|
||||
"agent": "implementer",
|
||||
"stage": "build",
|
||||
"state": "running",
|
||||
"message": "Implementando módulo identity"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:51:41Z",
|
||||
"agent": "implementer",
|
||||
"stage": "build",
|
||||
"state": "done",
|
||||
"message": "Build F-005 completo con evidencia"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:53:01Z",
|
||||
"agent": "implementer",
|
||||
"stage": "build",
|
||||
"state": "running",
|
||||
"message": "Fix de review: cobertura COOKIE_SECURE"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:54:22Z",
|
||||
"agent": "implementer",
|
||||
"stage": "build",
|
||||
"state": "done",
|
||||
"message": "Fix de review aplicado y verificado"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:54:22Z",
|
||||
"agent": "reviewer",
|
||||
"stage": "review_gate",
|
||||
"state": "running",
|
||||
"message": "Review gate F-005"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:54:44Z",
|
||||
"agent": "reviewer",
|
||||
"stage": "review_gate",
|
||||
"state": "done",
|
||||
"message": "Review gate APPROVED"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:55:17Z",
|
||||
"agent": "security",
|
||||
"stage": "security_gate",
|
||||
"state": "running",
|
||||
"message": "Security gate F-005"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:55:17Z",
|
||||
"agent": "security",
|
||||
"stage": "security_gate",
|
||||
"state": "done",
|
||||
"message": "Security gate APPROVED"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:55:17Z",
|
||||
"agent": "qa",
|
||||
"stage": "qa_gate",
|
||||
"state": "running",
|
||||
"message": "QA gate F-005"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:55:36Z",
|
||||
"agent": "qa",
|
||||
"stage": "qa_gate",
|
||||
"state": "done",
|
||||
"message": "QA gate APPROVED"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:55:36Z",
|
||||
"agent": "documenter",
|
||||
"stage": "document",
|
||||
"state": "running",
|
||||
"message": "Documentación F-005"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:55:42Z",
|
||||
"agent": "implementer",
|
||||
"stage": "build",
|
||||
"state": "running",
|
||||
"message": "Docs README: auth contract"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:57:22Z",
|
||||
"agent": "implementer",
|
||||
"stage": "build",
|
||||
"state": "done",
|
||||
"message": "README auth contract documentado"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:57:22Z",
|
||||
"agent": "documenter",
|
||||
"stage": "document",
|
||||
"state": "running",
|
||||
"message": "Documentación F-005"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:58:18Z",
|
||||
"agent": "documenter",
|
||||
"stage": "document",
|
||||
"state": "done",
|
||||
"message": "Docs F-005 completas"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-14T20:58:24Z",
|
||||
"agent": "leader",
|
||||
@@ -147,6 +21,132 @@
|
||||
"stage": "close",
|
||||
"state": "done",
|
||||
"message": "F-005 cerrada con gates aprobados"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:04:47Z",
|
||||
"agent": "leader",
|
||||
"stage": "intake",
|
||||
"state": "running",
|
||||
"message": "Intake F-006"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:04:48Z",
|
||||
"agent": "leader",
|
||||
"stage": "intake",
|
||||
"state": "done",
|
||||
"message": "Intake F-006 OK"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:04:48Z",
|
||||
"agent": "architect",
|
||||
"stage": "design",
|
||||
"state": "running",
|
||||
"message": "Diseño users + RBAC"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:07:20Z",
|
||||
"agent": "architect",
|
||||
"stage": "design",
|
||||
"state": "done",
|
||||
"message": "Diseño F-006 aprobado"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:07:20Z",
|
||||
"agent": "implementer",
|
||||
"stage": "build",
|
||||
"state": "running",
|
||||
"message": "Implementando users + RBAC"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:22:07Z",
|
||||
"agent": "implementer",
|
||||
"stage": "build",
|
||||
"state": "done",
|
||||
"message": "Build F-006 completo: users + RBAC, tests y verify verdes"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:22:07Z",
|
||||
"agent": "reviewer",
|
||||
"stage": "review_gate",
|
||||
"state": "running",
|
||||
"message": "Revisando users + RBAC"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:23:01Z",
|
||||
"agent": "reviewer",
|
||||
"stage": "review_gate",
|
||||
"state": "done",
|
||||
"message": "Review gate F-006 APPROVED"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:23:01Z",
|
||||
"agent": "security",
|
||||
"stage": "security_gate",
|
||||
"state": "running",
|
||||
"message": "Audit users + RBAC"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:23:41Z",
|
||||
"agent": "security",
|
||||
"stage": "security_gate",
|
||||
"state": "done",
|
||||
"message": "Security gate F-006 APPROVED"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:23:41Z",
|
||||
"agent": "qa",
|
||||
"stage": "qa_gate",
|
||||
"state": "running",
|
||||
"message": "Validación users + RBAC"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:24:24Z",
|
||||
"agent": "qa",
|
||||
"stage": "qa_gate",
|
||||
"state": "done",
|
||||
"message": "QA gate F-006 APPROVED"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:24:24Z",
|
||||
"agent": "implementer",
|
||||
"stage": "build",
|
||||
"state": "running",
|
||||
"message": "Actualización README requerida por document"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:25:35Z",
|
||||
"agent": "implementer",
|
||||
"stage": "build",
|
||||
"state": "done",
|
||||
"message": "README users + RBAC documentado"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:25:35Z",
|
||||
"agent": "documenter",
|
||||
"stage": "document",
|
||||
"state": "running",
|
||||
"message": "Cerrando documentación"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:25:55Z",
|
||||
"agent": "documenter",
|
||||
"stage": "document",
|
||||
"state": "done",
|
||||
"message": "Docs F-006 completas"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:25:56Z",
|
||||
"agent": "leader",
|
||||
"stage": "close",
|
||||
"state": "running",
|
||||
"message": "Verificación final antes de cerrar"
|
||||
},
|
||||
{
|
||||
"ts": "2026-08-15T07:27:44Z",
|
||||
"agent": "leader",
|
||||
"stage": "close",
|
||||
"state": "done",
|
||||
"message": "F-006 cerrada con gates aprobados"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user