Promote develop to main — the Core↔Tracker integration backend, closed end to end - #133
Merged
Conversation
…abierta Actualice la seccion de detalle al hacer el trabajo y me deje el ESTADO, tanto en la fila del registro maestro como en la cabecera de la ficha. Una fila que reclama trabajo ya hecho desvia las prioridades de la semana siguiente, que es exactamente lo que he corregido tres veces hoy en el board del Core. `docs/artifacts/CORE_ARTIFACT_SCHEMAS.md` enlaza los 14 artefactos que publican esquema canonico a su `$id`, lista los 3 que son salida de herramienta y nombra los 7 que no tienen ninguno. Aguas arriba, `evolith_arch32#378` llevo la cobertura de 10 a 17 de 24 y arreglo las rutas `schemaRef` rotas. Marcar la fila como resuelta obliga a REORDENAR: el validador exige el orden BLOCKED → OPEN → DEFERRED → RESOLVED, y GAP-020 estaba en el septimo puesto, entre las abiertas. Se reordena de forma estable —conservando el orden relativo dentro de cada grupo— y se renumera la columna `#`. Verificado que el registro no perdio nada: mismos 107 IDs antes y despues, mismo conjunto exacto, cero duplicados. Resultado: 13 OPEN, 1 DEFERRED, 93 RESOLVED. `validate-docs` pasa sobre 359 ficheros; `check-bilingual-parity`, `doc-inventory --check` y `check-gap-registry` (203 fichas / 203 filas) en verde. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cerrar GAP-020: el trabajo está hecho y la fila seguía abierta
…la nunca se movio No es una refutacion parcial: es un gap CERRADO POR SUPERSESION con ADR aceptado. `T-047` (Aceptado 2026-07-19) ratifica la topologia consolidada de cuatro esquemas, supersede a `T-028` (schema-per-context, 10 esquemas) y **nombra a COH-005 entre los gaps que cierra**, en su propio frontmatter. Nadie movio la fila. `tracker_audit` no se creo A PROPOSITO. Los agregados de auditoria viven en `tracker_governance`, donde funcionan: `audit_entries` se consulta en produccion, `GT-603` tipo su actor, y `GT-588` le enchufo hoy los statements firmados de transparencia. El propio diseno de datos lo dice, y esto es lo que convierte la ficha en un malentendido y no en una discrepancia: sus secciones 3-12 son «the original 2026-06-07 TARGET design ... kept for traceability only ... read them as intent, never as a description of the database». No faltaba capacidad. Un documento y el codigo discrepaban, y el ADR ya habia decidido cual gana. Mover una tabla que funciona, con datos de produccion y un ledger firmado encima, para satisfacer un diagrama que su propio documento etiqueta como intencion, seria trabajo al reves. Marcarla resuelta obliga a reordenar (BLOCKED → OPEN → DEFERRED → RESOLVED). Se reordena de forma estable y se renumera. Verificado que no se pierde nada: mismos 107 IDs, mismo conjunto exacto, cero duplicados. Resultado: 12 OPEN, 1 DEFERRED, 94 RESOLVED. Verificado: `validate-docs` sobre 359 ficheros y `check-gap-registry` en verde. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
COH-005 estaba resuelta por decisión desde julio y la fila nunca se movió
…te, existe
La ficha afirma que `DeploymentRecord` «NEVER created by any story» y que UC-005b
(«records a DeploymentRecord») no se puede cumplir. Comprobado contra el codigo el
2026-08-01, las dos afirmaciones son falsas:
agregado Tracker.Domain/Release/DeploymentRecord — Start, Finish, RollBack
tabla tracker_release.deployment_records
endpoints POST /, POST /{id}/finish, POST /{id}/rollback
tests ConstructionReleaseTests
De modo que UC-005b es cumplible hoy. La fila describe un estado del repositorio que
dejo de ser cierto hace tiempo y nadie la movio.
Es la tercera ficha de esta sesion que reclama trabajo ya hecho — GT-588 criterio 3,
COH-005 y ahora esta. No es casualidad: el board recoge hallazgos de auditoria y nada
los vuelve a contrastar con el codigo, asi que envejecen en silencio y siguen dirigiendo
prioridades.
Se deja en la ficha el comando con el que se comprueba antes de reabrirla.
Marcarla resuelta obliga a reordenar; se reordena de forma estable y se renumera.
Verificado: mismos 107 IDs, mismo conjunto. Resultado 11 OPEN, 1 DEFERRED, 95 RESOLVED.
Verificado: `validate-docs` sobre 359 ficheros y `check-gap-registry` en verde.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ables del board En una sola sesion se encontraron CINCO fichas reclamando trabajo ya hecho: el criterio 3 de GT-588 (cuatro sub-reglas y nueve tests negativos embarcados), COH-005 (cerrada por el ADR T-047 en julio, que la nombra), COH-008 (el `DeploymentRecord` que dice inexistente tiene tabla, tres endpoints y tests), GAP-022 y GAP-025. Las cinco se detectaron leyendo el codigo ANTES de empezar el trabajo. Quien se hubiera fiado de la ficha habria construido cinco cosas que ya existian. El board recoge hallazgos de auditoria y nada los vuelve a contrastar, asi que envejecen en silencio mientras siguen dirigiendo prioridades. Este guard es esa re-comprobacion que faltaba. LO QUE NO HACE, Y ES LA DECISION DE DISENO: no lee la prosa de las filas. El `50-validate-gap-claim` del Core ya corrio ese experimento — infirio intencion del texto de los PRs, produjo nueve atribuciones falsas en un dia y enseno a los autores a BORRAR referencias cruzadas verdaderas para aplacarlo. Aqui una afirmacion se comprueba solo cuando alguien escribe, como DATO, los simbolos y las rutas cuya presencia la refutaria. SU SILENCIO NO PRUEBA NADA, y el guard lo dice en cada corrida imprimiendo su denominador (2 declaradas de 107 filas). Puede demostrar que una afirmacion listada dejo de ser cierta; nunca que una fila sigue siendolo. UN DEFECTO PROPIO, ENCONTRADO EN SU PRIMERA CORRIDA REAL Y CONVERTIDO EN TEST: todas las afirmaciones salian «refutadas» por las propias filas que las asertan y por los informes de auditoria de los que vienen. Un documento que HABLA de un gap no es un documento que lo CIERRA, y una sonda que no distingue las dos cosas refuta todo lo que se le da — que es indistinguible de estar rota. `docs/audit` queda excluido por defecto y hay un test negativo que lo fija. Hallazgos de su primera corrida util, ya aplicados: · GAP-022 → RESUELTA. `reference/specs/metrics/prd.md` lleva `REQ-MET-03`, que define las cinco metricas SPACE con criterios de aceptacion. · GAP-025 → ESTRECHADA. El mapa estrategico existe y se autodenomina «the single, authoritative strategic map» de los 9 contextos; queda abierta solo la mitad de los cuatro contextos de soporte, y su sonda se RETIRA porque «cuales cuatro» pide criterio y una sonda que fingiera comprobarlo refutaria la fila con cualquier documento que mencione un contexto. 7 self-tests, cinco de ellos negativos. Cableado al job `gap-registry` junto a su propio self-test. Verificado: registro intacto (mismos 107 IDs), `validate-docs`, `check-bilingual-parity` y `check-gap-registry` en verde. Resultado del board: 10 OPEN, 1 DEFERRED, 96 RESOLVED. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
COH-008 está refutada: el agregado que dice que no existe, existe
…equisito si La fila dice «Redis in Docker Compose Without Requirement». Comprobado contra el repositorio el 2026-08-01, las dos mitades son falsas: LA PREMISA NO EXISTE. No hay ningun fichero `docker-compose` en este repositorio, ni Redis en los charts de Helm de `product/infra/helm/` —que son el despliegue real— ni Redis en el codigo de aplicacion. Y EL REQUISITO QUE DICE FALTAR ESTA DECIDIDO. `T-026` («Redis solo para soporte operacional») esta registrado en `AdrRegistryEndpoints`: «Redis is operational support only (cache/locks/jobs/idempotency); PostgreSQL is the system of record». La fila pide un requisito que existe, sobre un fichero que no. Es la sexta ficha de esta sesion que reclama trabajo hecho o describe un estado que dejo de ser cierto. El guard `check-falsifiable-claims` que se anadio hoy existe por esto, aunque esta no era declarable como sonda: su afirmacion es sobre la AUSENCIA de un fichero de infraestructura, no sobre la presencia de un simbolo. Marcarla resuelta obliga a reordenar; reordenamiento estable y renumeracion. Verificado: mismos 107 IDs, mismo conjunto. Resultado 9 OPEN, 1 DEFERRED, 97 RESOLVED. Verificado: `validate-docs` y `check-gap-registry` en verde. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GAP-021 refutada dos veces: el fichero no existe y el requisito sí
…ea que tenia era falsa `GAP-015` pide el racional tecnico de T-001. Verificarlo produjo un segundo hallazgo que la fila no menciona: **la unica linea que la entrada SI tenia era inexacta.** Decia «Inicializado en `src/` utilizando npm workspaces con Nx». Ningun `package.json` de este repositorio declara un campo `workspaces` — comprobado sobre todos ellos. Nunca se usaron. Nx orquesta por GRAFO DE PROYECTOS: `project.json` mas los plugins de inferencia `@nx/vite`, `@nx/webpack`, `@nx/eslint` y `@nx/jest` declarados en `src/nx.json`. Eso importa mas que la ausencia del racional: quien leyera la entrada buscaria un array `workspaces`, no lo encontraria, y concluiria razonablemente que el monorepo esta mal configurado — cuando lo cierto es que esta configurado de otra manera. El ADR registra las tres razones, en el orden que decidio: 1. El repositorio NO es homogeneo. `tracker-api` es .NET, y npm workspaces enlaza `node_modules` entre paquetes npm: no tiene opinion sobre un `.csproj` ni puede ordenar un `dotnet build` frente a un `vite build`. Habria cubierto tres proyectos de cuatro dejando fuera al mayor. 2. Inferencia antes que declaracion, porque una lista de targets escrita a mano deriva — el mismo modo de fallo que ya costo tiempo en `GT-647`, `GT-640` y `ADR T-038`. 3. `nx affected` vale lo que valgan sus aristas; sin ellas, «ejecuta lo que cambio» degenera en «ejecuta todo» y se va el valor que justificaba el monorepo. Se registra tambien el COSTE, que un ADR sin contrapartidas es propaganda: Nx pineado en `^22.7.5` hace de una subida mayor un evento de todo el repositorio. La entrada de `DECISIONS.md` queda corregida en ambos idiomas y enlazada al ADR. Verificado: `validate-docs`, `check-bilingual-parity`, `doc-inventory --check` y `check-gap-registry` en verde; registro intacto (mismos 107 IDs). Resultado del board: 8 OPEN, 1 DEFERRED, 98 RESOLVED. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GAP-015: el racional de T-001, y la línea que sí tenía era falsa
…en ingles» estan en castellano GAP-011 y GAP-017 dicen que el C4 y el Discovery Canvas no tienen version inglesa. El fichero `.md` EXISTE en ambos casos y se autodescribe «English (this document)». Su contenido esta en castellano. Por eso las dos filas seguian siendo ciertas con `check-bilingual-parity` en verde: ese guard compara que el par EXISTA y que las cabeceras cuadren, y ninguna de las dos cosas ve el idioma. MEDIDO: 9 de 143 documentos emparejados estan asi, y las filas solo nombraban dos. Entre los otros siete estan `DECISIONS.md` (128 marcadores de castellano contra 1 de ingles) y `MASTER_INDEX.md`, canonicos de raiz. COMO SE DETECTA: frecuencia de stopwords, con MARGEN de 2x y nunca por mayoria simple. El margen se eligio midiendo — los nueve casos reales estan en 10x o mas y el documento legitimo mas cercano ronda la paridad; una mayoria simple habria marcado glosarios bilingues. Se retira el codigo antes de contar, porque un fichero lleno de identificadores en castellano no es un documento en castellano. VA COMO TRINQUETE: los nueve se declaran con su motivo, asi que CI pasa hoy y el DECIMO falla. La regla escrita en el propio fichero dice que «no es prioritario» no es un motivo. Comprobado que muerde: copiando un documento en castellano como `.md` se pone rojo nombrandolo, y vuelve a verde al retirarlo. LIMITACION QUE CONVIENE SABER: va en `docs.yml`, que solo se dispara en PRs a `main` —como todos los guardianes de doc de este repositorio—, asi que muerde al PROMOVER y no en el PR que introduce el fichero. Es coherente con el resto y se deja dicho en vez de descubrirse dentro de un mes. Verificado: `validate-docs` y `check-gap-registry` en verde; YAML del workflow validado. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ratchet-v2 El guard de paridad no ve el idioma: nueve documentos «en inglés» están en castellano
…sion ES La fila dice «14 Technical Design Docs Lack ES Version». Medido el 2026-08-01: `reference/specs/design/` tiene 20 documentos y TODOS tienen su `.es.md`. Cero faltan. En todo el repositorio hay 19 ficheros `.md` sin pareja en castellano, y conviene mirar CUALES antes de contarlos como deuda: NUEVE estan bajo `docs/audit/` —informes de auditoria, triajes de corpus y este mismo registro—, que son documentos de trabajo y no documentacion de producto. Los otros diez son READMEs y notas de tarea (`robosoft/README.md`, `product/infra/helm/README.md`, `Tracker.ArchitectureTests/README.md`, `docs/tasks/*`). Ninguno es un diseno tecnico. LO QUE SI SOBREVIVE, y es distinto: tener la pareja no es tener la traduccion. El guard `check-translation-language` anadido hoy encontro 9 documentos cuyo `.md` esta escrito en castellano pese a existir el par — que es exactamente lo que describen GAP-011 y GAP-017, y siguen abiertas. Es la septima ficha de esta sesion que reclama trabajo hecho o describe un estado que dejo de ser cierto. Reordenamiento estable y renumeracion; mismos 107 IDs, mismo conjunto. Resultado: 7 OPEN, 1 DEFERRED, 99 RESOLVED. Verificado: `validate-docs` y `check-gap-registry` en verde. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GAP-013 refutada: los 20 diseños técnicos tienen su versión ES
The row asks for one business-rule id carrying two rules to be split. The business-rule table already carries the split, and credits this row for it: BR-010, CFR Quality Threshold, described as escindida de BR-003 por COH-009; BR-003 es solo la firma humana. Verified across the repository rather than from that one line: every BR-003 occurrence outside the audit tree means Human Sign-Off and nothing else, and the QA-gate half is cited as BR-010 by reference/specs/qa, by the test strategy against PhaseGateEvaluator, and by the Re-Do flow design. This is the eighth row found describing work already done. Its claim is not machine-falsifiable (it is about meaning, not presence), so it is NOT added to falsifiable-claims.json: that file takes only what a probe can refute without judgement, and a regex over prose is the failure mode it exists to avoid.
docs(audit): close COH-009 — BR-003 conflation already split into BR-010
…1, GAP-017 Both rows were TRUE, and both had been marked DONE on the other board while the defect was still there. c4-macro-topology-phase1.md carried the banner English (this document) and 86 of its 87 non-empty lines were byte-identical to the Spanish file: only the navigation banner had been translated. DISCOVERY_CANVAS.md was 23 of 24. Both passed check-bilingual-parity, which compares that the pair exists and that the headers match, and can see neither language nor content. Both are now genuinely English — prose, mermaid node descriptions and relationship labels — and their entries are removed from untranslated-allowlist.json, which drops from 9 declared to 7. That file is a list of promises, so shrinking it is the point. Verified after translating that no line still identical to the Spanish file contains Spanish prose: the remaining overlap is fences, braces and identifiers.
docs(i18n): translate C4 topology and Discovery Canvas — close GAP-011, GAP-017
…12, GAP-016 COH-012 asked for the two missing CRUD operations. Nine scenarios added across three Features: Create (type derivation for 0/1/2/7 products against InitiativeScope.Derive, the Initiative.TenantRequired guard, ConsolidateFrom lineage), Read (own-tenant hit; other-tenant read returning exactly what a non-existent id returns) and withdrawal. The row's premise is corrected, not obeyed. There is no Delete in this aggregate and there must not be: an initiative is superseded into another (Supersede -> consolidated, BR-DIS-005) because its lineage is governance evidence. A Scenario: Delete would have satisfied the row, satisfied the probe that was watching for exactly that string, and described a system that does not exist. The COH-012 entry is withdrawn from falsifiable-claims.json with that reason recorded in the file rather than deleted silently — a probe inherits whatever its row assumed, and a row can assume something false. Constants in the scenarios were read from the code, not guessed: TENANT_WIDE, SINGLE_PRODUCT, CROSS_PRODUCT, consolidated, Initiative.TenantRequired, Initiative.NotConsolidatable. GAP-016 was stale. The roadmap has carried Document Status: SUPERSEDED since it was corrected, and the marker cites this row by id. Note on coverage: with COH-012 withdrawn and GAP-022 resolved, the falsifiable-claim guard now probes ZERO claims and says so on every run. That is the guard working as designed, not a pass.
docs(discovery): cover Create/Read/Supersede in Gherkin — close COH-012, GAP-016
…025, file COH-016
This repository keeps THREE gap surfaces sharing one id namespace: the register
(tracker-gaps-opportunities-tracking.md), the board (tracker-gap-tracking.md)
and the catalog (tracker-gap-reference-catalog.md). check-gap-registry.py kept
the last two in step since the day it was written, and never looked at the
first.
Measured today: 34 ids in common, 5 of them contradicting each other. And
neither surface was the trustworthy one — the board claimed GAP-011 and GAP-017
done while both files were still Spanish, and the register kept three rows open
that the catalog had closed with evidence in July.
Reconciled, each verified against the repository rather than against the other
board:
GAP-023 -> RESOLVED. The 254-line Re-Do design exists with state model,
recalculation algorithm, events and traceability. Its five open items are
scheduling decisions awaiting PO ratification, not missing design.
GAP-025 -> RESOLVED. bounded-context-map.md names the four supporting
contexts and all ten ddd-model.md files exist.
GAP-020 -> REOPENED. This corrects my own closure from earlier today. The
catalog reopened it by product direction and the reason stands: the Tracker
must CONSUME the Core schema references, not merely document them.
The guard now compares the two vocabularies on the only thing both state
unambiguously — closed or not — because inventing an OPEN->PENDING mapping
would be a decision dressed up as a check. Six self-tests; three go red when the
comparison is disabled, verified by disabling it.
COH-016 records the finding so the next reader does not rediscover it.
chore(audit): contrast the third gap surface — reconcile GAP-020/023/025, file COH-016
… GAP-020 The row was reopened by product direction with a precise complaint: a satellite that only DOCUMENTS the link has not consumed it. CORE_ARTIFACT_SCHEMAS.md let a reader find the canonical shape of a PRD; no machine could. The correspondence now lives as data in CoreArtifactSchemaCatalog, and every artifact served by GET /api/phase-artifact-profiles carries a canonicalSchema with the published $id. The artifact-fields screen renders it as a link. provenance has THREE values and not a nullable id, because two of them are different absences: core-schema, tool-output (the Core deliberately publishes none — CI run records, coverage reports) and none (nothing upstream; the tenant's ArtifactFieldSchema is the only authority). Collapsing the last two into null would erase the difference between a gap someone should close and a decision already taken. The mapping is a WRITTEN DECISION, not a slug transformation. A matcher would miss security-scan-result vs Security Scan Report, rollback-plan vs Rollback Procedure and observability-readiness vs Observability Validation, and would miss them silently. So CoreArtifactSchemaCatalogTests parses the markdown table and compares BOTH ways: a documented row nobody wired fails, and a wired entry nobody documented fails too. Verified by breaking it in both directions rather than assuming. adr-registry is deliberately NOT mapped to adr.schema.json: a registry is a list of ADRs and not an ADR, so the mapping would validate green on paper and produce a false negative against the real artifact. DELIBERATELY NOT BUILT: hardwired validation of artifact content against these schemas. The reopen text asks for it, and T-056 — ratified by the user — says content validation is the tenant's configuration and not engine code. The reference is exposed so a tenant's own configuration can point at it; enforcing it here would make the Core the authority over what a tenant may record. Flagged rather than silently skipped. Local: 6/6 new tests, frontend lint+typecheck+build green. The 10 integration failures on this machine are the DB-gated suite that fails by design without PostgreSQL; CI provides it.
…four Correcting my own bookkeeping before merging. The reopen asked for four things: consume the references, persist or cache them tenant-aware, expose them in the phase forms, and use them to validate what gets filled in. This PR does the first and the third. Marking the row RESOLVED would be the exact failure this session has spent its time undoing — a row that reads as done while a stated requirement is unbuilt. What remains is recorded on the row: tenant-aware persistence (arguably unnecessary, since the references are Core-authoritative and tenant-agnostic, but that judgement has not been ratified) and content validation, which collides with T-056 and needs a product decision before anyone writes code for it.
…schemas feat(governance): consume the Core artifact schema references — GAP-020 (2 of 4)
…020; unblock GAP-004's truth Started as GAP-020 bookkeeping and turned into three rows that were lying, found by extending the coherence guard to a fourth check. GAP-020 — closed. Two of the four asks shipped in #108. Content validation is WITHDRAWN by product direction (2026-08-02): T-056 rules, so the Tracker offers the canonical shape and never enforces it, and the schema document now says so as a ratified decision rather than an omission. Tenant-aware persistence is handed to GAP-004: without validation a cache has no consumer, and the version that matters is syncing the catalog from the Core, which is the IPhaseArtifactProfileSource seam GAP-004 owns. COH-006 / COH-007 — closed by fixing the DOCUMENTS, not by building tools. Both rows claimed tracker-gateway exposes 6/6 Tracker MCP tools. It does not, and its controller says so: the Core remains the single agent-facing MCP server, this is a client of it. History: built (c5f3a50), rebound to the BFF (293e3de, T-051), DELETED (614ddff) when T-052 made the Core's evolith-mcp the single surface. So the original finding was live again in its own terms — documents promising a surface that does not exist — with a different remedy. Six documents aligned: PRD 5.3 (EN+ES) rewritten with where each capability is served now, the local-dev guide (EN+ES) whose smoke command pointed at a 404, and banners on ux-concept, blueprint and tracker-agent-assignment-api. Text kept rather than deleted: the capabilities are real and served by the BFF; only the transport is gone. GAP-004 — corrected to BLOCKED, matching the card that was right all along. It had been closed with 'Resolved via Defensive Isolation (Mocks)'. Installing a stand-in is not resolving a dependency, and the code agrees: the stand-in is still the only implementation and every profile it serves is stamped core-standin. The guard now contrasts row against card inside the register — 3 of 108 disagreed, all three with the row claiming closure. Three new tests; the new check goes red when disabled, verified by disabling it.
…t052 docs(mcp): align six documents to T-052 — close COH-006/COH-007, GAP-020; correct GAP-004
…am as GT-650 The row said 'Define owner and remediation action before execution' and nobody ever had. Each of the three upstream dependencies was probed today rather than assumed. Core API. The root cause is not a missing endpoint, it is that the Core has no single answer to endorse: two unreconciled artifact corpora that disagree on names, on membership in both directions, and on phase — Coverage Report is a construction artifact in one and a quality artifact in the other — while the HTTP surface serves a third file with no artifacts at all. Filed upstream as evolith_arch32 GT-650 (PR #383) with five acceptance criteria. Cannot be unblocked from this repository. UMS JWKS. Still blocked; T-053 is still Proposed. Probed today: the local UMS ingress answers 503 on every path, so nothing could even be re-tested. The Tracker already carries both validation modes, so this blocks the target state rather than the product. UMS Auth Graph. Same system, same reachability, still blocked. Also recorded: the residue of GAP-020 lands here, because tenant-aware sync of the artifact catalog is the same seam and the same blocker.
docs(audit): scope GAP-004 blocker by blocker; Core half filed upstream as GT-650
Closes CP-05 criterion 1.
The finding was better than the card's statement. The problem was not missing
columns: `correlation_id`, `schema_version`, `overall_verdict`, `outcome`,
`rules_executed`, `evaluated_at` and `versions` already existed, but only the
INGEST path filled them. A row born in the gateway left them empty even when the
Core's response carried the values.
A column that exists and is empty for half the population is worse than no
column, because whoever queries it cannot tell "there was none" from "this path
did not store it".
`CompleteFromCore` fills them and stamps `producer_surface = tracker-gateway`,
so a gateway row and a CLI-deposited one are distinguishable in the table itself
rather than by guesswork.
What has no column of its own -- policiesApplied, risks, recommendations,
requiredActions, qualitySignals, the result kinds -- is preserved in the
`response_data` jsonb that already existed. NO MIGRATION AND NO SPECULATIVE
SCHEMA: the typed columns that exist were typed when somebody needed to query
them (route by owner, count blockers). When criterion 2's UI says what it
actually queries, that gets typed and not before.
`CoreEvaluationDeposit` was deliberately NOT reused. It is the INGEST shape and
requires violations with a fingerprint and an accountable owner, which the
gateway never receives; filling those here would be exactly what that shape
forbids in writing -- inventing an accusation.
Two judgement calls, both with a test:
- a rule with no engine is RECORDED as `unknown`, not dropped. Dropping it
would erase the evidence that the rule ran; saying "I do not know which
engine" is true and checkable. Coercing it to a known engine is the one
unacceptable option, and the contract says so itself.
- `correlation_id` stays NULL when the Core issued none. Copying the Tracker's
`operation_id` into it would add no information -- it already has its own
column -- and would make a Core-issued id indistinguishable from ours.
Verified: 6/6 new, full suite 1200 passed / 10 failed, and the 10 are the same
environment-dependent set that fails on a clean worktree at HEAD. Zero new.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…re-facts feat(ledger): a gateway-born row stops being born half empty
Closes CP-06 (all four criteria).
The Tracker decided phase advance from the Core's `overallVerdict` plus a count
of blocking gaps. Nothing consulted tenant configuration, so the authority
boundary was blurred in both directions: an optional technical signal could stop
a phase, and a contractual obligation of the tenant could sit there as a warning.
`CoreSignalRule` is a matrix per tenant AND phase, hung off the GatePolicy that
already carries the tenant's gate configuration. Dispositions: blocking,
advisory, hidden, not-applicable, with an optional minSeverity. It is configured
through the existing `PUT /gate-policies/{phase}` and persisted in three new
jsonb columns (additive migration, with a Down).
Six decisions, each with a test that fails against the alternative:
- THE DEFAULT IS ADVISORY. A signal with no rule is reported and does not
block. The opposite default would turn every new dimension the Core
publishes into a block no tenant asked for -- the product forming opinions
of its own, which T-056 forbids. Erring toward "does not block" is
recoverable by reading the report; erring toward "blocks" on a phase in
flight is not.
- THE MOST SPECIFIC RULE WINS, not the first in the list. Ordering by position
would leave the outcome at the mercy of how a tenant sorted their list,
which is not a decision anyone consciously made. Tested in BOTH orders --
with only one, a first-match-wins evaluator passes.
- AN UNKNOWN SEVERITY IS NOT COERCED. Raising it would fabricate blocks and
lowering it would hide findings, so it falls outside min-severity rules and
stays visible through the default path.
- A GATE WITH NO MATRIX BEHAVES EXACTLY AS BEFORE. Without this, landing the
card would have changed the behaviour of every already-configured gate
without any tenant asking.
- AN UPSERT WITHOUT `coreSignalRules` CLEARS THE LIST rather than keeping what
was there. Preserving unsent rules would make removing a block impossible,
and removing one is precisely what a tenant needs to do quickly.
- AN INVALID DISPOSITION IS REJECTED ON WRITE. Storing it and ignoring it
later would give a tenant who believes they configured a block and a gate
that does not apply it.
Only `blocking` folds into unmet reasons; advisory/hidden/not-applicable are
recorded and never stop the phase. `AppliedCoreSignalRules` travels COPIED with
the submission rather than referenced -- a tenant's matrix changes, and without
that copy an audit would have to assume today's configuration is the one that
decided.
Not covered, stated rather than implied: tracker-web does not expose the matrix
yet, so it is configured through the API. And the caller still builds the
observations -- feeding the Core facts CP-05 now preserves in `Intelligence`
into `CoreSignalObservation` is the next stretch.
Caught in passing: the two new collections broke `PropsDeepCloneTests` (GT-470's
guard, which clones for real rather than grepping for `this with`). Both Props
clone hooks fixed.
Verified: 14 new tests; full suite 1214 passed / 10 failed, the same
environment-dependent set that fails on a clean worktree at HEAD. Zero new,
compared set-by-set.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(governance): the tenant decides which Core signals block a phase
…l shape
Advances CP-04: criteria 1 and 4 closed, criterion 2 closed, criterion 3 unstarted.
The Tracker sent kinds, workspaceRef, phaseId and a passthrough bag. Everything
else it knew about a request -- who asked, which revision was judged, which
contract version it speaks -- either did not travel or travelled as untyped
strings the Core echoes without interpreting.
- `schemaVersion` is a fact of the CALLER ("I speak this version"), not an
assumption about the Core. The capability manifest was no help: its
`schemaVersion` is the RESULT contract's. Configurable precisely because it
is a copy of a constant that lives in the other repo -- the day the Core
moves its own, this is adjusted rather than left quietly lying.
- `requester` and `repositoryRevision` now travel TYPED. `repositoryRevision`
leaves `passthrough`, which the Core echoes without interpreting: the fact
was present and took no part in the evaluation. No copy left behind, because
there was no consumer -- verified on all three sides: the Core reads only
`evaluationDate` from passthrough, and neither the Tracker nor the web reads
the passthrough it gets back.
- the optional technical fields are gated OFF by default, and there is nothing
to send today: the Tracker produces no structural facts and no quality
signals. Sending them empty would tell the Core "I looked and found nothing"
when the truth is "I did not look", and the Core reads absence as no
structural findings, never as a failure.
Criterion 2 was IMPOSSIBLE until today. The Core runs a global ValidationPipe
with `forbidNonWhitelisted: true` and its `EvaluationContextDto` declared
neither field, so sending them would have 400'd the whole evaluation rather than
arriving stripped. Registered and fixed as GT-652 in evolith_arch32 (PR #401).
DEPLOY ORDER IS THEREFORE CORE FIRST -- stated rather than discovered.
`SendTypedContextFacts` (on by default) returns the payload to the previous
minimal shape, so a Tracker pointed at a pre-GT-652 Core is fixed with an
environment variable instead of a rollback. Both switch positions have a test.
Absence stays absence: with no known revision the field is omitted rather than
sent as `HEAD`, which would be indistinguishable from a real revision named HEAD.
`requester` is omitted with no actor -- an empty one would assert an attribution
we do not have. `actorType` is `user` because the Tracker authenticates people
and services only; claiming `agent` would be guessing.
Criterion 3 is untouched and says so on the card: `artifacts`/`evidence` ARE
accepted by the wire, but `EvaluateContextRequest` receives them from nobody --
the endpoint would have to take the phase record (the one CP-02 already
contrasts against the Core catalogue) and translate it.
Verified: 6 new tests, 16/16 in the class; full suite 1220 passed / 10 failed,
the same environment-dependent set that fails on a clean worktree at HEAD.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…n-context feat(integration): the context the Tracker sends stops being a minimal shape
Closes CP-04 criterion 3 — and with it, CP-04.
`artifacts` now travels in the shape the Core declares (`ArtifactContext`):
`required` from the Core-DERIVED catalogue CP-02 wired, `presented` from the
record people fill. It is the join between the two cards -- the same seam, not a
second copy. Sending only what was presented would leave the Core with nothing
to contrast it against; sending only what is required, with no idea what is there.
THE SERVER BUILDS IT, NOT THE CALLER. Both sources are injected into the gateway
and `EvaluateContextRequest` still has no artifact field -- there is a test on
the TYPE that breaks if anyone adds one. If it travelled in the body, a caller
could declare an artifact that does not exist as presented, and the Core would
evaluate it as though it were there.
Three decisions, each with a test:
- AN EMPTY SLOT IS NOT PRESENTED. Only items with a `ref` or a body travel;
declaring a blank slot somebody opened in the form would tell the Core the
artifact is there.
- INLINE CONTENT DOES NOT LEAVE THE PERIMETER. The reference travels, never
the body: the Core evaluates DECLARED facts, and dumping the document here
would take out of the tenant something nobody asked to send.
- WITH NEITHER HALF, THE BLOCK IS OMITTED. Two empty lists would assert "this
gate requires nothing and nothing was presented", which is a different
evaluation from "I could not assemble the package". Same reasoning for an
opaque `initiativeId`, which the contract explicitly allows: there is no
record to look up, so it says so rather than logging a failure for something
permitted.
`evidence` is deliberately NOT sent. In the Tracker, evidence is the
`GateSubmission` slots -- a different aggregate this endpoint does not hold.
Sending the record's items under that key would rename them to a concept they
are not.
Wiring verified, not assumed: the repository is Scoped and the typed client is
transient, so a test resolves the gateway INSIDE a scope and checks the container
fills both dependencies. Confirmed by removing the registration and watching it
fail. "Built but not wired" is the defect this campaign keeps finding.
Verified: 8 new tests; full suite 1228 passed / 10 failed, the same
environment-dependent set that fails on a clean worktree at HEAD. Zero new.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(integration): the phase package travels, and the server builds it
Closes CP-07 criterion 1.
`GT-604` gave the Core a place to deposit a verdict it produced. The row was
auditable as a technical ledger and appeared nowhere near the phase somebody was
trying to close: no typed link to an initiative, a phase, an artifact or a gate
submission.
WHY A LINK AND NOT A FIELD ON THE DEPOSIT. `EvaluationIngestPayload` carries
neither initiative nor phase, and should not: whoever deposits is the CLI, the
MCP or the drift gate, and none of them knows which Tracker initiative the thing
they evaluated belongs to. Attaching is therefore a governance decision taken
afterwards by somebody who does know.
Five decisions, each with a test:
- ATTACHING DOES NOT TOUCH THE VERDICT. `overallVerdict`, `outcome`,
`violations` and the blocking count come out identical. It is what separates
a link from a second opinion: a method that could adjust the verdict would
stop the ledger being what the producer deposited.
- IT DOES NOT CROSS THE TENANT BOUNDARY, in both directions. Attaching is the
operation that joins two aggregates -- exactly where a leak between clients
would slip in. Someone else's deposit answers NOT FOUND rather than
forbidden, because telling them apart would make this endpoint a way to
discover which ids exist in another client.
- ITS OWN PERMISSION, `tracker:core-transaction:attach`, separate from ingest.
Depositing is a MACHINE operation; attaching is a GOVERNANCE decision. The
machine handler does not grant it, so a leaked key cannot hang evidence on
the phase somebody is trying to close.
- YOU CAN DETACH, NOT UN-DEPOSIT. Attaching to the wrong place is an ordinary
human error and must be reversible; what is not undone is the record of what
the Core said.
- ABSENCE STAYS ABSENCE: a blank `artifactKind` stores as null. "It did not say
which artifact" is not "it speaks about the artifact named empty-string".
Caught by the repo's own guard: `CoreEvaluationTransactionContractTests` pins the
emitted JSON field-for-field against `tracker-web/src/api/types.ts`, so adding
`attachment` reddened it until the TypeScript side declared it too. Both updated;
`tsc --noEmit` clean.
Verified: 9 new tests; full suite 1237 passed / 10 failed, the same
environment-dependent set that fails on a clean worktree at HEAD. Zero new.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…atrix
Closes CP-07 criterion 3 — and the stretch CP-06 recorded as pending.
CP-05 preserves what the Core returns, CP-06 built the matrix that decides what
blocks, CP-07 let a deposit hang off a phase. Nothing produced a
`CoreSignalObservation`, so the matrix existed with no input: "built but not
wired", this time in pieces of my own from earlier in this campaign.
`CoreDepositSignals` translates an ATTACHED deposit into observations;
`CoreSignalGateEvaluator` runs them through the tenant's rules inside
`EvaluateGateSubmission`.
It TRANSLATES, it does not decide -- no thresholds, no severity normalisation.
Five decisions, each with a test:
- ONLY ATTACHED DEPOSITS ENTER, filtered by initiative AND phase. A loose one
speaks about no phase at all; one hanging off the same phase of a different
initiative has nothing to say about this gate.
- THE VIOLATION'S CATEGORY IS THE SIGNAL when there is one, which is how a
tenant writes "architecture violations deposited on me block, style ones do
not". With none it falls to `core-deposit`, a stable documented name --
inventing a category would be guessing what it speaks about.
- THE VERDICT IS OBSERVED even with no typed violations. Not redundant: a
producer can return FAIL and nothing else, and without that observation the
case would be invisible to the matrix.
- A FROZEN VIOLATION IS NOT RE-OBSERVED. It is one the tenant already accepted
knowingly, and the ledger already excludes it from its blocking count;
counting it here would make two surfaces of the same product disagree about
the same fact.
- NO MATRIX CONFIGURED RETURNS NULL and the gate decides exactly as before,
like the T-031 criteria alongside it. With a matrix and no deposits it DOES
evaluate, with zero observations: "nothing blocked" is true and belongs in
the trace, whereas null would erase from the audit that the matrix was
active and found nothing.
Verified: 8 new tests; full suite 1245 passed / 10 failed, the same
environment-dependent set that fails on a clean worktree at HEAD. Zero new.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(integration): a Core deposit can hang off the SDLC record
…h-matrix feat(governance): attached deposits run through the tenant's signal matrix
…ndard
Closes CP-02 criterion 2 — the other half of that card's title.
`ArtifactApplicabilityRule` hangs off `GatePolicy`, which is already per tenant
and phase, and marks an artifact `required`, `optional` or `not-applicable`.
"WITHOUT MODIFYING THE BASE STANDARD" IS TRUE IN THE CODE, not just on the card.
`IPhaseArtifactProfileSource` stays tenant-agnostic: it serves what the Core says
and takes no tenant. The overlay is applied on top at serve time, in
`TenantPhaseArtifactCatalog`, so there is no path by which a tenant's
configuration edits the Core's catalogue. There is a test that applying the
overlay does not mutate the input profile.
Five decisions, each with a test:
- MARKING IS NOT DELETING. A `not-applicable` artifact is still served, with
the mark on it. Removing it would leave a reader unable to tell that the
standard asks for it and this tenant waived it -- two different facts, both
auditable.
- WHO REQUIRES IT IS DISTINGUISHABLE. `tenantApplicability` and
`standardRequired` travel separately; serving only the outcome would leave
an auditor unable to separate "the Core requires it" from "this tenant
requires it".
- A MARK ON SOMETHING THE CORE DOES NOT DECLARE IS IGNORED. Adding
tenant-specific artifacts is a different capability and a different card;
accepting it here would turn the catalogue from "the Core's standard with an
opinion on top" into two catalogues mixed with no way to tell them apart.
- AN UPSERT WITHOUT THE OVERLAY CLEARS IT rather than keeping what was there:
otherwise returning an artifact to the standard would be impossible.
- AN INVALID APPLICABILITY IS REJECTED ON WRITE, for the same reason as in the
signal matrix.
ALL THREE SURFACES READ THE SAME OVERLAID CATALOGUE, which was the real risk of
leaving this half-done: the profiles endpoint, the phase-record contrast
(criterion 1), and the `artifacts.required` sent to the Core during evaluation.
Sending the Core the bare standard while the Tracker governs with the overlay
would have been, once again, two surfaces disagreeing about the same fact.
Additive migration with a Down. Two existing test doubles moved to the
tenant-aware interface -- the intended consequence of the change, not collateral.
Verified: 8 new tests; full suite 1253 passed / 10 failed, the same
environment-dependent set that fails on a clean worktree at HEAD. Zero new.
`tsc --noEmit` clean on the web app.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Advances CP-16: criteria 1, 2 and 4 closed; criterion 3 partial and said why.
The Tracker had a technical ledger for evaluations and scattered domain records,
and no business timeline explaining what happened to an initiative, who took
part, and what kind of fact each event was.
IT IS A PROJECTION, NOT A SECOND LEDGER. Every entry derives from an aggregate
that already holds it -- audit, gate submission, attached deposit -- and there is
no write path of its own. A parallel store would have its own writes and could
drift from the aggregates it summarises: the ledger would say one thing and the
submission another, with nothing to flag it. Deriving makes that divergence
impossible by construction, at the price of reading from three places.
Criterion 2 needed almost nothing built: the audit entry already carried
`ActorType`, `AgentId`, `ModelId`, `SessionId` and `CorrelationId`. The ledger
projects them rather than re-recording them.
Five decisions, each with a test:
- THE TENANT IS CHECKED AT EVERY SOURCE, not just at the entrance. A query by
initiative that trusted the id to belong to the right tenant would be a leak
shaped like a listing.
- A LOOSE DEPOSIT IS NOT THIS PHASE'S HISTORY. Only attached ones enter
(CP-07): including one nobody placed there would count as the initiative's
journey something that is not.
- WHAT DOES NOT CLASSIFY FALLS TO `record` AND IS NOT FORCED. Marking as
"blocked" something that merely resembles one by name would be inventing
history on an audit surface.
- THE CORE'S VERDICT IS NOT RE-CLASSIFIED AS A BLOCK HERE. What blocks the
phase is the tenant's matrix (CP-06), whose dispositions already travel as
their own entries; classifying it twice would duplicate the decision.
- WITH NOTHING RECORDED THE LEDGER IS EMPTY. Filling gaps with assumptions
would turn an audit trail into a narrative.
Criterion 3 stays open and says why on the card: artifact versions are CP-14 and
exports are CP-17, neither of which records anything yet. You cannot link what is
not written down.
Verified: 8 new tests; full suite 1261 passed / 10 failed, the same
environment-dependent set that fails on a clean worktree at HEAD. Zero new.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(governance): the tenant marks artifacts without touching the standard
feat(sdlc): the initiative ledger, derived rather than stored
Advances CP-14: criteria 1, 2 and 4 closed; criterion 3 unstarted and said so.
`SetItem` mutated in place, so every edit erased the previous one. "Which version
was sent to the Core" and "which one was approved" had no answer.
`ArtifactVersion` lives INSIDE the aggregate, so an item and its history cannot
diverge -- a separate store would have its own write path and could disagree
with the item it describes.
The actor already travelled in `SetPhaseArtifactItemCommand` and was used for
nothing. It is now what turns an edit into traceable history.
Five decisions, each with a test:
- A VERSION IS NEVER EDITED. What is immutable is the content and its
authorship; the STATUS does transition, because moving to `superseded` is
precisely the record that another replaced it. Conflating the two would mean
either losing that record or duplicating it somewhere else.
- RESTORING OPENS A NEW VERSION AND DOES NOT REWRITE THE PAST. What happened in
between stays in the history: making it look like the edit never happened is
exactly what an auditable record cannot do.
- A SAVE THAT CHANGES NOTHING CREATES NO VERSION. Filling the history with
noise would make the real change harder to find, which is what the history
exists to allow.
- NO AUTHOR, NO VERSION -- but the record still updates. Storing a half-filled
version would give a history that looks traceable and is not.
- A `ref` AND A `content` WITH THE SAME TEXT DO NOT SHARE A CHECKSUM. They are
different things, and confusing them would claim two versions carry the same
when they do not.
Criterion 4 holds by construction: an approved version moves to `superseded` and
the edit opens the next one. If approved content could change, the approval would
stop saying anything about what was approved.
RISK TAKEN AND STATED: versions live in the aggregate's `versions` jsonb, so
versioning inline content multiplies a row's volume. It is not a NEW class of
problem -- content is already stored inline in `items` -- but it does worsen it.
If the volume becomes real the way out is a per-version store; recorded on the
card rather than discovered as a 40 MB row.
Also corrected CP-16's note, which named CP-14 as a blocker for its criterion 3.
It no longer is: versions exist, and linking them in the ledger is a concrete
pending stretch rather than a dependency.
Verified: 10 new tests; full suite 1271 passed / 10 failed, the same
environment-dependent set that fails on a clean worktree at HEAD. Zero new.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(sdlc): artifact versions, with a history that cannot be rewritten
Closes CP-14 criterion 3, and advances CP-16 criterion 3 to exports-only.
Versions existed but nothing referred to one. "Which version was approved" and
"which one did that evaluation judge" were still unanswerable the moment somebody
edited the artifact.
Three links, and one deliberate NON-link:
- the deposit attachment (CP-07) carries `ArtifactVersion`;
- approving a gate SEALS the live version of every artifact in the phase;
- the ledger surfaces each version as an entry with author, reason and number.
WHERE THE VERSION DOES NOT GO: the payload sent to the Core.
`ArtifactContext.presented` does not declare a version, so sending one would
produce a field that arrives and nobody reads -- the same defect GT-652 closed in
the other direction. Whoever needs to answer "which version was evaluated" is the
Tracker's own record, and that is where it lives.
Four decisions, each with a test:
- SEALING HAPPENS ONLY ON APPROVED. A RETURNED submission leaves the record in
editing on purpose, and freezing versions there would stop the author redoing
exactly what they were asked to redo.
- IT WALKS WHAT THE RECORD ACTUALLY CARRIES, not what the gate demanded:
approving the version of an artifact nobody delivered would assert it exists.
- A MISSING RECORD IS NOT AN ERROR. A gate can be decided on evidence and
approvals with no phase record; failing there would turn a valid approval
into an error over something the model permits.
- AN APPROVED VERSION IS A REQUIREMENT MET; EDITING IS ONLY A RECORD. If every
version counted as met, the ledger would claim the artifact was satisfied
every time somebody pressed save.
Verified: 3 new tests plus two existing suites moved to the new dependencies;
full suite 1274 passed / 10 failed, the same environment-dependent set that fails
on a clean worktree at HEAD. Zero new.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
feat(sdlc): approvals and evaluations point at a concrete version
…sible Closes CP-02 criterion 3, and with it CP-02. The scorecard's facts came ONLY from what was delivered (`artefacto.Items`), so an artifact the standard requires and nobody delivered did not appear at all. A phase with an empty record did not score badly -- it scored `null`, "nothing demandable yet", while the tenant's catalogue asked for three things. The dashboard said there was nothing to answer for exactly when everything was missing. `ScorecardProjection` now folds in the required artifacts from the SAME tenant-aware catalogue that governs the phase record, the read-only endpoint and the payload sent to the Core. Four surfaces, one catalogue. WHAT WAS NOT TOUCHED, AND IS RIGHT: a required-but-undelivered artifact still sits at `pendiente` while it is within its deadline, not red. Penalising before the due date would be punishing in advance, and that rule was already well reasoned. My first version of the test asserted the score moves immediately -- it was the assertion that was wrong, not the rule. The test now pins the moment it must hurt: overdue and undelivered. It scores against the catalogue WITH the overlay, not the bare standard. Punishing a tenant for not delivering what they themselves marked not-applicable would be the exact opposite of what CP-02 criterion 2 just allowed them to do. Verified: 4 new tests; full suite 1278 passed / 10 failed, the same environment-dependent set that fails on a clean worktree at HEAD. Zero new. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fix(scorecard): a required artifact nobody delivered stops being invisible
…onfiguration Advances CP-10: criteria 3 and 4 closed; 1 partial and 2 unstarted, both said why. `core-sdlc-parity` asserts the property no other robot covers and that four cards were built for: the SAME attached deposit contributes nothing to the verdict with no matrix, contributes `core-signal:architecture` when the tenant marks it blocking, and contributes nothing again when marked advisory. Between the three evaluations nothing about the deposit changed -- only the configuration. VERIFIED FOR REAL, NOT IN CI. The full local stack was brought up (`local-test.sh up`: kind + build + Helm) and the robot ran against it: 27 checks, 0 failures. The nine robots on the CI list ran against that same deployment: 211 checks, 0 failures. I had earlier written this row off as unverifiable. That was not a measurement -- docker, kind, kubectl and helm were all installed and a cluster already existed. The user asked whether I had tried; I had not. ONE TENANT, TWO CONFIGURATIONS -- stated rather than implied. The criterion says "a tenant ... and another"; this runs it as ONE tenant reconfigured between evaluations, because a deposit is scoped by the machine key's tenant and a second one would need another key plus its own seeded initiative. The code path is identical -- `CoreSignalRule` is stored per tenant AND phase -- but what it does NOT prove is isolation between two tenants' matrices, which `tenant-isolation` covers. WHY IT IS NOT ON THE CI LIST: it needs a CoreMachine key bound to the tenant the robot reads as, and `deploy-check.yml` configures none -- the same reason `core-evidence-ingest`, `core-integration` and `runtime-approvals` are excluded. Without the key it SOFT-SKIPS VISIBLY: the summary reports `0 ok`, not a silent PASS. A robot claiming PASS having exercised nothing is the vacuous green this repository keeps finding. A defect in the robot itself, corrected: its first version asserted "the gate is NOT blocked by the deposit" by checking the state was not RETURNED. The gate was RETURNED over an unresolved approval unrelated to the deposit, so the assertion measured something else. It now compares the unmet reasons with and without the matrix: with the signal advisory the gate is EXACTLY as it was with no matrix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
test(robosoft): the same Core signal blocks, or does not, by tenant configuration
…uate GAP-020
Two corrections to my own work in this campaign.
1. I said CP-02 was fully closed. It was not: criterion 4 -- "GAP-020 re-evaluated
in the light of the live catalogue" -- was still open. Now done, with evidence:
- the tenant-aware persistence/caching of schema references, which GAP-020
parked in GAP-004 as "blocked upstream", is RESOLVED and not as foreseen.
`CoreSyncPhaseArtifactProfileSource` syncs from the Core and caches IN
MEMORY -- deliberately, because `GetProfiles()` is synchronous and sits on a
governance decision -- carries `canonicalSchema` per artifact keeping
`core-schema`, `tool-output` and `none` apart, and the tenant overlay
preserves it. No database copy was needed: the Core is the authority and a
copy on disk would be a third version to keep in step.
- "use them to validate what is filled in" stays WITHDRAWN, same reason:
T-056, the tenant configures content validation and the engine does not
hardwire it. The live catalogue does not change that; if anything it
reinforces it, because the tenant can now mark applicability without anyone
interpreting content on their behalf.
GAP-020 is not reopened: what it left pending is either done or deliberately
out of scope.
2. CP-02, CP-04, CP-06 and CP-14 had every criterion checked and their rows still
said PENDING -- drift I introduced by updating criteria and prose without
flipping the status. Both surfaces now agree; `check-gap-registry.py` reports
203 cards / 203 rows coherent, 179 DONE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs(board): flip the rows whose criteria are all closed, and re-evaluate GAP-020
…it 14 The inventory guard caught it: `core-sdlc-parity` landed and the generated roster still said 13. The block is generated, so it was regenerated rather than hand-edited — editing the number by hand is what the guard exists to catch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs(robosoft): regenerate the robot roster — the parity robot makes it 14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotes everything accumulated on
developtomain: 75 commits.What this session closed
Four cards fully closed, and the thread joining them is the same defect in different places: surfaces that did not talk to each other.
schemaVersion,requester,repositoryRevisionand the phase package — assembled by the server, not the caller.SetItemmutated in place, so every edit erased the previous one. Now versions are immutable, restorable, and an approved one is sealed.Also advanced: CP-05 (the canonical result stops dying at the ACL), CP-07 (a deposit hangs off the SDLC record and runs through the tenant matrix), CP-16 (the initiative ledger, derived rather than stored), CP-10 (
core-sdlc-parity— verified against a live local deployment: the same signal blocks or not by configuration alone).Two of the defects found were in pieces built earlier in this same session.
A false green worth naming
The scorecard's facts came only from what was delivered, so an artifact the standard requires and nobody delivered was invisible. A phase with an empty record scored
null— "nothing demandable yet" — while the catalogue asked for three things.Verification
Every commit reached
developthrough its own PR with the full check set green, including theDeploy (kind + Helm + smoke)job that runs the RoboSoft fleet. Locally, against a real kind deployment: 211 checks across the nine CI robots, plus 27 for the new parity robot, zero failures.🤖 Generated with Claude Code