feat: 006 dashboard/AI evidence classification 및 deletion proof 구현 - #283
Conversation
📝 WalkthroughWalkthroughThis change adds T042 SAST evidence governance. It defines purpose-bound dashboard and AI-advisory access, second-pass redaction, seven-day deletion scheduling, leased deletion processing, immutable deletion proofs, dashboard routing, Prisma persistence, validation contracts, and T043 handoff updates. ChangesSAST evidence governance
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 204105e1bd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (9)
apps/api/test/scan-plane/sast-evidence-access-persistence.e2e-spec.ts (2)
61-79: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThese assertions couple the test to source formatting.
Lines 62-67 assert an exact line break and indentation inside the shared source file. A formatter change, such as a different print width, breaks these assertions without any functional regression. The same applies to lines 73-78, which assert exact expression text in the service and store.
Prefer importing the constants from
@aegisai/sharedand asserting their numeric values. Keep source-text matching only for the SQL constraints, which have no runtime accessor here.♻️ Proposed direction
- expect(shared).toContain( - 'SAST_EVIDENCE_MAX_RETENTION_SECONDS =\n 7 * 24 * 60 * 60' - ); - expect(shared).toContain( - 'SAST_AI_PAYLOAD_MAX_RETENTION_SECONDS =\n 24 * 60 * 60' - ); + expect(SAST_EVIDENCE_MAX_RETENTION_SECONDS).toBe(7 * 24 * 60 * 60); + expect(SAST_AI_PAYLOAD_MAX_RETENTION_SECONDS).toBe(24 * 60 * 60);🤖 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 `@apps/api/test/scan-plane/sast-evidence-access-persistence.e2e-spec.ts` around lines 61 - 79, Refactor the test case `pins seven-day evidence and 24-hour AI payload retention in code and SQL` to import the retention constants from `@aegisai/shared` and assert their numeric seven-day and 24-hour values instead of matching formatted source text. Remove the exact service and store expression assertions, while retaining source-text checks only for the SQL interval and constraint expressions.
132-138: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winFour specs parse the
ScanPlaneModuleexportsarray with duplicated and divergent regexes. The shared root cause is the absence of one helper that extracts the exports block. Two different patterns are now in use, so a formatting change toscan-plane.module.tscan break one group of specs while the other group still matches, which produces inconsistent module-boundary enforcement.Add one helper, for example
readScanPlaneExports(), in a shared test utility and call it from every spec.
apps/api/test/scan-plane/sast-evidence-access-persistence.e2e-spec.ts#L132-L138: replace the/exports:\s*\[([\s\S]*?)\]\s*\}\)\s*export class/match with the shared helper.apps/api/test/scan-plane/sast-accepted-evidence-persistence.e2e-spec.ts#L254-L259: replace the local regex and the?? ''fallback with the shared helper.apps/api/test/scan-plane/sast-scan-coverage-persistence.e2e-spec.ts#L117-L121: replace the local regex and the?? ''fallback with the shared helper.apps/api/test/scan-plane/sast-scan-freshness-persistence.e2e-spec.ts#L173-L177: replace the local regex and the?? ''fallback with the shared helper.🤖 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 `@apps/api/test/scan-plane/sast-evidence-access-persistence.e2e-spec.ts` around lines 132 - 138, Add a shared test utility helper such as readScanPlaneExports() to extract the ScanPlaneModule exports block, then replace each local regex extraction and fallback with that helper. Update apps/api/test/scan-plane/sast-evidence-access-persistence.e2e-spec.ts#L132-L138, apps/api/test/scan-plane/sast-accepted-evidence-persistence.e2e-spec.ts#L254-L259, apps/api/test/scan-plane/sast-scan-coverage-persistence.e2e-spec.ts#L117-L121, and apps/api/test/scan-plane/sast-scan-freshness-persistence.e2e-spec.ts#L173-L177; each site requires the shared helper call and no duplicated regex.apps/api/test/scan-plane/sast-evidence-access.e2e-spec.ts (1)
506-512: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAssert a rejection instead of a synchronous throw.
finalizeDeletiondeclaresPromise<...>as its return type. This assertion requires a synchronous throw. It passes only becauseMemoryAccessStore.finalizeDeletionthrows before returning a promise at line 628. The Prisma-backed store performsawaitwork and will reject instead. The double therefore diverges from the production contract, and this fencing check does not exercise the real failure mode.Make the double reject, and assert with
rejects, which also matches lines 490-495.♻️ Proposed change
- expect(() => + await expect( replayStore.finalizeDeletion({ candidate, receipt: changedReceipt, proof: changedProof }) - ).toThrow(SastEvidenceAccessPersistenceError); + ).rejects.toBeInstanceOf(SastEvidenceAccessPersistenceError);Change the two
throwstatements inMemoryAccessStore.finalizeDeletionand the one inreleaseDeletiontoPromise.reject(...)so the double matches the async contract.🤖 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 `@apps/api/test/scan-plane/sast-evidence-access.e2e-spec.ts` around lines 506 - 512, Update MemoryAccessStore.finalizeDeletion and releaseDeletion to return Promise.reject(...) for their failure paths instead of throwing synchronously, matching the async contract. Change the finalizeDeletion assertion in the test to await expect(...).rejects.toThrow(SastEvidenceAccessPersistenceError), consistent with the existing rejection assertions around lines 490-495.packages/shared/src/types/sast-evidence-access.ts (1)
838-843: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valuePrecompile the contract identifier patterns.
isContractIdbuilds a newRegExpon every call. The validators call it many times per decision, andisSastEvidenceAccessScopeValidalone calls it five times. Cache the compiled patterns in aMapkeyed by prefix, or export one frozen record of prefix-to-pattern entries.The static analysis hint about regex from variable input is not exploitable here, because every caller passes a module-local literal prefix. The change is a performance and clarity improvement only.
🤖 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/shared/src/types/sast-evidence-access.ts` around lines 838 - 843, Update isContractId to reuse precompiled regular expressions instead of constructing a new RegExp on every call. Cache patterns by prefix in a module-level Map or frozen prefix-to-pattern record, while preserving the existing contract-ID validation behavior for all callers.Source: Linters/SAST tools
apps/api/src/scan-plane/prisma-sast-evidence-access.store.ts (3)
271-302: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value
backfillDeletionSchedulesopens one serializable transaction per row.The raw query selects up to 128 packs. The loop then calls
this.loadfor each pack, and everyloadopens its own serializable transaction that re-reads the schedule, reads the pack with all fragments, revalidates the build result, and writes the schedule. With the default limit of 32 fromSastEvidenceDeletionTask, this is 32 sequential serializable transactions per tick.Batch the work into one transaction, or lower the per-tick limit. Add a comment that states the intended bound if the sequential shape is deliberate.
🤖 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 `@apps/api/src/scan-plane/prisma-sast-evidence-access.store.ts` around lines 271 - 302, Update backfillDeletionSchedules so the selected packs are processed within a single transaction rather than calling this.load separately for each row and opening one serializable transaction per pack. Reuse the existing load validation and schedule-writing behavior through a transaction-aware helper or equivalent batch path, while preserving the limit and scheduled count semantics.
589-619: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueAdd jitter to the serializable retry delay.
The backoff is
10ms * attempt, which is deterministic. Concurrent workers that abort on the same serialization failure retry at the same instants and collide again. Add a random component to each delay.♻️ Proposed change
await delay( - SERIALIZABLE_RETRY_BASE_DELAY_MILLISECONDS * attempt + SERIALIZABLE_RETRY_BASE_DELAY_MILLISECONDS * + attempt * + (1 + Math.random()) );🤖 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 `@apps/api/src/scan-plane/prisma-sast-evidence-access.store.ts` around lines 589 - 619, Update the retry delay in runSerializable to add a random jitter component to the existing SERIALIZABLE_RETRY_BASE_DELAY_MILLISECONDS * attempt backoff. Keep the linear attempt-based delay and retry conditions unchanged, ensuring each retry waits a different randomized duration to reduce concurrent collisions.
33-36: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winReduce the serializable transaction timeout on the request path.
loadruns insiderunSerializablewithSERIALIZABLE_TIMEOUT_MILLISECONDS = 120_000.loadalso writes, because it callscreateSchedulewhen no schedule row exists. The dashboard read path therefore holds a serializable read-write transaction for up to two minutes and holds a connection from the pool for that time.Two concurrent dashboard reads of the same unscheduled evidence pack also contend on
SastEvidenceDeletionSchedule_evidencePackId_key, whichisRetryableTransactionErrorhandles, but each retry repeats the same long window.Use a short timeout for the interactive request path, for example five to ten seconds, and keep the long timeout only for the background deletion task. Consider letting
SastEvidenceDeletionTaskown all schedule creation soloadbecomes read-only.Also applies to: 91-108
🤖 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 `@apps/api/src/scan-plane/prisma-sast-evidence-access.store.ts` around lines 33 - 36, The request-path transaction in load currently uses the two-minute SERIALIZABLE_TIMEOUT_MILLISECONDS, including when createSchedule writes a missing schedule. Reduce this timeout to a short interactive value of roughly five to ten seconds, while preserving the longer timeout exclusively for the background SastEvidenceDeletionTask; if schedule creation is moved, update load and createSchedule accordingly so load remains read-only.apps/api/prisma/migrations/20260810070000_sast_evidence_access_deletion/migration.sql (1)
232-243: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAdd indexes that support the new composite foreign keys.
SastEvidenceAccessDecisiondeclares foreign keys on("scanRequestId","tenantId","repositoryBindingId")and on("buildDecisionId","tenantId","repositoryBindingId","scanRequestId","attemptId"). No index has these columns as a leading prefix.SastEvidenceAccessDecision_lookup_idxstarts withtenantId, repositoryBindingId, evidencePackId, so PostgreSQL cannot use it for the referencing side. Every cascade delete of aScanRequestorSastEvidenceBuildDecisionthen triggers a sequential scan of the access ledger.SastEvidenceDeletionSchedulehas the same gap forscan_scope_fkey.⚡ Proposed indexes
CREATE INDEX "SastEvidenceAccessDecision_deletionScheduleId_idx" ON "SastEvidenceAccessDecision"("deletionScheduleId"); +CREATE INDEX "SastEvidenceAccessDecision_scan_scope_idx" + ON "SastEvidenceAccessDecision"("scanRequestId", "tenantId", "repositoryBindingId"); +CREATE INDEX "SastEvidenceAccessDecision_build_scope_idx" + ON "SastEvidenceAccessDecision"("buildDecisionId", "tenantId", "repositoryBindingId", "scanRequestId", "attemptId"); +CREATE INDEX "SastEvidenceDeletionSchedule_scan_scope_idx" + ON "SastEvidenceDeletionSchedule"("scanRequestId", "tenantId", "repositoryBindingId");Add the matching
@@indexentries toapps/api/prisma/schema.prismaso the schema and the migration stay in sync.🤖 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 `@apps/api/prisma/migrations/20260810070000_sast_evidence_access_deletion/migration.sql` around lines 232 - 243, Add indexes for the composite foreign-key columns on SastEvidenceAccessDecision, with leading prefixes matching (scanRequestId, tenantId, repositoryBindingId) and (buildDecisionId, tenantId, repositoryBindingId, scanRequestId, attemptId). Also add the corresponding scan-scope foreign-key index to SastEvidenceDeletionSchedule, keeping schema.prisma consistent with the migration.apps/api/src/scan-plane/sast-evidence-deletion.service.ts (1)
80-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the dead branch and log the deletion authority failure.
Both paths in this
catchblock return'RETRY_SCHEDULED', so theinstanceoftest has no effect. The error itself is discarded. A persistent provider failure then produces retries every tick with no operator signal, and the seven-day retention guarantee degrades silently.Inject a
Loggerand record the error name and theoperationIdat warn level. Keep the payload out of the log.♻️ Proposed change
} catch (error) { await this.safeRelease(candidate, referenceTime); - if ( - error instanceof - SastEvidenceDeletionAuthorityUnavailableError - ) { - return 'RETRY_SCHEDULED'; - } + this.logger.warn( + `Evidence deletion authority failed for operation ${candidate.schedule.operationId}: ${ + error instanceof Error ? error.name : 'UnknownError' + }` + ); return 'RETRY_SCHEDULED'; }Remove the now unused
SastEvidenceDeletionAuthorityUnavailableErrorimport if no other reference remains.🤖 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 `@apps/api/src/scan-plane/sast-evidence-deletion.service.ts` around lines 80 - 89, Update the catch block in the deletion flow to remove the redundant SastEvidenceDeletionAuthorityUnavailableError check and retain a single RETRY_SCHEDULED return after safeRelease. Inject and use Logger to warn with the caught error name and operationId, without logging the payload; remove the unused error import if no other references remain.
🤖 Prompt for all review comments with 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.
Inline comments:
In
`@apps/api/prisma/migrations/20260810070000_sast_evidence_access_deletion/migration.sql`:
- Around line 320-324: Define the tenant-offboarding behavior for the
SastEvidenceDeletionProof relationship by changing the
SastEvidenceDeletionProof_schedule_scope_fkey deletion policy to detach proofs
from SastEvidenceDeletionSchedule cascades, or document the required procedure
for removing existing proofs before deleting a tenant and its dependent records.
Ensure deletions of Tenant, RepositoryBinding, ScanRequest, or
SastEvidenceBuildDecision do not fail unexpectedly.
In `@apps/api/src/scan-plane/prisma-sast-evidence-access.store.ts`:
- Around line 336-348: The CONTEXT_DRIFT path must fence drifted claims before
failing so one claim cannot block the deletion queue. In
apps/api/src/scan-plane/prisma-sast-evidence-access.store.ts lines 336-348,
update claimDeletion to use a separate transaction that advances nextAttemptAt
and increments attemptCount for the drifted scheduleId, quarantining it after
the existing bounded attempt limit before raising the persistence error; in
apps/api/src/scan-plane/sast-evidence-deletion.service.ts lines 57-64, wrap
this.store.claimDeletion in try/catch, log the error, and return
'RETRY_SCHEDULED' so processBatch continues.
In `@apps/api/src/scan-plane/sast-evidence-access.service.ts`:
- Around line 834-843: Update reducedReference to return a denial result instead
of throwing when reducedEvidenceRef, redactedProjectionDigest, or
aiPayloadExpiresAt is missing. Adjust classifyForAi to handle this denied result
at its call site outside the try block, preserving the existing
denied('EVIDENCE_ACCESS_OUTPUT_INVALID', classified.decision) fail-closed
behavior and result contract.
In `@apps/api/src/scan-plane/sast-evidence-deletion.service.ts`:
- Around line 57-64: Update processNext around the claimDeletion call to catch
SastEvidenceAccessPersistenceError with code CONTEXT_DRIFT and handle it as an
idle/no-work result, preventing the batch from aborting; preserve propagation of
other errors and the existing candidate-processing flow.
In `@apps/api/src/scan-plane/sast-evidence-deletion.task.ts`:
- Around line 59-78: Update processBatch so each processNext deletion attempt
receives a freshly created Date rather than the batch-level referenceTime. Keep
the initial referenceTime for service.backfill, while preserving the existing
loop limits, IDLE handling, and processed counting.
In `@apps/api/test/scan-plane/sast-evidence-access.e2e-spec.ts`:
- Around line 423-424: Update the assertion for unavailableContext.result in the
SAST evidence access test so it fails when the result is either null or
undefined, rather than asserting against the optional pack property. Preserve
the existing deletionState assertion and verify the result object directly with
an appropriate non-nullish matcher.
In `@packages/shared/src/types/sast-evidence-access.ts`:
- Around line 329-336: Update buildSastEvidenceAccessDecision so evidenceExpiry,
decidedAt, and payloadExpiry are computed only within the allowed && purpose ===
'AI_ADVISORY' path. Use the resulting aiPayloadExpiresAt value directly where
the AI advisory response is built, while leaving dashboard and denied paths free
of date parsing.
- Around line 600-628: Update the decision validator’s `value.purpose` checks so
a denied decision requires `secondPassRedactionDecisionRef`,
`reducedEvidenceRef`, and `aiPayloadExpiresAt` to be exactly `null`, rather than
validating only their formats. Ensure the `aiPayloadExpiresAt` retention-window
parsing and comparisons run only for allowed decisions, preventing malformed
denied timestamps from passing validation.
---
Nitpick comments:
In
`@apps/api/prisma/migrations/20260810070000_sast_evidence_access_deletion/migration.sql`:
- Around line 232-243: Add indexes for the composite foreign-key columns on
SastEvidenceAccessDecision, with leading prefixes matching (scanRequestId,
tenantId, repositoryBindingId) and (buildDecisionId, tenantId,
repositoryBindingId, scanRequestId, attemptId). Also add the corresponding
scan-scope foreign-key index to SastEvidenceDeletionSchedule, keeping
schema.prisma consistent with the migration.
In `@apps/api/src/scan-plane/prisma-sast-evidence-access.store.ts`:
- Around line 271-302: Update backfillDeletionSchedules so the selected packs
are processed within a single transaction rather than calling this.load
separately for each row and opening one serializable transaction per pack. Reuse
the existing load validation and schedule-writing behavior through a
transaction-aware helper or equivalent batch path, while preserving the limit
and scheduled count semantics.
- Around line 589-619: Update the retry delay in runSerializable to add a random
jitter component to the existing SERIALIZABLE_RETRY_BASE_DELAY_MILLISECONDS *
attempt backoff. Keep the linear attempt-based delay and retry conditions
unchanged, ensuring each retry waits a different randomized duration to reduce
concurrent collisions.
- Around line 33-36: The request-path transaction in load currently uses the
two-minute SERIALIZABLE_TIMEOUT_MILLISECONDS, including when createSchedule
writes a missing schedule. Reduce this timeout to a short interactive value of
roughly five to ten seconds, while preserving the longer timeout exclusively for
the background SastEvidenceDeletionTask; if schedule creation is moved, update
load and createSchedule accordingly so load remains read-only.
In `@apps/api/src/scan-plane/sast-evidence-deletion.service.ts`:
- Around line 80-89: Update the catch block in the deletion flow to remove the
redundant SastEvidenceDeletionAuthorityUnavailableError check and retain a
single RETRY_SCHEDULED return after safeRelease. Inject and use Logger to warn
with the caught error name and operationId, without logging the payload; remove
the unused error import if no other references remain.
In `@apps/api/test/scan-plane/sast-evidence-access-persistence.e2e-spec.ts`:
- Around line 61-79: Refactor the test case `pins seven-day evidence and 24-hour
AI payload retention in code and SQL` to import the retention constants from
`@aegisai/shared` and assert their numeric seven-day and 24-hour values instead
of matching formatted source text. Remove the exact service and store expression
assertions, while retaining source-text checks only for the SQL interval and
constraint expressions.
- Around line 132-138: Add a shared test utility helper such as
readScanPlaneExports() to extract the ScanPlaneModule exports block, then
replace each local regex extraction and fallback with that helper. Update
apps/api/test/scan-plane/sast-evidence-access-persistence.e2e-spec.ts#L132-L138,
apps/api/test/scan-plane/sast-accepted-evidence-persistence.e2e-spec.ts#L254-L259,
apps/api/test/scan-plane/sast-scan-coverage-persistence.e2e-spec.ts#L117-L121,
and
apps/api/test/scan-plane/sast-scan-freshness-persistence.e2e-spec.ts#L173-L177;
each site requires the shared helper call and no duplicated regex.
In `@apps/api/test/scan-plane/sast-evidence-access.e2e-spec.ts`:
- Around line 506-512: Update MemoryAccessStore.finalizeDeletion and
releaseDeletion to return Promise.reject(...) for their failure paths instead of
throwing synchronously, matching the async contract. Change the finalizeDeletion
assertion in the test to await
expect(...).rejects.toThrow(SastEvidenceAccessPersistenceError), consistent with
the existing rejection assertions around lines 490-495.
In `@packages/shared/src/types/sast-evidence-access.ts`:
- Around line 838-843: Update isContractId to reuse precompiled regular
expressions instead of constructing a new RegExp on every call. Cache patterns
by prefix in a module-level Map or frozen prefix-to-pattern record, while
preserving the existing contract-ID validation behavior for all callers.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 31b02e21-f746-4b48-b1ec-bdc2fea07ed2
📒 Files selected for processing (34)
apps/api/prisma/migrations/20260810070000_sast_evidence_access_deletion/migration.sqlapps/api/prisma/schema.prismaapps/api/src/dashboard/dashboard-evidence.controller.tsapps/api/src/dashboard/dashboard.module.tsapps/api/src/scan-plane/prisma-sast-accepted-evidence.store.tsapps/api/src/scan-plane/prisma-sast-evidence-access.store.tsapps/api/src/scan-plane/sast-evidence-access.service.tsapps/api/src/scan-plane/sast-evidence-access.store.tsapps/api/src/scan-plane/sast-evidence-deletion.authority.tsapps/api/src/scan-plane/sast-evidence-deletion.service.tsapps/api/src/scan-plane/sast-evidence-deletion.task.tsapps/api/src/scan-plane/sast-evidence-secret-registry.tsapps/api/src/scan-plane/scan-plane.module.tsapps/api/test/scan-plane/sast-accepted-evidence-persistence.e2e-spec.tsapps/api/test/scan-plane/sast-evidence-access-persistence.e2e-spec.tsapps/api/test/scan-plane/sast-evidence-access.e2e-spec.tsapps/api/test/scan-plane/sast-finding-correlation-persistence.e2e-spec.tsapps/api/test/scan-plane/sast-finding-lineage-persistence.e2e-spec.tsapps/api/test/scan-plane/sast-scan-coverage-persistence.e2e-spec.tsapps/api/test/scan-plane/sast-scan-freshness-persistence.e2e-spec.tspackages/shared/src/index.tspackages/shared/src/types/sast-evidence-access.tspackages/shared/test/sast-evidence-access.test.mjsspecs/006-production-sast-runtime-design/contracts/sast-runtime.mdspecs/006-production-sast-runtime-design/data-model.mdspecs/006-production-sast-runtime-design/plan.mdspecs/006-production-sast-runtime-design/quality-gates.mdspecs/006-production-sast-runtime-design/quickstart.mdspecs/006-production-sast-runtime-design/research.mdspecs/006-production-sast-runtime-design/spec.mdspecs/006-production-sast-runtime-design/tasks.mdspecs/006-production-sast-runtime-design/threat-model.mdtest/github-actions/active-feature.test.mjstest/github-actions/ontology.test.mjs
🎋 작업 중인 브랜치 및 이슈
feat/282-006-evidence-access🔎 주요 변경 사항
sast-evidence-access-decision-v1shared 계약에 dashboard와 AI advisory를 분리한 exact-shape classification, canonical digest 및 zero downstream-authority 검증을 추가했습니다.ScanPlaneModule은 T042 access service만 T043 handoff로 export합니다.✅ 컨벤션 확인
type/issue-number-short-feature형식을 따르나요?<type>: <description>형식을 따르나요?Check List
검증
git diff --check31455491247성공006 진행 상태
Closes #282
Summary by CodeRabbit
New Features
Security
Documentation & Tests