Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,10 @@ Este catálogo explica cada gap: problema, propósito, evidencia, criterios de c
- **Criticality:** P1 · **Complexity:** L
- **Proposed fix:** Contrato ACL con linaje al origen + salvaguardas de transición (la evidencia de Jira alimenta pero no autoriza gates) + una referencia de integración documentada.
- **Acceptance criteria:**
- [ ] Elementos de Jira se mapean a artefactos Evolith preservando origen/identidad/timestamps/linaje.
- [ ] Completar un workflow de Jira no autoriza por sí solo una transición de fase.
- [x] Elementos de Jira se mapean a artefactos Evolith preservando origen/identidad/timestamps/linaje. _(`parseJiraIssue`→`CanonicalWorkItem` con `WorkItemProvenance`; rechaza sin id)_
- [x] Completar un workflow de Jira no autoriza por sí solo una transición de fase. _(`authorizesPhaseTransition:false` por contrato + `externalWorkAuthorizesTransition`⇒false)_
- **Dependencies:** none.
- **Status:** `PENDING`
- **Status:** `IN-PROGRESS`

#### GT-530

Expand Down
6 changes: 3 additions & 3 deletions reference/core/control-center/gaps/gap-reference-catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,10 @@ This catalog explains each gap: problem, purpose, evidence, closure criteria, an
- **Criticality:** P1 · **Complexity:** L
- **Proposed fix:** ACL contract with lineage to origin + transition safeguards (Jira evidence feeds but does not authorize gates) + a documented integration reference.
- **Acceptance criteria:**
- [ ] Jira items map to Evolith artifacts preserving origin/identity/timestamps/lineage.
- [ ] Completing a Jira workflow does not by itself authorize a phase transition.
- [x] Jira items map to Evolith artifacts preserving origin/identity/timestamps/lineage. _(`parseJiraIssue`→`CanonicalWorkItem` with `WorkItemProvenance`; rejects a missing id)_
- [x] Completing a Jira workflow does not by itself authorize a phase transition. _(`authorizesPhaseTransition:false` by contract + `externalWorkAuthorizesTransition`⇒false)_
- **Dependencies:** none.
- **Status:** `PENDING`
- **Status:** `IN-PROGRESS`

#### GT-530

Expand Down
4 changes: 2 additions & 2 deletions reference/core/control-center/gaps/gap-tracking.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Este tablero es la única fuente de verdad para deuda técnica, gaps, oportunida
| [`GT-526`](./gap-reference-catalog.es.md#gt-526) | **[Superficie de control · eje 2] Enforcement `edit-time` — hook cross-agente.** La 3ª de las tres superficies READ→CONTROL (§14.1): pre-generación (MCP, parcial GT-520) y PR/CI (GT-518) existen, pero falta el hook que bloquea el cambio infractor al vuelo mientras el agente escribe. Sin incumbente en el mercado. Fix: hook cross-agente (Claude Code/Cursor/Copilot) que consulta el contrato de arquitectura y rechaza el edit no conforme. Análisis §14. **EN-PROGRESO (`edit-gate.ts`):** aterrizó el núcleo verificable — `evaluateEdit(edit, rules)` decide **allow/block al vuelo** con un chequeo rápido de un solo archivo (sin toolchain; el PR/CI de GT-518 sigue siendo el gate autoritativo): `extractImports` (TS/JS `import`/`export from`/`require` + C# `using`) + reglas de frontera `EditBoundaryRule` (`appliesTo`/`forbiddenImports`), emite `Violation` canónicas (tool `edit-gate`, reusables por evidencia/compliance), `allow=false` solo ante severidad `error`. Agent-neutral (función pura). Verificado: core-domain 917/917 (+7), tsc limpio. **Pendiente (integración):** el adapter por-agente (hook PreToolUse de Claude Code / extensión Cursor) que alimenta el edit y aplica la decisión. | `Evolith CLI` | Cross | P1 | M | `EN-PROGRESO` |
| [`GT-527`](./gap-reference-catalog.es.md#gt-527) | **[Wedge · eje 2] Conectores de ingesta de ownership sin lock-in: Port / Cortex / OpsLevel + Backstage.** Ingerir blueprints de IDP y `catalog-info.yaml` como fuente de ownership/servicios para enriquecer violaciones y ADRs sin depender del proveedor; Evolith bloquea donde ellos solo miden (§13.2). Fix: conectores read-only vía ACL que normalizan a la forma canónica de ownership. **EN-PROGRESO (`domain/ownership.ts`):** aterrizó el núcleo puro de normalización — `OwnershipEntry` canónico, `parseBackstageCatalog` (`catalog-info.yaml` kind Component → owner + pathPrefix vía anotación `evolith.io/path`/`source-location` relativo; descarta no-Components/incompletos), `parseBlueprintOwnership` (genérico Port/Cortex/OpsLevel: `component`\|`identifier` + `owner`\|`team`), `resolveOwner` (file→owner por longest-prefix) y `enrichViolationsWithOwner` (puebla `owner` sin sobrescribir, fuera del fingerprint). **Completa la cadena violación→owner→compliance** (compone con GT-525). Verificado: core-domain 925/925 (+8), tsc limpio. **Pendiente (conector/infra):** leer el `catalog-info.yaml` del repo (vía el port de config-parser) y el fetch read-only de las APIs Port/Cortex. | `Evolith Core` | Cross | P2 | L | `EN-PROGRESO` |
| [`GT-528`](./gap-reference-catalog.es.md#gt-528) | **[Wedge · eje 2] Ingesta de DSL Structurizr / C4 → ADR ejecutable.** Convertir modelos Structurizr/C4 (intención de arquitectura, hoy prosa/diagrama) en reglas `enforce:` verificables contra el código real (§13.2). Fix: parser del DSL + mapeo a `NormalizedRule.enforce`; complementa GT-516. **EN-PROGRESO (`c4-compiler.ts`):** aterrizó el compilador — `compileC4ToBoundaryRules(model)` transforma un `C4Model` normalizado (elementos con `path`/`importPrefix` + relaciones permitidas) en `EditBoundaryRule` de GT-526, derivando el denylist desde el allowlist del modelo (un elemento solo puede depender de lo que declara; el resto queda prohibido), con `ruleId` `C4-<id>`, ADR y severidad. **El diagrama C4 se vuelve ejecutable** y se enchufa al gate edit-time (GT-526) y al PR/CI. Verificado end-to-end: un edit de `src/domain` que importa `src/infrastructure` → bloqueado; core-domain 933/933 (+5), tsc limpio. **Pendiente (ingesta):** parsear el `.dsl` crudo de Structurizr (o su export JSON) al `C4Model` normalizado. | `Evolith Core` | Cross | P2 | M | `EN-PROGRESO` |
| [`GT-529`](./gap-reference-catalog.es.md#gt-529) | **[Surround · eje 1] Contrato ACL + referencia de integración Jira Enterprise.** Mapear ideas/epics/stories/aprobaciones/releases de Jira a artefactos Evolith preservando origen/identidad/timestamps/linaje, con salvaguardas de transición (completar un workflow de Jira no autoriza una transición de fase). §8.3 / §12. Fix: ACL de sistemas de trabajo externos + guía de integración. | `Evolith Core` | Cross | P1 | L | `PENDIENTE` |
| [`GT-529`](./gap-reference-catalog.es.md#gt-529) | **[Surround · eje 1] Contrato ACL + referencia de integración Jira Enterprise.** Mapear ideas/epics/stories/aprobaciones/releases de Jira a artefactos Evolith preservando origen/identidad/timestamps/linaje, con salvaguardas de transición (completar un workflow de Jira no autoriza una transición de fase). §8.3 / §12. Fix: ACL de sistemas de trabajo externos + guía de integración. **EN-PROGRESO (`domain/external-work-acl.ts`):** aterrizó el ACL puro — `CanonicalWorkItem` con `WorkItemProvenance` (source/externalId/externalKey/url/created/updated), `parseJiraIssue`/`parseJiraIssues` (mapea issues de Jira preservando procedencia; rechaza sin `id` en vez de fabricar identidad), `mapJiraIssueType` (Epic/Story/Task/Version→kind canónico). **Salvaguarda de transición (§9-6):** `authorizesPhaseTransition: false` por contrato + `externalWorkAuthorizesTransition`⇒`false` (completar un workflow de Jira NO autoriza un phase gate). Verificado: core-domain 939/939 (+6), tsc limpio. **Pendiente (conector/infra + doc):** fetch read-only de la Jira REST API + la guía de integración documentada. | `Evolith Core` | Cross | P1 | L | `EN-PROGRESO` |
| [`GT-530`](./gap-reference-catalog.es.md#gt-530) | **[Surround · eje 1] Adaptador Langfuse → evidencia canónica.** Mapear traces/evaluaciones/costo/latencia/versión-de-prompt/tool-calls de Langfuse al modelo de evidencia de Evolith, para no reconstruir una plataforma de telemetría LLM. §8.1 / §12. Fix: `LangfuseEvidenceAdapter` vía puerto de observabilidad. | `Evolith Core` | Cross | P2 | L | `PENDIENTE` |
| [`GT-531`](./gap-reference-catalog.es.md#gt-531) | **[Surround · eje 1] Adaptador Cowork/Claude como ejecutor gobernado acotado.** Ejecutor de actividades con permisos/planes/aprobaciones/captura de evidencia, tratando a Claude como uno de varios ejecutores reemplazables (§8.2, §9). Extiende el épico agent-runtime GT-383…394 (HITL GT-441 / adapters GT-438). | `agent-runtime` | Cross | P2 | M | `PENDIENTE` |
| [`GT-532`](./gap-reference-catalog.es.md#gt-532) | **[Surround · eje 1] Vistas ejecutivas de portafolio + adaptadores marketplace + paquetes de gobernanza por tenant.** Mejora de adopción empresarial y escala de ecosistema (§12 P2); mayormente Tracker (plano de captura de valor enterprise). | `Tracker` | Cross | P3 | XL | `PENDIENTE` |
Expand Down Expand Up @@ -547,7 +547,7 @@ Este tablero es la única fuente de verdad para deuda técnica, gaps, oportunida
| [`GT-246`](./gap-reference-catalog.es.md#gt-246) | Implementar experimentos Chaos Mesh/Litmus | `QA` | Cross | P3 | L | `COMPLETADO` |


**Progreso:** 496 / 532 completados · 17 en progreso · 17 pendientes · 2 diferido
**Progreso:** 496 / 532 completados · 18 en progreso · 16 pendientes · 2 diferido

**Oleada 2026-06-23 (auditoría profunda de Winston III):** Añadidos 14 gaps nuevos `GT-212`…`GT-225` del Winston Audit Playbook que cubren: higiene de estado ADR (GT-212), metadata + presupuestos operativos + corpus de guías por topología (GT-213, GT-217, GT-219), observabilidad + OpenAPI en controladores REST (GT-214, GT-215), paridad de input-schemas OPA + densidad de tests por topología (GT-216, GT-222), plantillas de rollback + on-call de Fase 05 (GT-218), cobertura de ramas CLI + paridad de envelope --format + limpieza de skip-list (GT-220, GT-224, GT-225), audit logging HTTP de MCP (GT-221), y tests e2e de paridad cross-surface (GT-223).

Expand Down
4 changes: 2 additions & 2 deletions reference/core/control-center/gaps/gap-tracking.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ This board is the single source of truth for technical debt, gaps, opportunities
| [`GT-526`](./gap-reference-catalog.md#gt-526) | **[Control surface · axis 2] `edit-time` enforcement — cross-agent hook.** The 3rd of the three READ→CONTROL surfaces (§14.1): pre-generation (MCP, partial GT-520) and PR/CI (GT-518) exist, but the hook that blocks the offending change in-flight as the agent writes is missing. No market incumbent. Fix: cross-agent hook (Claude Code/Cursor/Copilot) that queries the architecture contract and rejects the non-conforming edit. Analysis §14. **IN-PROGRESS (`edit-gate.ts`):** landed the verifiable core — `evaluateEdit(edit, rules)` decides **allow/block in-flight** with a fast single-file check (no toolchain; GT-518's PR/CI stays the authoritative gate): `extractImports` (TS/JS `import`/`export from`/`require` + C# `using`) + `EditBoundaryRule` boundary rules (`appliesTo`/`forbiddenImports`), emits canonical `Violation`s (tool `edit-gate`, reusable by evidence/compliance), `allow=false` only on `error` severity. Agent-neutral (pure function). Verified: core-domain 917/917 (+7), tsc clean. **Remaining (integration):** the per-agent adapter (Claude Code PreToolUse hook / Cursor extension) that feeds the edit and enforces the decision. | `Evolith CLI` | Cross | P1 | M | `IN-PROGRESS` |
| [`GT-527`](./gap-reference-catalog.md#gt-527) | **[Wedge · axis 2] Lock-in-free ownership ingestion connectors: Port / Cortex / OpsLevel + Backstage.** Ingest IDP blueprints and `catalog-info.yaml` as an ownership/service source to enrich violations and ADRs without vendor lock-in; Evolith blocks where they only measure (§13.2). Fix: read-only connectors via ACL normalizing to the canonical ownership shape. **IN-PROGRESS (`domain/ownership.ts`):** landed the pure normalization core — canonical `OwnershipEntry`, `parseBackstageCatalog` (`catalog-info.yaml` kind Component → owner + pathPrefix via `evolith.io/path`/relative `source-location`; drops non-Components/incomplete), `parseBlueprintOwnership` (generic Port/Cortex/OpsLevel: `component`\|`identifier` + `owner`\|`team`), `resolveOwner` (file→owner longest-prefix) and `enrichViolationsWithOwner` (fills `owner` without overwriting, excluded from the fingerprint). **Completes the violation→owner→compliance chain** (composes with GT-525). Verified: core-domain 925/925 (+8), tsc clean. **Remaining (connector/infra):** reading the repo's `catalog-info.yaml` (via the config-parser port) and the read-only Port/Cortex API fetch. | `Evolith Core` | Cross | P2 | L | `IN-PROGRESS` |
| [`GT-528`](./gap-reference-catalog.md#gt-528) | **[Wedge · axis 2] Structurizr / C4 DSL ingestion → executable ADR.** Turn Structurizr/C4 models (architecture intent, today prose/diagram) into `enforce:` rules verifiable against real code (§13.2). Fix: DSL parser + mapping to `NormalizedRule.enforce`; complements GT-516. **IN-PROGRESS (`c4-compiler.ts`):** landed the compiler — `compileC4ToBoundaryRules(model)` turns a normalized `C4Model` (elements with `path`/`importPrefix` + allowed relationships) into GT-526 `EditBoundaryRule`s, deriving the denylist from the model's allowlist (an element may only depend on what it declares; everything else is forbidden), with `ruleId` `C4-<id>`, ADR and severity. **The C4 diagram becomes executable** and feeds the edit-time gate (GT-526) and PR/CI. Verified end-to-end: a `src/domain` edit importing `src/infrastructure` → blocked; core-domain 933/933 (+5), tsc clean. **Remaining (ingestion):** parse the raw Structurizr `.dsl` (or its JSON export) into the normalized `C4Model`. | `Evolith Core` | Cross | P2 | M | `IN-PROGRESS` |
| [`GT-529`](./gap-reference-catalog.md#gt-529) | **[Surround · axis 1] ACL contract + Jira Enterprise integration reference.** Map Jira ideas/epics/stories/approvals/releases to Evolith artifacts preserving origin/identity/timestamps/lineage, with transition safeguards (completing a Jira workflow does not authorize a phase transition). §8.3 / §12. Fix: external work-system ACL + integration guide. | `Evolith Core` | Cross | P1 | L | `PENDING` |
| [`GT-529`](./gap-reference-catalog.md#gt-529) | **[Surround · axis 1] ACL contract + Jira Enterprise integration reference.** Map Jira ideas/epics/stories/approvals/releases to Evolith artifacts preserving origin/identity/timestamps/lineage, with transition safeguards (completing a Jira workflow does not authorize a phase transition). §8.3 / §12. Fix: external work-system ACL + integration guide. **IN-PROGRESS (`domain/external-work-acl.ts`):** landed the pure ACL — `CanonicalWorkItem` with `WorkItemProvenance` (source/externalId/externalKey/url/created/updated), `parseJiraIssue`/`parseJiraIssues` (maps Jira issues preserving provenance; rejects a missing `id` rather than fabricating identity), `mapJiraIssueType` (Epic/Story/Task/Version→canonical kind). **Transition safeguard (§9-6):** `authorizesPhaseTransition: false` by contract + `externalWorkAuthorizesTransition`⇒`false` (completing a Jira workflow does NOT authorize a phase gate). Verified: core-domain 939/939 (+6), tsc clean. **Remaining (connector/infra + doc):** the read-only Jira REST API fetch + the documented integration guide. | `Evolith Core` | Cross | P1 | L | `IN-PROGRESS` |
| [`GT-530`](./gap-reference-catalog.md#gt-530) | **[Surround · axis 1] Langfuse adapter → canonical evidence.** Map Langfuse traces/evaluations/cost/latency/prompt-version/tool-calls to Evolith's evidence model, to avoid rebuilding an LLM telemetry platform. §8.1 / §12. Fix: `LangfuseEvidenceAdapter` via the observability port. | `Evolith Core` | Cross | P2 | L | `PENDING` |
| [`GT-531`](./gap-reference-catalog.md#gt-531) | **[Surround · axis 1] Cowork/Claude adapter as a bounded governed executor.** Activity executor with permissions/plans/approvals/evidence capture, treating Claude as one of several replaceable executors (§8.2, §9). Extends the agent-runtime epic GT-383…394 (HITL GT-441 / adapters GT-438). | `agent-runtime` | Cross | P2 | M | `PENDING` |
| [`GT-532`](./gap-reference-catalog.md#gt-532) | **[Surround · axis 1] Executive portfolio views + marketplace adapters + per-tenant governance packages.** Enterprise-adoption and ecosystem-scale improvement (§12 P2); mostly Tracker (enterprise value-capture plane). | `Tracker` | Cross | P3 | XL | `PENDING` |
Expand Down Expand Up @@ -547,7 +547,7 @@ This board is the single source of truth for technical debt, gaps, opportunities
| [`GT-246`](./gap-reference-catalog.md#gt-246) | Implement Chaos Mesh/Litmus experiments | `QA` | Cross | P3 | L | `DONE` |


**Progress:** 496 / 532 done · 17 in progress · 17 pending · 2 deferred
**Progress:** 496 / 532 done · 18 in progress · 16 pending · 2 deferred

**Wave 2026-06-23 (Winston deep audit III):** Added 14 new gaps `GT-212`…`GT-225` from the Winston Audit Playbook covering: ADR status hygiene (GT-212), topology manifest metadata + operational budgets + guidance corpus (GT-213, GT-217, GT-219), REST controller observability + OpenAPI (GT-214, GT-215), OPA input-schema parity + per-topology test density (GT-216, GT-222), SDLC Phase 05 rollback + on-call templates (GT-218), CLI branch coverage + envelope format coverage + skip-list cleanup (GT-220, GT-224, GT-225), MCP HTTP audit logging (GT-221), and cross-surface parity e2e tests (GT-223).

Expand Down
72 changes: 72 additions & 0 deletions src/packages/core-domain/src/domain/external-work-acl.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {
externalWorkAuthorizesTransition,
mapJiraIssueType,
parseJiraIssue,
parseJiraIssues,
type JiraIssue,
} from './external-work-acl';

const issue = (over: Partial<JiraIssue> & { fields?: Partial<NonNullable<JiraIssue['fields']>> } = {}): JiraIssue => ({
id: '10001',
key: 'PROJ-123',
self: 'https://acme.atlassian.net/rest/api/3/issue/10001',
fields: {
summary: 'Add checkout flow',
issuetype: { name: 'Story' },
status: { name: 'In Progress' },
created: '2026-07-01T10:00:00.000Z',
updated: '2026-07-10T12:00:00.000Z',
...over.fields,
},
...over,
});

describe('mapJiraIssueType (GT-529 — Jira type → canonical kind)', () => {
it('maps the common Jira types case-insensitively, unknown otherwise', () => {
expect(mapJiraIssueType('Epic')).toBe('epic');
expect(mapJiraIssueType('user story')).toBe('story');
expect(mapJiraIssueType('Sub-task')).toBe('task');
expect(mapJiraIssueType('Version')).toBe('release');
expect(mapJiraIssueType('Spike')).toBe('unknown');
expect(mapJiraIssueType(undefined)).toBe('unknown');
});
});

describe('parseJiraIssue (ACL — preserve provenance, reject non-conforming)', () => {
it('preserves origin, identity, key, url and timestamps', () => {
const item = parseJiraIssue(issue())!;
expect(item.kind).toBe('story');
expect(item.title).toBe('Add checkout flow');
expect(item.status).toBe('In Progress');
expect(item.provenance).toEqual({
source: 'jira',
externalId: '10001',
externalKey: 'PROJ-123',
url: 'https://acme.atlassian.net/rest/api/3/issue/10001',
createdAt: '2026-07-01T10:00:00.000Z',
updatedAt: '2026-07-10T12:00:00.000Z',
});
});

it('rejects an issue with no id (no identity → no lineage), never fabricating one', () => {
expect(parseJiraIssue({ key: 'PROJ-1', fields: { summary: 's' } })).toBeNull();
});

it('falls back to key then id for the title when summary is absent', () => {
expect(parseJiraIssue({ id: '9', key: 'PROJ-9' })!.title).toBe('PROJ-9');
expect(parseJiraIssue({ id: '9' })!.title).toBe('9');
});

it('parseJiraIssues drops non-conforming items', () => {
const out = parseJiraIssues([issue({ id: '1' }), { key: 'no-id' }, issue({ id: '2' })]);
expect(out.map((i) => i.provenance.externalId)).toEqual(['1', '2']);
});
});

describe('transition safeguard (§9-6 — external work never authorizes a gate)', () => {
it('authorizesPhaseTransition is false regardless of Jira status', () => {
const done = parseJiraIssue(issue({ fields: { status: { name: 'Done' } } }))!;
expect(done.authorizesPhaseTransition).toBe(false);
expect(externalWorkAuthorizesTransition(done)).toBe(false);
});
});
Loading