promote: dev history slice 2 of 4 - #31
Conversation
…-completion feat: add Spreadsheet Auditor vertical slice
📝 WalkthroughWalkthroughThe PR adds versioned immutable domain contracts, deterministic engine processors, tenant-scoped IAE, DSM, and spreadsheet-audit workflows, OpenAPI definitions, Prisma persistence, NestJS modules, and integration tests. ChangesPlatform foundations
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
services/api/test/features/iae/prisma-artifact-intake-repository.test.ts (1)
56-61: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winThe fixture
createaccepts duplicate ids.
createpushes unconditionally. Prisma rejects a duplicate primary key with P2002. The same weakness exists inservices/api/test/features/iae/prisma-artifact-export-repository.test.ts. See the consolidated comment for the shared fix.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/test/features/iae/prisma-artifact-intake-repository.test.ts` around lines 56 - 61, The fixture create method in the Prisma artifact intake repository test should enforce primary-key uniqueness before pushing a persisted row, matching Prisma’s P2002 behavior for duplicate ids. Apply the same shared fix to the corresponding create fixture in the artifact export repository test, while preserving successful creation for unique ids.
🟡 Minor comments (29)
services/engine/src/databreeze_engine/processors/dataset_quality.py-51-64 (1)
51-64: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winRequire
MISSING,NULL, andBLANKinstateCounts.
StateCountsdoes not enforce these keys at runtime. A malformedDatasetProfilecan make_required_countraiseKeyError. Enforce the keys in the model or return a controlled validation error.DatasetProfilecontains aggregate metadata and fingerprints, not raw source values.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/engine/src/databreeze_engine/processors/dataset_quality.py` around lines 51 - 64, Update _required_count to safely handle missing MISSING, NULL, or BLANK entries in summary.stateCounts, returning a controlled validation result instead of raising KeyError. Preserve the existing count calculation when all required keys are present, and keep profile_fingerprint unchanged.services/api/src/features/sa/api/spreadsheet-audit.dto.ts-107-111 (1)
107-111: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
blockedReasonsaccepts duplicate values.
@ArrayMaxSize(3)matches the count of allowed enum members. The current rules still accept['MACRO', 'MACRO', 'MACRO']. Add@ArrayUnique()to enforce the intended set semantics.🛡️ Proposed validation fix
`@IsArray`() + `@ArrayUnique`() `@ArrayMaxSize`(3) `@IsIn`(['MACRO', 'EXTERNAL_LINK', 'UNSUPPORTED_XML'], { each: true }) blockedReasons!: Array<'MACRO' | 'EXTERNAL_LINK' | 'UNSUPPORTED_XML'>;Add
ArrayUniqueto theclass-validatorimport list.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/sa/api/spreadsheet-audit.dto.ts` around lines 107 - 111, Update the blockedReasons validation in the DTO by importing ArrayUnique from class-validator and adding `@ArrayUnique`() alongside the existing array validators, so duplicate enum values are rejected while the current allowed values and maximum size remain unchanged.services/api/src/features/sa/adapter/in-memory-spreadsheet-audit-repository.adapter.ts-62-85 (1)
62-85: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winRollback can discard writes made outside the transaction.
withTransactionserializes only calls that enter throughwithTransaction. The publicsavemethod does not take the same tail lock. If a caller awaitssavedirectly while a transaction is open, and the transaction then fails,this.results = beforeremoves that write.Route the non-transactional methods through the same queue, or document that the in-memory adapter supports one writer at a time.
♻️ Proposed serialization of direct writes
+ private enqueue<TValue>(work: () => Promise<TValue>): Promise<TValue> { + const previous = this.transactionTail; + let release!: () => void; + this.transactionTail = new Promise<void>((resolve) => { + release = resolve; + }); + return previous.then(work).finally(release); + }Then call
this.enqueue(...)fromsave, and keep the transaction body using the unwrapped internal helpers.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/sa/adapter/in-memory-spreadsheet-audit-repository.adapter.ts` around lines 62 - 85, Update the in-memory spreadsheet audit repository’s serialization so direct calls to save cannot overlap an active withTransaction rollback. Route the public save method through the same transactionTail queue, while keeping the transaction callback on unwrapped internal save/find/list helpers to avoid nested queueing and deadlocks; preserve the existing rollback and transaction ordering behavior.services/api/src/features/sa/api/spreadsheet-audit.dto.ts-118-120 (1)
118-120: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winAlign
createdAtvalidation with the UTC timestamp contract.
@IsISO8601({ strict: true })still accepts date-only values and offsets. Add a pattern that requires the time component and uppercaseZ; the domain rejects these values before Prisma persistence.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/sa/api/spreadsheet-audit.dto.ts` around lines 118 - 120, Update the createdAt property decorators in the spreadsheet audit DTO to enforce the UTC timestamp contract: retain strict ISO-8601 validation and add a matching pattern requiring a time component and uppercase Z, rejecting date-only values and offset timestamps before persistence.services/api/openapi/v1.json-7376-7376 (1)
7376-7376: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winBound the remaining unbounded arrays.
Several array properties declare
maxItems, for examplelabelsat line 7284 andsheetsat line 8188. Other arrays in the same document declare no upper bound:versionIds(line 7376),fields(line 7525),steps(line 7569),rules(line 7595),inputArtifactVersionIds(line 7641),evidenceIds(line 7713), andfindings(line 7742). The FastifybodyLimitof 65,536 bytes limits the total request size, so this is not an unbounded-memory hazard. It remains an inconsistent validation contract, and it produces the CheckovCKV_OPENAPI_21finding. AddMaxArraySize-style decorators to the matching DTO properties so the generated document declares the same bound everywhere.Also applies to: 7525-7525, 7569-7569, 7595-7595, 7641-7641, 7713-7713, 7742-7742
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/openapi/v1.json` at line 7376, Add the established MaxArraySize-style validation to the DTO properties generating versionIds, fields, steps, rules, inputArtifactVersionIds, evidenceIds, and findings, so the OpenAPI document emits a consistent maxItems bound matching existing arrays such as labels and sheets.Source: Linters/SAST tools
services/api/test/features/iae/prisma-artifact-intake-repository.test.ts-174-177 (1)
174-177: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAssert the persisted revision after the transition.
The read-back checks only
state. The test is named for revisioned transitions, but an adapter that writesROUTEDand drops the revision increment still passes. Assertrevisionas well.💚 Proposed fix
- assert.equal( - (await repository.find(context(workspaceId, 'transition-read'), itemId))?.state, - 'ROUTED', - ); + const persisted = await repository.find(context(workspaceId, 'transition-read'), itemId); + assert.equal(persisted?.state, 'ROUTED'); + assert.equal(persisted?.revision, 2);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/test/features/iae/prisma-artifact-intake-repository.test.ts` around lines 174 - 177, Update the read-back assertion in the revisioned transition test to validate the persisted revision alongside the existing ROUTED state check. Use the expected incremented revision value for the transition, ensuring adapters that change state without persisting the revision increment fail.services/api/test/features/iae/inbox.controller.test.ts-106-106 (1)
106-106: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winAdd the case-insensitive flag to the content-leak assertion.
This assertion guards against content fields in the response. It uses
/path|source|byte|excerpt/uwithout theiflag, so it does not matchsourcePath,Excerpt, or other capitalized forms. The equivalent assertions inservices/api/test/features/iae/artifact-upload.controller.test.tsuse/iu. Align this one.🛡️ Proposed fix
- assert.doesNotMatch(accepted.body, /path|source|byte|excerpt/u); + assert.doesNotMatch(accepted.body, /path|source|byte|excerpt/iu);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/test/features/iae/inbox.controller.test.ts` at line 106, Update the content-leak assertion in the relevant inbox controller test to use the case-insensitive regular-expression flag, matching the equivalent artifact-upload assertion while preserving the existing prohibited terms.services/api/openapi/v1.json-149-153 (1)
149-153: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winDeclare
application/problem+jsonfor the 503 response.
ProblemDetailsFiltersends readiness failures asapplication/problem+json, but the generated OpenAPI document declaresapplication/json. Set this media type inHealthController.readinessStatus's@ApiServiceUnavailableResponsedecorator.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/openapi/v1.json` around lines 149 - 153, Update the HealthController.readinessStatus `@ApiServiceUnavailableResponse` decorator to declare application/problem+json for the 503 response instead of application/json, keeping the ProblemDetails schema reference unchanged.services/api/test/features/iae/artifact-upload.service.test.ts-99-100 (1)
99-100: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winReturn a session-lifecycle error for expired uploads.
issuePartTransferdelegates anEXPIREDsession to storage, which returnsUPLOAD_STORAGE_NOT_READYfor every non-OPENsession. Add a distinct terminal code such asUPLOAD_SESSION_EXPIREDso clients can distinguish expiration from storage unavailability.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/test/features/iae/artifact-upload.service.test.ts` around lines 99 - 100, Update issuePartTransfer to detect sessions in the EXPIRED state before delegating to storage and return the distinct terminal code UPLOAD_SESSION_EXPIRED; preserve UPLOAD_STORAGE_NOT_READY for other non-OPEN sessions and storage failures.packages/domain/src/artifact-export/v1.ts-66-73 (1)
66-73: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winValidate the string after normalization and trimming.
textvalidates the raw input, then returnsinput.normalize('NFC').trim(). A whitespace-only string passes thelength > 0check and returns''. The caller at Line 124 only rejectsundefined, so an emptyprocessorVersionsentry enters a frozen manifest. Normalize and trim first, then apply the bounds check.🐛 Proposed fix
function text(input: unknown): string | undefined { - return typeof input === 'string' && - input.length > 0 && - input.length <= 128 && - !/\p{Cc}/u.test(input) - ? input.normalize('NFC').trim() - : undefined; + if (typeof input !== 'string' || /\p{Cc}/u.test(input)) return undefined; + const value = input.normalize('NFC').trim(); + return value.length > 0 && value.length <= 128 ? value : undefined; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/domain/src/artifact-export/v1.ts` around lines 66 - 73, Update text to normalize and trim string inputs before validating them. Apply the non-empty and maximum-length checks, along with the control-character check, to the normalized trimmed value so whitespace-only inputs return undefined while valid text is returned unchanged.packages/domain/test/public-api-v1.test.mjs-74-80 (1)
74-80: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAssert the two missing schema-version constants.
This block asserts the schema version for six modules. The export list at Lines 29-30 also requires
./artifact-retention/v1and./artifact-export/v1. Their constants are not asserted here.Both constants exist:
ARTIFACT_RETENTION_SCHEMA_VERSION_V1atpackages/domain/src/artifact-retention/v1.tsLine 13, andARTIFACT_EXPORT_SCHEMA_VERSION_V1atpackages/domain/src/artifact-export/v1.tsLine 12. If either is dropped from the aggregate re-export inpackages/domain/src/v1.ts, this smoke test still passes and the break reaches consumers.💚 Proposed fix
assert.equal(typeof aggregate.parseTenantScopeV1, 'function'); assert.equal(aggregate.ARTIFACT_UPLOAD_SCHEMA_VERSION_V1, 1); assert.equal(aggregate.PROTECTED_DOCUMENT_SCHEMA_VERSION_V1, 1); + assert.equal(aggregate.ARTIFACT_RETENTION_SCHEMA_VERSION_V1, 1); + assert.equal(aggregate.ARTIFACT_EXPORT_SCHEMA_VERSION_V1, 1);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/domain/test/public-api-v1.test.mjs` around lines 74 - 80, Add assertions in the public API smoke-test block for ARTIFACT_RETENTION_SCHEMA_VERSION_V1 and ARTIFACT_EXPORT_SCHEMA_VERSION_V1, verifying each equals 1 alongside the existing schema-version constants.packages/domain/test/dataset-profile-v1.test.mjs-46-54 (1)
46-54: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winIsolate the count violation in the first negative case.
The override sets
completeness: 'COMPLETE'and clearssamplingSeed, butsamplingMethod: 'HASHED_ROW_RESERVOIR_V1'still comes frombase. Case 2 at Lines 55-58 shows that a sample-only field on aCOMPLETEprofile yieldsINVALID_SAMPLING. This input therefore violates two rules at once.The assertion of
INVALID_COUNTpasses only while the count check runs before the sampling check insidecreateDatasetProfileV1. Reordering the checks, which preserves behavior, breaks this test. The test also does not prove the count rule alone is enforced. ClearsamplingMethodso the input violates only the count rule.💚 Proposed fix
createDatasetProfileV1({ ...base, completeness: 'COMPLETE', + samplingMethod: undefined, samplingSeed: undefined, rowCountScanned: 1001, }), { accepted: false, code: 'INVALID_COUNT' },🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/domain/test/dataset-profile-v1.test.mjs` around lines 46 - 54, Update the first negative test case in createDatasetProfileV1 to explicitly clear samplingMethod alongside samplingSeed, ensuring the COMPLETE profile violates only the row-count rule and continues asserting INVALID_COUNT.packages/domain/test/spreadsheet-audit-v1.test.mjs-36-38 (1)
36-38: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winAssert the value-free guarantee on the finding, not on the manifest root.
Lines 37-38 call
Object.hasOwnagainstresult.value, the top-level audit result. Raw workbook content would never appear there. It would appear on a finding.
build_spreadsheet_audit_manifestinservices/engine/src/databreeze_engine/processors/spreadsheet_auditor_manifest.pyat Lines 53-101 carriesformulaFingerprinton each finding for exactly this reason: the raw formula must not travel. These two assertions pass regardless of whether that holds, so they give false confidence in the privacy control this test is named for.🛡️ Proposed fix
assert.equal(result.value.findings[0]?.address, 'C1'); - assert.equal(Object.hasOwn(result.value, 'formula'), false); - assert.equal(Object.hasOwn(result.value, 'sourceValue'), false); + const finding = result.value.findings[0]; + assert.equal(Object.hasOwn(finding, 'formula'), false); + assert.equal(Object.hasOwn(finding, 'sourceValue'), false); + assert.equal(finding.formulaFingerprint, 'b'.repeat(64)); + assert.equal(Object.hasOwn(result.value.sheets[0], 'cells'), false);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/domain/test/spreadsheet-audit-v1.test.mjs` around lines 36 - 38, Update the assertions in the spreadsheet audit test to check the first finding, result.value.findings[0], rather than the top-level result.value. Verify that the finding does not own formula or sourceValue, preserving the existing address assertion and directly testing the value-free guarantee where raw workbook content could appear.packages/domain/src/artifact-upload/v1.ts-242-253 (1)
242-253: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winReturn a code that matches the rejection reason.
Line 249 rejects when
nowis earlier thansession.expiresAt. The session has not expired at that point, so the transition is premature. The returned code is'EXPIRED', which reports the opposite condition.A sweeper cannot separate "the session is not due yet" from a genuine expiry state error. Use
'INVALID_TIMESTAMP', which is already inArtifactUploadErrorCodeV1.🐛 Proposed fix
if (session.state !== 'OPEN') return rejected('INVALID_STATE'); - if (Date.parse(timestampValue) < Date.parse(session.expiresAt)) return rejected('EXPIRED'); + if (Date.parse(timestampValue) < Date.parse(session.expiresAt)) + return rejected('INVALID_TIMESTAMP');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/domain/src/artifact-upload/v1.ts` around lines 242 - 253, Update expireArtifactUploadSessionV1 so the branch where now precedes session.expiresAt returns INVALID_TIMESTAMP instead of EXPIRED, preserving the existing validation order and expiry transition behavior.packages/domain/src/spreadsheet-audit/v1.ts-166-186 (1)
166-186: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winStructural failures in
sheetandfindingare reported as'INVALID_COUNT'.
sheetandfindingreturnundefinedfor every failure cause. Lines 169-170 and 179-180 map that singleundefinedto'INVALID_COUNT'.The result is that a malformed cell address, a bad severity, an unknown kind, or a non-hex
formulaFingerprintall report'INVALID_COUNT', which describes none of them. The error union at lines 47-59 declares'INVALID_COORDINATE','INVALID_SEVERITY', and'INVALID_KIND', and no code path returns any of the three. Those three members are unreachable today.The SA controller returns these codes to API clients, so a client cannot tell "too many sheets" apart from "bad cell address".
Return the specific code from the element parsers, or at minimum use a distinct code for a structurally invalid element.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/domain/src/spreadsheet-audit/v1.ts` around lines 166 - 186, Update the sheet and finding validation flow around the sheet and finding parsers so structural element failures preserve their specific rejection codes instead of being collapsed to INVALID_COUNT. Return INVALID_COORDINATE, INVALID_SEVERITY, or INVALID_KIND as appropriate for the corresponding malformed fields, while retaining INVALID_COUNT for invalid collection sizes and preserving existing duplicate and reference checks.packages/domain/test/artifact-upload-v1.test.mjs-55-61 (1)
55-61: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winGuard the completion result before you read
.value.state.
completeArtifactUploadSessionV1returns a discriminated union. The rejected branch carriescodeand novalue. Line 59 reads.value.statewithout checkingacceptedfirst.If completion ever returns a rejection, the expression evaluates
undefined.stateand the test aborts with a TypeError. The report then names a property access instead of the real cause, which is the rejection code.Lines 28-29, 37-38, and 53-54 already use the guard pattern. Apply it here too.
💚 Proposed fix to keep the failure diagnostic useful
- assert.equal( - completeArtifactUploadSessionV1(second.value, { - assembledSha256: base.expectedSha256, - expectedRevision: 3, - }).value.state, - 'COMPLETED', - ); + const completed = completeArtifactUploadSessionV1(second.value, { + assembledSha256: base.expectedSha256, + expectedRevision: 3, + }); + assert.equal(completed.accepted, true); + if (!completed.accepted) return; + assert.equal(completed.value.state, 'COMPLETED');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/domain/test/artifact-upload-v1.test.mjs` around lines 55 - 61, Guard the result of completeArtifactUploadSessionV1 before accessing value.state, matching the accepted/result assertion pattern already used near lines 28-29, 37-38, and 53-54. Assert or validate accepted first so rejected results expose their code rather than causing a TypeError, then read state only from the accepted value.packages/domain/src/protected-document/v1.ts-192-203 (1)
192-203: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winLine 199 returns
'EXPIRED'for a request that has not expired.The condition
Date.parse(timestampValue) < Date.parse(request.expiresAt)is true whennowis beforeexpiresAt. In that branch the request is still live and must not be expired. The function correctly refuses, but it reports the code'EXPIRED', which states the opposite of the real condition.A caller that branches on
codewill read'EXPIRED'and conclude the request already expired, while the request is in fact still valid. The error union at lines 41-52 already contains'INVALID_STATE', which describes "the request is not yet expirable".The control flow needs no change. Only the reported code is wrong.
🐛 Proposed fix for the inverted error code
if (request.state !== 'REQUESTED') return rejected('INVALID_STATE'); - if (Date.parse(timestampValue) < Date.parse(request.expiresAt)) return rejected('EXPIRED'); + if (Date.parse(timestampValue) < Date.parse(request.expiresAt)) + return rejected('INVALID_STATE');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/domain/src/protected-document/v1.ts` around lines 192 - 203, In expireProtectedDocumentUnlockRequestV1, change only the rejection code for the branch where the current timestamp is before request.expiresAt: return INVALID_STATE instead of EXPIRED. Preserve the existing condition and all other control flow unchanged.packages/domain/src/dataset-profile/v1.ts-144-153 (1)
144-153: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winConsider enforcing
rowCountScanned <= resourceLimits.maxRows.The factory validates
rowCountScannedandmaxRowsindependently. It never compares them. A profile that reportsrowCountScanned: 50_000_000withmaxRows: 1_000is accepted today. Line 10 states the profile is a "bounded, reproducible profiling disclosure", so the accepted value contradicts its own declared limit.The file already enforces the comparable relation
rowCountScanned <= rowCountAvailableat lines 145-146. If the bound is intended to be authoritative, add the matching check.🐛 Proposed fix to enforce the declared row bound
if (!maxRows || !maxBytes || !maxDurationMs) return rejected('INVALID_LIMITS'); + if (rowCountScanned > maxRows) return rejected('INVALID_LIMITS'); if (!profileFingerprint) return rejected('INVALID_HASH');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/domain/src/dataset-profile/v1.ts` around lines 144 - 153, Update the validation flow around rowCountScanned, rowCountAvailable, and maxRows to reject profiles when rowCountScanned exceeds the declared maxRows limit. Preserve the existing INVALID_COUNT result for count-bound violations and ensure the comparison occurs after maxRows is parsed and validated.packages/domain/src/spreadsheet-audit/v1.ts-100-118 (1)
100-118: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAlign the
maxRowbound with the supported XLSX range.XLSX supports 1,048,576 rows. A workbook with a cell at row 1,048,576 can produce
maxRow=1_048_576, but the engine manifest andsheetreject it; the domain result then returns'INVALID_COUNT'. Raise the bound to 1,048,576, or return a distinct resource-limit result when the 1,000,000 cap is intentional.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/domain/src/spreadsheet-audit/v1.ts` around lines 100 - 118, The sheet validator’s maxRow limit is below the supported XLSX range. Update the upper-bound check in sheet to accept values through 1,048,576, preserving the existing maxColumn and formulaCount limits and validation behavior.services/api/src/features/iae/api/inbox.controller.ts-89-92 (1)
89-92: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winRemove the unreachable branch in the
mutationContextexpression.Line 89 returns when
expectedRevisionisundefined. At Line 92 the same condition is tested again, so thecontextbranch is unreachable andexpectedRevisionis always a number at that point.♻️ Proposed simplification
if (expectedRevision === undefined) return Object.freeze({ accepted: false, code: 'INVALID_METADATA' as const }); - const mutationContext = - expectedRevision === undefined ? context : Object.freeze({ ...context, expectedRevision }); + const mutationContext = Object.freeze({ ...context, expectedRevision });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/iae/api/inbox.controller.ts` around lines 89 - 92, Remove the redundant expectedRevision === undefined conditional from the mutationContext assignment in the surrounding controller method; after the preceding early return, always create the frozen context by spreading context and setting expectedRevision.services/api/src/features/iae/adapter/prisma-artifact-export-repository.adapter.ts-99-115 (1)
99-115: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winAlign
savewith tenant visibility and concurrent-create handling.
savereads the globalidwithoutvisible. A sibling-tenant manifest therefore producesIAE_IMMUTABLE_EXPORT_MANIFEST, whilefindreturnsundefined. Define whether global IDs intentionally expose this result, or apply the visibility rule before comparing.- Public
savebypasses$transaction. Concurrent calls can both read no row, then the secondcreatefails with a Prisma unique-constraint error. Use an atomic idempotency path and returnIAE_IMMUTABLE_EXPORT_MANIFESTfor the losing call.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/iae/adapter/prisma-artifact-export-repository.adapter.ts` around lines 99 - 115, The save method’s lookup and create flow must match tenant visibility and handle concurrent idempotent writes. Update the findUnique query to apply the same visible tenant-scope rule used by find, then wrap the check-and-create path in the repository’s transaction mechanism and translate a losing concurrent unique-constraint create into IAE_IMMUTABLE_EXPORT_MANIFEST, while preserving the existing scope validation and identical-manifest no-op behavior.services/api/src/features/iae/adapter/prisma-artifact-lineage-repository.adapter.ts-119-127 (1)
119-127: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winSelect a visible lineage row before applying
rowToDomain.
derivedArtifactVersionIdhas only a non-unique index. Duplicate rows across scopes are possible. The currentfindFirstcan select an invisible row and returnundefinedwhile a visible row exists. Match the in-memory adapter by filtering matching rows withvisible; do not use an exact-scope predicate that excludes visible parent or child scopes.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/iae/adapter/prisma-artifact-lineage-repository.adapter.ts` around lines 119 - 127, Update findByDerived to retrieve candidate lineage rows for the derivedArtifactVersionId, select the first row satisfying visible(context.tenantScope, row), and only then pass it to rowToDomain. Preserve visibility matching across parent and child scopes, and return undefined when no visible row exists.services/api/src/features/iae/application/artifact-retention.service.ts-15-18 (1)
15-18: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDeclared service error unions omit codes the services return. Both services declare a narrow error-code union and then return additional codes that only compile because the underlying domain result type permits them. Callers that switch on the declared union cannot handle every outcome.
services/api/src/features/iae/application/artifact-retention.service.ts#L15-L18: addINVALID_IDENTIFIER,INVALID_TIMESTAMP,INVALID_STATE, andINVALID_REVISIONtoArtifactRetentionServiceErrorV1.services/api/src/features/iae/application/content-placement.service.ts#L11-L14: addINVALID_IDENTIFIERtoContentPlacementServiceErrorV1, which line 32 returns.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/iae/application/artifact-retention.service.ts` around lines 15 - 18, Add INVALID_IDENTIFIER, INVALID_TIMESTAMP, INVALID_STATE, and INVALID_REVISION to ArtifactRetentionServiceErrorV1 in services/api/src/features/iae/application/artifact-retention.service.ts (lines 15-18). Also add INVALID_IDENTIFIER to ContentPlacementServiceErrorV1 in services/api/src/features/iae/application/content-placement.service.ts (lines 11-14), so each declared result union includes every code returned by its service.services/api/src/features/iae/api/artifact-admission.controller.ts-35-44 (1)
35-44: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winMap unavailable request context explicitly.
SessionRequestTenantContextAdapteralready maps missing or invalid bearer credentials to HTTP 401 throughProblemDetailsFilter; it does not needUnauthorizedException. IfUnavailableRequestTenantContextAdaptercan be active in a deployed app, map its plain error to an explicit authentication or configuration response instead of HTTP 500.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/iae/api/artifact-admission.controller.ts` around lines 35 - 44, Update the request-context resolution in ArtifactAdmissionController.admit to handle errors from UnavailableRequestTenantContextAdapter explicitly instead of allowing them to become HTTP 500. Map that plain unavailable-context error to the established authentication or configuration response mechanism, while preserving SessionRequestTenantContextAdapter’s existing 401 behavior and the remaining admission flow.services/api/src/features/iae/api/artifact-admission.dto.ts-18-21 (1)
18-21: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse
@IsInt()foractualByteSize.
actualByteSizeaccepts fractional numbers because it uses@IsNumber().maxByteSizeon Line 32 uses@IsInt(). A byte count must be an integer. Align the two fields and addtype: 'integer'to the OpenAPI metadata.🐛 Proposed fix
- `@ApiProperty`({ minimum: 0 }) - `@IsNumber`() + `@ApiProperty`({ type: 'integer', minimum: 0 }) + `@IsInt`() `@Min`(0) actualByteSize!: number;Remove the now-unused
IsNumberimport on Line 6.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/iae/api/artifact-admission.dto.ts` around lines 18 - 21, Update the actualByteSize property in the artifact admission DTO to use `@IsInt`() instead of `@IsNumber`(), and add type: 'integer' to its `@ApiProperty` metadata to match maxByteSize. Remove the unused IsNumber import.services/api/src/features/iae/api/artifact-export.controller.ts-42-52 (1)
42-52: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winMap failed result envelopes to HTTP errors. Both handlers return
{ accepted: false, code: ... }directly. The global filter handles exceptions only, so failures return HTTP 201 fromcreateand HTTP 200 fromget. Throw mappedHttpExceptioninstances or convert failed envelopes before responding.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/iae/api/artifact-export.controller.ts` around lines 42 - 52, Update the create and get methods in the artifact export controller to inspect the service result and convert any { accepted: false, code: ... } envelope into the appropriate mapped HttpException before returning. Preserve successful envelopes and existing status codes, ensuring failures are handled through exception-based HTTP responses rather than returned directly.services/api/src/features/iae/api/artifact-retention.dto.ts-5-23 (1)
5-23: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
@IsISO8601()accepts date-only values although Swagger declaresdate-time. Both DTOs document adate-timeformat but validate with the default, non-strict@IsISO8601(), which accepts2026-08-03. The shared root cause is the missing strict option on every timestamp field.
services/api/src/features/iae/api/artifact-retention.dto.ts#L5-L23: pass{ strict: true }to@IsISO8601()onevaluatedAt,workspaceRetentionUntil,resourceRetentionUntil,auditRetentionUntil, andrecoveryWindowUntil.services/api/src/features/iae/api/inbox-item.dto.ts#L63-L69: pass{ strict: true }to@IsISO8601()ondueAt, and keep@IsOptional()so an explicitnullstill clears the value.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/iae/api/artifact-retention.dto.ts` around lines 5 - 23, Update IsISO8601 validation to use strict mode for evaluatedAt, workspaceRetentionUntil, resourceRetentionUntil, auditRetentionUntil, and recoveryWindowUntil in services/api/src/features/iae/api/artifact-retention.dto.ts:5-23, and for dueAt in services/api/src/features/iae/api/inbox-item.dto.ts:63-69. Preserve IsOptional on dueAt so explicit null remains valid.services/api/src/features/dsm/adapter/prisma-dataset-profile-repository.adapter.ts-147-159 (1)
147-159: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winRead-then-create in
savedepends on the transaction isolation level. All three adapters callfindUniqueand thencreate. Two concurrent requests for the same identifier can both observe no row and both attempt the insert. The primary-key constraint prevents duplicate rows, so the second request fails with a raw Prisma unique-constraint error instead of the intendedDSM_IMMUTABLE_*error. Confirm the isolation level used by$transaction, or map the unique-constraint error to the immutability error.
services/api/src/features/dsm/adapter/prisma-dataset-profile-repository.adapter.ts#L147-L159: catch the unique-constraint failure fromdatasetProfileRecord.createand re-run the comparison, or translate it toDSM_IMMUTABLE_DATASET_PROFILE.services/api/src/features/dsm/adapter/prisma-dataset-quality-repository.adapter.ts#L121-L133: apply the same handling forDSM_IMMUTABLE_QUALITY_RESULT.services/api/src/features/dsm/adapter/prisma-dataset-version-repository.adapter.ts#L125-L137: apply the same handling forDSM_IMMUTABLE_DATASET_VERSION.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/dsm/adapter/prisma-dataset-profile-repository.adapter.ts` around lines 147 - 159, Handle concurrent insert conflicts in save: in services/api/src/features/dsm/adapter/prisma-dataset-profile-repository.adapter.ts lines 147-159, catch the unique-constraint failure from datasetProfileRecord.create and re-check or translate it to DSM_IMMUTABLE_DATASET_PROFILE; apply equivalent handling in services/api/src/features/dsm/adapter/prisma-dataset-quality-repository.adapter.ts lines 121-133 for DSM_IMMUTABLE_QUALITY_RESULT and services/api/src/features/dsm/adapter/prisma-dataset-version-repository.adapter.ts lines 125-137 for DSM_IMMUTABLE_DATASET_VERSION.services/api/src/features/dsm/api/dataset-quality.dto.ts-53-60 (1)
53-60: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
valueaccepts any type despite the documentedoneOfschema.
@Allow()disables validation entirely forvalue. The@ApiPropertydocumentsoneOf: [{type:'string'},{type:'number'},{type:'boolean'}], but nothing enforces that at runtime. A caller can submit an object or array forvalueand it passes DTO validation. Since this DTO underpins a "value-free"/"safe value" audit contract, unvalidated arbitrary payloads reaching the domain layer weakens that guarantee unlesscreateDatasetQualityResultV1independently re-checks the type.🛡️ Proposed fix using a custom validator
-import { ArrayMaxSize, Allow, IsArray, IsIn, IsInt, IsString, IsUUID, IsOptional, Matches, Max, MaxLength, Min, MinLength, ValidateNested } from 'class-validator'; +import { ArrayMaxSize, IsArray, IsIn, IsInt, IsString, IsUUID, IsOptional, Matches, Max, MaxLength, Min, MinLength, ValidateBy, ValidateNested } from 'class-validator'; ... `@ApiProperty`({ required: false, oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], }) `@IsOptional`() - `@Allow`() + `@ValidateBy`({ + name: 'isSafeValuePrimitive', + validator: { + validate: (v) => ['string', 'number', 'boolean'].includes(typeof v), + defaultMessage: () => 'value must be a string, number, or boolean', + }, + }) value?: string | number | boolean;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@services/api/src/features/dsm/api/dataset-quality.dto.ts` around lines 53 - 60, Replace the unrestricted `@Allow`() validation on DatasetQuality DTO property value with runtime validation that accepts only string, number, or boolean values while preserving optionality. Keep the ApiProperty oneOf schema aligned with this constraint and ensure objects and arrays are rejected before reaching createDatasetQualityResultV1.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: de11d1ba-a94d-4185-866f-f0c78038abbe
📒 Files selected for processing (176)
docs/release-evidence/sa-spreadsheet-auditor-slice.mdpackages/domain/package.jsonpackages/domain/src/artifact-export/v1.tspackages/domain/src/artifact-intake/v1.tspackages/domain/src/artifact-retention/v1.tspackages/domain/src/artifact-upload/v1.tspackages/domain/src/artifact/v1.tspackages/domain/src/dataset-export/v1.tspackages/domain/src/dataset-profile/v1.tspackages/domain/src/dataset-quality/v1.tspackages/domain/src/protected-document/v1.tspackages/domain/src/spreadsheet-audit/v1.tspackages/domain/src/v1.tspackages/domain/test/artifact-export-v1.test.mjspackages/domain/test/artifact-intake-v1.test.mjspackages/domain/test/artifact-retention-v1.test.mjspackages/domain/test/artifact-upload-v1.test.mjspackages/domain/test/built-public-api-smoke.mjspackages/domain/test/dataset-export-v1.test.mjspackages/domain/test/dataset-profile-v1.test.mjspackages/domain/test/dataset-quality-v1.test.mjspackages/domain/test/protected-document-v1.test.mjspackages/domain/test/public-api-v1.test.mjspackages/domain/test/spreadsheet-audit-v1.test.mjsservices/api/openapi/v1.jsonservices/api/prisma/migrations/20260802230000_iae_retention_exports/migration.sqlservices/api/prisma/migrations/20260802240000_iae_upload_sessions/migration.sqlservices/api/prisma/migrations/20260802250000_dsm_quality_results/migration.sqlservices/api/prisma/migrations/20260802260000_iae_inbox_metadata/migration.sqlservices/api/prisma/migrations/20260802270000_dsm_profiles/migration.sqlservices/api/prisma/migrations/20260802280000_iae_protected_document_unlocks/migration.sqlservices/api/prisma/migrations/20260802290000_dsm_export_manifests/migration.sqlservices/api/prisma/migrations/20260802300000_sa_spreadsheet_audits/migration.sqlservices/api/prisma/schema/dsm.prismaservices/api/prisma/schema/iae.prismaservices/api/prisma/schema/platform.prismaservices/api/prisma/schema/sa.prismaservices/api/src/app.module.tsservices/api/src/bootstrap.tsservices/api/src/features/dsm/adapter/in-memory-dataset-export-repository.adapter.tsservices/api/src/features/dsm/adapter/in-memory-dataset-profile-repository.adapter.tsservices/api/src/features/dsm/adapter/in-memory-dataset-quality-repository.adapter.tsservices/api/src/features/dsm/adapter/in-memory-dataset-version-repository.adapter.tsservices/api/src/features/dsm/adapter/prisma-dataset-export-repository.adapter.tsservices/api/src/features/dsm/adapter/prisma-dataset-profile-repository.adapter.tsservices/api/src/features/dsm/adapter/prisma-dataset-quality-repository.adapter.tsservices/api/src/features/dsm/adapter/prisma-dataset-version-repository.adapter.tsservices/api/src/features/dsm/api/dataset-export.controller.tsservices/api/src/features/dsm/api/dataset-export.dto.tsservices/api/src/features/dsm/api/dataset-profile.controller.tsservices/api/src/features/dsm/api/dataset-profile.dto.tsservices/api/src/features/dsm/api/dataset-quality.controller.tsservices/api/src/features/dsm/api/dataset-quality.dto.tsservices/api/src/features/dsm/api/dataset-version.controller.tsservices/api/src/features/dsm/api/dataset-version.dto.tsservices/api/src/features/dsm/api/governed-dataset.controller.tsservices/api/src/features/dsm/api/governed-dataset.dto.tsservices/api/src/features/dsm/api/mapping.controller.tsservices/api/src/features/dsm/api/mapping.dto.tsservices/api/src/features/dsm/api/reference-entity.controller.tsservices/api/src/features/dsm/api/rule-set.controller.tsservices/api/src/features/dsm/application/dataset-export-repository.port.tsservices/api/src/features/dsm/application/dataset-export.service.tsservices/api/src/features/dsm/application/dataset-profile-repository.port.tsservices/api/src/features/dsm/application/dataset-profile.service.tsservices/api/src/features/dsm/application/dataset-quality-repository.port.tsservices/api/src/features/dsm/application/dataset-quality.service.tsservices/api/src/features/dsm/application/dataset-version-repository.port.tsservices/api/src/features/dsm/application/dataset-version.service.tsservices/api/src/features/dsm/application/governed-dataset.service.tsservices/api/src/features/dsm/application/reference-entity.service.tsservices/api/src/features/dsm/dsm.module.tsservices/api/src/features/iae/adapter/in-memory-artifact-export-repository.adapter.tsservices/api/src/features/iae/adapter/in-memory-artifact-intake-repository.adapter.tsservices/api/src/features/iae/adapter/in-memory-artifact-repository.adapter.tsservices/api/src/features/iae/adapter/in-memory-artifact-retention-repository.adapter.tsservices/api/src/features/iae/adapter/in-memory-artifact-upload-repository.adapter.tsservices/api/src/features/iae/adapter/in-memory-artifact-upload-storage.adapter.tsservices/api/src/features/iae/adapter/in-memory-protected-document-secret-input.adapter.tsservices/api/src/features/iae/adapter/in-memory-protected-document-unlock-repository.adapter.tsservices/api/src/features/iae/adapter/prisma-artifact-export-repository.adapter.tsservices/api/src/features/iae/adapter/prisma-artifact-intake-repository.adapter.tsservices/api/src/features/iae/adapter/prisma-artifact-lineage-repository.adapter.tsservices/api/src/features/iae/adapter/prisma-artifact-repository.adapter.tsservices/api/src/features/iae/adapter/prisma-artifact-retention-repository.adapter.tsservices/api/src/features/iae/adapter/prisma-artifact-upload-repository.adapter.tsservices/api/src/features/iae/adapter/prisma-evidence-grant-repository.adapter.tsservices/api/src/features/iae/adapter/prisma-protected-document-unlock-repository.adapter.tsservices/api/src/features/iae/api/artifact-admission.controller.tsservices/api/src/features/iae/api/artifact-admission.dto.tsservices/api/src/features/iae/api/artifact-export.controller.tsservices/api/src/features/iae/api/artifact-export.dto.tsservices/api/src/features/iae/api/artifact-lineage.controller.tsservices/api/src/features/iae/api/artifact-read.controller.tsservices/api/src/features/iae/api/artifact-retention.controller.tsservices/api/src/features/iae/api/artifact-retention.dto.tsservices/api/src/features/iae/api/artifact-upload.controller.tsservices/api/src/features/iae/api/artifact-upload.dto.tsservices/api/src/features/iae/api/content-placement.controller.tsservices/api/src/features/iae/api/content-placement.dto.tsservices/api/src/features/iae/api/inbox-item.dto.tsservices/api/src/features/iae/api/inbox.controller.tsservices/api/src/features/iae/api/protected-document-unlock.controller.tsservices/api/src/features/iae/api/protected-document-unlock.dto.tsservices/api/src/features/iae/application/artifact-admission.service.tsservices/api/src/features/iae/application/artifact-export-repository.port.tsservices/api/src/features/iae/application/artifact-export.service.tsservices/api/src/features/iae/application/artifact-intake.service.tsservices/api/src/features/iae/application/artifact-repository.port.tsservices/api/src/features/iae/application/artifact-retention-repository.port.tsservices/api/src/features/iae/application/artifact-retention.service.tsservices/api/src/features/iae/application/artifact-upload-repository.port.tsservices/api/src/features/iae/application/artifact-upload-storage.port.tsservices/api/src/features/iae/application/artifact-upload.service.tsservices/api/src/features/iae/application/content-placement.service.tsservices/api/src/features/iae/application/protected-document-secret-input.port.tsservices/api/src/features/iae/application/protected-document-unlock-repository.port.tsservices/api/src/features/iae/application/protected-document-unlock.service.tsservices/api/src/features/iae/iae.module.tsservices/api/src/features/sa/adapter/in-memory-spreadsheet-audit-repository.adapter.tsservices/api/src/features/sa/adapter/prisma-spreadsheet-audit-repository.adapter.tsservices/api/src/features/sa/api/spreadsheet-audit.controller.tsservices/api/src/features/sa/api/spreadsheet-audit.dto.tsservices/api/src/features/sa/application/spreadsheet-audit-repository.port.tsservices/api/src/features/sa/application/spreadsheet-audit.service.tsservices/api/src/features/sa/sa.module.tsservices/api/test/features/dsm/dataset-export.controller.test.tsservices/api/test/features/dsm/dataset-export.service.test.tsservices/api/test/features/dsm/dataset-profile.controller.test.tsservices/api/test/features/dsm/dataset-profile.pagination.test.tsservices/api/test/features/dsm/dataset-profile.service.test.tsservices/api/test/features/dsm/dataset-quality.controller.test.tsservices/api/test/features/dsm/dataset-quality.service.test.tsservices/api/test/features/dsm/dataset-version.controller.test.tsservices/api/test/features/dsm/governed-dataset.controller.test.tsservices/api/test/features/dsm/mapping.controller.test.tsservices/api/test/features/dsm/prisma-dataset-export-repository.test.tsservices/api/test/features/dsm/prisma-dataset-profile-repository.test.tsservices/api/test/features/dsm/prisma-dataset-quality-repository.test.tsservices/api/test/features/dsm/prisma-dataset-version-repository.test.tsservices/api/test/features/dsm/reference-entity.controller.test.tsservices/api/test/features/dsm/rule-set.controller.test.tsservices/api/test/features/foundation-module-composition.test.tsservices/api/test/features/iae/artifact-admission.controller.test.tsservices/api/test/features/iae/artifact-admission.service.test.tsservices/api/test/features/iae/artifact-export.service.test.tsservices/api/test/features/iae/artifact-intake-metadata.service.test.tsservices/api/test/features/iae/artifact-lineage.controller.test.tsservices/api/test/features/iae/artifact-read.controller.test.tsservices/api/test/features/iae/artifact-retention.service.test.tsservices/api/test/features/iae/artifact-upload-storage.adapter.test.tsservices/api/test/features/iae/artifact-upload.controller.test.tsservices/api/test/features/iae/artifact-upload.service.test.tsservices/api/test/features/iae/content-placement.service.test.tsservices/api/test/features/iae/inbox.controller.test.tsservices/api/test/features/iae/prisma-artifact-export-repository.test.tsservices/api/test/features/iae/prisma-artifact-intake-repository.test.tsservices/api/test/features/iae/prisma-artifact-lineage-repository.test.tsservices/api/test/features/iae/prisma-artifact-repository.test.tsservices/api/test/features/iae/prisma-artifact-retention-repository.test.tsservices/api/test/features/iae/prisma-artifact-upload-repository.test.tsservices/api/test/features/iae/prisma-evidence-grant-repository.test.tsservices/api/test/features/iae/prisma-protected-document-unlock-repository.test.tsservices/api/test/features/iae/protected-document-unlock.controller.test.tsservices/api/test/features/iae/protected-document-unlock.service.test.tsservices/api/test/features/sa/prisma-spreadsheet-audit-repository.test.tsservices/api/test/features/sa/spreadsheet-audit.controller.test.tsservices/api/test/features/sa/spreadsheet-audit.service.test.tsservices/api/test/openapi.test.tsservices/api/test/prisma-foundation.test.mjsservices/engine/src/databreeze_engine/processors/__init__.pyservices/engine/src/databreeze_engine/processors/dataset_quality.pyservices/engine/src/databreeze_engine/processors/spreadsheet_auditor.pyservices/engine/src/databreeze_engine/processors/spreadsheet_auditor_manifest.pyservices/engine/tests/test_dataset_quality.pyservices/engine/tests/test_spreadsheet_auditor.py
Merge the 30-commit CodeRabbit PR #31 repair batch into dev. CodeRabbit was skipped on this base branch by policy.
|
CodeRabbit review 4842552845 has been fully reproduced: 29 findings accepted and fixed through merged dev PR #32; 3 findings rejected with evidence. Full disposition: https://github.com/DatabreezeService/databreeze-platform/blob/dev/docs/operations/coderabbit-pr-31-disposition.md. No second CodeRabbit review was requested or run on this promotion PR. |
Promotes the second ordered DataBreeze dev-history slice to main. This boundary contains 75 commits after promotion slice 1, remains within the approved exceptional ceiling of 79, and preserves merge commits. CodeRabbit policy: allow exactly one automatic full review on this PR, reproduce each claim, fix valid findings on dev in focused commits, document rejected findings, and do not request a rerun. This is an intermediate promotion boundary; main is not releaseable until all four slices and coordinated release gates pass.
Summary by CodeRabbit