Skip to content

feat: 006 stale-scan denial 및 bounded retry 구현 - #279

Merged
goodtu02 merged 3 commits into
devfrom
feat/278-006-stale-scan-retry
Aug 10, 2026
Merged

feat: 006 stale-scan denial 및 bounded retry 구현#279
goodtu02 merged 3 commits into
devfrom
feat/278-006-stale-scan-retry

Conversation

@goodtu02

@goodtu02 goodtu02 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

🎋 작업 중인 브랜치 및 이슈

🔎 주요 변경 사항

  • sast-scan-freshness-v1 shared 계약에 latest-target observation, freshness/comparability 판정, bounded retry 계획·시도·결과와 canonical digest 및 exact-shape validator를 추가했습니다.
  • freshness 허용은 실제 provider observation과 이전 스캔 비교가 모두 있을 때만 가능하며, denial은 canonical reason code를 반드시 보존하도록 fail-closed 규칙을 적용했습니다.
  • T039 current/previous coverage decision을 denormalized row, scanner coverage record set/digest, immutable zero-authority publication ledger와 다시 대조해 stale·tampered source를 거부합니다.
  • provider observation sequence는 terminal coverage 이후이면서 미래가 아닌 값만 허용하고, lifecycle 소비 시 freshness decision보다 엄격히 새로운 observation을 요구합니다.
  • attempt 2는 fresh preflight signature 검증, attempt 1과 다른 plan ref, 동일 inventory, 새로운 sandbox/workload identity가 모두 성립할 때만 승인합니다.
  • retry kill switch를 기본 비활성화하고, 명시적 활성화 시 non-null snapshot digest를 요구하며 retry budget은 infrastructure-only 1회로 제한했습니다.
  • runtime retry admission과 begin-attempt 직전에 fresh preflight를 재검증해 caller-owned stale plan 또는 identity 재사용을 차단했습니다.
  • latest-target/retry authority 기본 provider를 fail-closed로 두고 T037 lifecycle gate를 canonical T040 service에 결박했습니다.
  • tenant/attempt-scoped observation, freshness, retry ledger 및 exact final-audit 관계를 Prisma migration에 rolling-safe 제약과 함께 추가했습니다.
  • ScanPlaneModule은 다음 단계에 T040 service만 export하며 SCM writer, route, AI payload 또는 외부 publication 권한은 열지 않습니다.
  • 006 contracts/data-model/research/threat-model/quality-gates/spec/plan/quickstart/tasks와 active-feature guard를 T040 완료 및 T041 다음 진입점으로 동기화했습니다.

✅ 컨벤션 확인

  • 브랜치명이 type/issue-number-short-feature 형식을 따르나요?
  • 이슈 제목과 PR 제목을 동일하게 작성했나요?
  • 커밋 메시지가 <type>: <description> 형식을 따르나요?

Check List

  • Assignees 등록을 하였나요?
  • 라벨(Label) 등록을 하였나요?
  • PR 머지 전 반드시 CI가 정상적으로 작동하는지 확인했나요?

검증

  • corepack pnpm lint
  • corepack pnpm typecheck
  • corepack pnpm build
  • corepack pnpm --filter @aegisai/api prisma:validate
  • node --test test/runtime/*.test.mjs (1/1)
  • shared 91/91 tests 통과
  • API 96 suites / 606 tests 통과
  • focused T040 API 36/36 tests 통과
  • GitHub Actions contract/active-feature/ontology 31/31 tests 통과
  • GitHub Actions CI run #31355057293 Verify Workspace 성공
  • 자동 리뷰 유효 지적 9건 반영 및 review thread 9/9 해결
  • git diff --check

006 진행 상태

  • T040 완료
  • 다음 작업: T041 deterministic comment planning and truthful lifecycle authority

Closes #278

Summary by CodeRabbit

  • New Features

    • Added SAST scan freshness checks using verified repository-target observations and prior-scan comparability.
    • Scan actions are now blocked when freshness, coverage, authority, or comparison checks are unavailable or invalid.
    • Added controlled retry handling for eligible infrastructure failures, limited to a single authorized second attempt.
    • Retry attempts now require renewed runtime attestations and distinct execution identities.
  • Documentation

    • Updated SAST runtime contracts, safety requirements, threat model, and release status for freshness and retry controls.
  • Tests

    • Added comprehensive coverage for freshness validation, replay protection, retry admission, and attestation security.

@goodtu02 goodtu02 self-assigned this Aug 10, 2026
@goodtu02 goodtu02 added the ✨ feat 새로운 기능 추가 label Aug 10, 2026 — with ChatGPT Codex Connector
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change implements T040 SAST freshness and comparability validation, durable retry decisions, Prisma persistence, runtime admission checks, preflight-bound attestations, module wiring, and contract, behavioral, and design validation.

Changes

SAST freshness and retry

Layer / File(s) Summary
Shared contracts and durable schema
packages/shared/src/types/sast-scan-freshness.ts, apps/api/prisma/schema.prisma, apps/api/prisma/migrations/...
Adds strict freshness/retry contracts, canonical digests, validators, decision evaluators, three durable models, constraints, indexes, and scoped foreign keys.
Persistence and evaluation
apps/api/src/scan-plane/sast-scan-freshness.store.ts, apps/api/src/scan-plane/prisma-sast-scan-freshness.store.ts, apps/api/src/scan-plane/sast-scan-freshness.service.ts
Loads and validates coverage context, target observations, retry predecessors, audit records, and replay state. Persists decisions with serializable transactions and bounded retries.
Runtime enforcement and wiring
apps/api/src/scan-plane/sast-scanner-runtime.service.ts, apps/api/src/scan-plane/prisma-sast-scanner-runtime.store.ts, apps/api/src/scan-plane/sandbox-runtime-attestation.service.ts, apps/api/src/scan-plane/scan-plane.module.ts, apps/api/src/scan-plane/scanner-workspace-manifest.service.ts
Requires retry authorization before attempt two, validates durable bindings, checks preflight data, binds attestations to effective preflight state, and exports SastScanFreshnessService.
Validation and design synchronization
apps/api/test/scan-plane/*, packages/shared/test/*, specs/006-production-sast-runtime-design/*, test/github-actions/*
Adds freshness, retry, replay, migration, runtime, module, and contract tests. Updates T040 design documents and completion guards.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ScannerRuntime
  participant FreshnessService
  participant TargetAuthority
  participant FreshnessStore
  participant RuntimeStore
  ScannerRuntime->>FreshnessService: authorize retry request
  FreshnessService->>FreshnessStore: load retry context
  FreshnessService->>TargetAuthority: observe target scope
  TargetAuthority-->>FreshnessService: verified target observation
  FreshnessService->>FreshnessStore: persist retry decision
  FreshnessStore-->>FreshnessService: durable decision
  FreshnessService-->>ScannerRuntime: authorized or rejected
  ScannerRuntime->>RuntimeStore: begin attempt two
  RuntimeStore-->>ScannerRuntime: persisted attempt with retry linkage
Loading

Possibly related PRs

  • AigisAI/AegisAI_v2#275: Provides the coverage decision and service that this change extends with freshness and retry authority.
  • AigisAI/AegisAI_v2#251: Provides the preflight-attestation flow that this change binds to scanner and sandbox runtime validation.
  • AigisAI/AegisAI_v2#253: Provides the scan-attempt and runtime-attestation lifecycle extended by durable attempt-two retry decisions.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement the linked issue's freshness, comparability, fail-closed, durable retry, schema, testing, and module-boundary requirements.
Out of Scope Changes check ✅ Passed The code, schema, tests, documentation, and feature-guard changes all support the linked T040 freshness and bounded-retry objectives.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main changes: stale-scan denial and bounded retry implementation for T040.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 45983129e0

ℹ️ 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".

Comment thread apps/api/prisma/migrations/20260810030000_sast_scan_freshness_retry/migration.sql Outdated
Comment thread apps/api/src/scan-plane/sast-scan-freshness.service.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (8)
apps/api/src/scan-plane/sast-scan-freshness.service.ts (2)

327-347: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Inject the time source into targetStillCurrent.

evaluate accepts a clock parameter, but targetStillCurrent calls Date.now() directly at Line 347. This makes the skew boundary untestable and inconsistent with the rest of the service. Pass a clock or a reference time through verify into this method.

🤖 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-scan-freshness.service.ts` around lines 327 -
347, Inject the existing clock or reference time used by evaluate into the
verify flow and pass it to targetStillCurrent. Replace the direct Date.now()
call in targetStillCurrent with that injected time while preserving the existing
five-second future-skew boundary.

258-264: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Gate readTargetObservation on explicit VERIFIED status.

SastLatestTargetObservationResult is currently VERIFIED | UNAVAILABLE, but readTargetObservation returns VERIFIED for anything except UNAVAILABLE while targetStillCurrent rejects everything except VERIFIED. Add a result.status !== 'VERIFIED' guard at the same point to prevent future statuses from leaking authority: 'VERIFIED'.

🤖 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-scan-freshness.service.ts` around lines 258 -
264, Update readTargetObservation to explicitly return the unavailable result
whenever result.status is not VERIFIED, rather than only when it equals
UNAVAILABLE. Preserve the existing VERIFIED path, and ensure future statuses
cannot produce authority: 'VERIFIED'.
apps/api/src/scan-plane/scan-plane.module.ts (1)

172-181: 🩺 Stability & Availability | 🔵 Trivial

Track the replacement of the unavailable authorities.

SastLatestTargetAuthority maps to UnavailableSastLatestTargetAuthority, which always returns { status: 'UNAVAILABLE' }. SastRetryRuntimeAuthority maps to UnavailableSastRetryRuntimeAuthority, which always reports scannerSetAvailable: false and killSwitchStatus: 'UNAVAILABLE'.

With this wiring, every freshness evaluation resolves to UNAVAILABLE authority and every retry decision resolves to retryAllowed: false. That matches the fail-closed T040 rollout. Add an alert or a startup log so an operator can see that the placeholder authorities are active, and so the swap to real implementations is not missed.

🤖 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/scan-plane.module.ts` around lines 172 - 181, Add a
startup log or alert in the module initialization path that clearly reports when
UnavailableSastLatestTargetAuthority and UnavailableSastRetryRuntimeAuthority
are wired for SastLatestTargetAuthority and SastRetryRuntimeAuthority. Keep the
existing fail-closed provider mappings unchanged and ensure the signal is
emitted once during application startup.
apps/api/src/scan-plane/sandbox-runtime-attestation.service.ts (1)

405-431: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the duplicated preflight predicate into packages/shared.

The validation at Lines 414-427 duplicates the preflight portion of isSastScannerWrapperExecutionRequestValid in packages/shared/src/types/sast-wrapper.ts: the 8192-byte attestationRef bound, the inventoryDigest equality against plan.repositoryState.inventoryDigest, the ACCEPT / RESTRICTED_ESCALATION plus RESTRICTED isolation rule, and the attempt-one / attempt-two attestationRef rule.

Attestation issuance and request validation must agree on these rules. Two copies can diverge. Export one predicate from packages/shared and call it from both places.

As per coding guidelines: "Place shared API contracts in packages/shared".

🤖 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/sandbox-runtime-attestation.service.ts` around lines
405 - 431, Extract the shared preflight validation predicate from
effectivePreflight and isSastScannerWrapperExecutionRequestValid into
packages/shared, exporting it from the shared SAST wrapper types. Preserve the
8192-byte attestationRef bound, inventoryDigest match, decision/isolation rules,
pathPolicyVersion bound, and attempt-based attestationRef rule, then replace
both local implementations with calls to the shared predicate.

Source: Coding guidelines

apps/api/src/scan-plane/prisma-sast-scan-freshness.store.ts (3)

241-266: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Select the predecessor attempt explicitly.

sastScanAttempts orders by attemptNumber descending and takes one row. The code then treats that row as the predecessor of request.attemptNumber. The query does not state that relationship.

If an attempt row with attemptNumber >= request.attemptNumber already exists, previous resolves to that row instead of the true predecessor. The flow still fails closed, because evaluateSastScanRetry then reports SANDBOX_IDENTITY_REUSED and RETRY_ATTEMPT_LIMIT_EXCEEDED. An explicit filter removes the ambiguity and makes the intent readable.

♻️ Proposed refactor
         sastScanAttempts: {
-          orderBy: { attemptNumber: 'desc' },
+          where: { attemptNumber: request.attemptNumber - 1 },
           take: 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 `@apps/api/src/scan-plane/prisma-sast-scan-freshness.store.ts` around lines 241
- 266, Update the sastScanAttempts query used before assigning previous to
filter attempts by attemptNumber less than request.attemptNumber, while
retaining the descending order and take: 1 behavior. This ensures previous is
explicitly the immediate predecessor rather than any existing attempt at or
after the requested attempt.

673-693: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add backoff between serializable retries.

runSerializable retries a serialization failure immediately. Under Serializable isolation, the competing transaction is often still running, so an immediate retry tends to conflict again and consumes the retry budget without progress.

Add a short jittered delay before each retry.

♻️ Proposed refactor
       } catch (error) {
         lastError = error;
         if (!isRetryableTransactionError(error) ||
           attempt === SERIALIZABLE_ATTEMPTS) {
           throw error;
         }
+        await new Promise((resolve) =>
+          setTimeout(resolve, attempt * 25 + Math.floor(Math.random() * 25))
+        );
       }
🤖 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-scan-freshness.store.ts` around lines 673
- 693, Add a short jittered delay in runSerializable before each retryable
transaction attempt, after confirming the error is retryable and another attempt
remains. Keep the final-attempt throw behavior unchanged, and use the existing
retry constants or established delay utility if available.

1103-1113: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Share one canonical serializer instead of duplicating stableJson.

This stableJson duplicates the private stableJson in packages/shared/src/types/sast-scan-freshness.ts lines 931-943. Both feed digest and equality comparisons that must agree exactly across the two packages.

The two implementations currently produce identical output. The shared version sorts with an explicit compareStrings comparator, and this version uses the default Array.prototype.sort(); both yield UTF-16 code-unit order for string keys. A future change to either one would silently break digest agreement, and the failure would surface as CONTEXT_DRIFT rather than as a serialization bug.

Export the canonical serializer from packages/shared and import it here.

As per coding guidelines: "Place shared API contracts in packages/shared".

🤖 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-scan-freshness.store.ts` around lines
1103 - 1113, Remove the local stableJson implementation and reuse the canonical
serializer from packages/shared. Export the existing stableJson from the shared
SAST scan freshness module, then import and use that symbol wherever this file
computes digest or equality serialization, preserving identical output across
packages.

Source: Coding guidelines

packages/shared/src/types/sast-scan-freshness.ts (1)

884-887: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Consider precompiling the contract-id patterns.

isContractId builds a new RegExp on every call. This function runs inside isSastScanFreshnessDecisionShapeValid and isSastScanRetryDecisionShapeValid, which the Prisma store calls on every load and persist. A small module-level cache removes the repeated compilation.

The ast-grep ReDoS hint is not applicable here. All callers pass literal prefixes, and the pattern contains no nested quantifiers.

♻️ Proposed refactor
+const CONTRACT_ID_PATTERNS = new Map<string, RegExp>();
+
 function isContractId(value: unknown, prefix: string): value is string {
-  return typeof value === 'string' &&
-    new RegExp(`^${prefix}:\\/\\/[a-f0-9]{64}$`, 'u').test(value);
+  if (typeof value !== 'string') return false;
+  let pattern = CONTRACT_ID_PATTERNS.get(prefix);
+  if (!pattern) {
+    pattern = new RegExp(`^${prefix}:\\/\\/[a-f0-9]{64}$`, 'u');
+    CONTRACT_ID_PATTERNS.set(prefix, pattern);
+  }
+  return pattern.test(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 `@packages/shared/src/types/sast-scan-freshness.ts` around lines 884 - 887,
Precompile and reuse contract-ID regular expressions instead of constructing one
on every call to isContractId. Add a module-level cache keyed by the literal
prefix, have isContractId retrieve or create the corresponding pattern, and
preserve the existing validation behavior for both freshness decision
validators.

Source: Linters/SAST tools

🤖 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/20260810030000_sast_scan_freshness_retry/migration.sql`:
- Around line 1-19: Adjust
apps/api/prisma/migrations/20260810030000_sast_scan_freshness_retry/migration.sql
at lines 1-19 to leave the NOT VALID t039_source_check in the initial
transaction and move its VALIDATE CONSTRAINT statement to a follow-up migration.
At lines 234-235 and 267-268, move the
SastScanCoverageDecision_comparison_scope_key and
SastScanAttempt_retryDecisionId_key builds into that separate non-transactional
migration using CREATE UNIQUE INDEX CONCURRENTLY. At lines 335-338, add
SastScanAttempt_retryDecisionId_fkey as NOT VALID in the initial migration and
validate it in the follow-up; indexes and checks on tables created by this
migration require no change.

In `@apps/api/src/scan-plane/prisma-sast-scan-freshness.store.ts`:
- Around line 409-462: The persistence behavior in persistRetryDecision
permanently consumes the requested-attempt slot for retryAllowed: false
decisions; confirm the intended policy. If denials are permanent, add a concise
comment at the decision-row creation explaining that the durable unique keys
intentionally prevent later authorization. If denials must be retriable, change
persistence to store only authorized decisions or key rows by decision inputs so
later evaluations can create a new decision, while preserving replay handling
for persisted outcomes.
- Around line 533-554: Update the previousRow lookup to exclude all coverage
decisions from the current scan request, not only the current decision id, so it
selects the newest genuinely comparable predecessor. Preserve the existing
tenant, repository, target, state, and timestamp filters, and decide separately
whether profile-family filtering belongs in the query or remains the evaluator’s
fail-closed PROFILE_FAMILY_INCOMPATIBLE outcome.

In `@apps/api/src/scan-plane/sandbox-runtime-attestation.service.ts`:
- Around line 49-57: Update issue so isSastScanPlanValid(binding.plan) is
evaluated before calling effectivePreflight(binding). Keep the existing
invalid-binding condition and Error('Sandbox runtime attestation binding is
invalid.') path intact, then resolve preflight only after plan validation
succeeds; leave verify unchanged.

In `@apps/api/test/scan-plane/sast-scan-freshness.e2e-spec.ts`:
- Around line 172-187: Rename the test around lifecycleDecision and
SastScanFreshnessService to describe sequence rollback rejection, since the
authority sequence is 1 while the decision sequence is 2. Do not call this a
same-sequence replay; add a separate same-sequence case only if rejection is
intended by the contract.

In `@packages/shared/src/types/sast-wrapper.ts`:
- Around line 446-451: Update the validation around
request.preflight.attestationRef and SCANNER_EXECUTION_REQUEST_INVALID so
attempt-two requests require evidence of a newly executed repository preflight,
not only an attestationRef different from
request.plan.repositoryState.attestationRef. Reuse the existing
repository-preflight state or validation symbol available in this flow, and
preserve the attempt-one equality and existing bounded-identifier checks.

In `@packages/shared/test/sast-scan-freshness.test.mjs`:
- Line 49: Fix the unused destructured binding in the decision-to-core
construction by removing decisionDigest without assigning it to _digest, while
preserving core as the decision object excluding decisionDigest.

---

Nitpick comments:
In `@apps/api/src/scan-plane/prisma-sast-scan-freshness.store.ts`:
- Around line 241-266: Update the sastScanAttempts query used before assigning
previous to filter attempts by attemptNumber less than request.attemptNumber,
while retaining the descending order and take: 1 behavior. This ensures previous
is explicitly the immediate predecessor rather than any existing attempt at or
after the requested attempt.
- Around line 673-693: Add a short jittered delay in runSerializable before each
retryable transaction attempt, after confirming the error is retryable and
another attempt remains. Keep the final-attempt throw behavior unchanged, and
use the existing retry constants or established delay utility if available.
- Around line 1103-1113: Remove the local stableJson implementation and reuse
the canonical serializer from packages/shared. Export the existing stableJson
from the shared SAST scan freshness module, then import and use that symbol
wherever this file computes digest or equality serialization, preserving
identical output across packages.

In `@apps/api/src/scan-plane/sandbox-runtime-attestation.service.ts`:
- Around line 405-431: Extract the shared preflight validation predicate from
effectivePreflight and isSastScannerWrapperExecutionRequestValid into
packages/shared, exporting it from the shared SAST wrapper types. Preserve the
8192-byte attestationRef bound, inventoryDigest match, decision/isolation rules,
pathPolicyVersion bound, and attempt-based attestationRef rule, then replace
both local implementations with calls to the shared predicate.

In `@apps/api/src/scan-plane/sast-scan-freshness.service.ts`:
- Around line 327-347: Inject the existing clock or reference time used by
evaluate into the verify flow and pass it to targetStillCurrent. Replace the
direct Date.now() call in targetStillCurrent with that injected time while
preserving the existing five-second future-skew boundary.
- Around line 258-264: Update readTargetObservation to explicitly return the
unavailable result whenever result.status is not VERIFIED, rather than only when
it equals UNAVAILABLE. Preserve the existing VERIFIED path, and ensure future
statuses cannot produce authority: 'VERIFIED'.

In `@apps/api/src/scan-plane/scan-plane.module.ts`:
- Around line 172-181: Add a startup log or alert in the module initialization
path that clearly reports when UnavailableSastLatestTargetAuthority and
UnavailableSastRetryRuntimeAuthority are wired for SastLatestTargetAuthority and
SastRetryRuntimeAuthority. Keep the existing fail-closed provider mappings
unchanged and ensure the signal is emitted once during application startup.

In `@packages/shared/src/types/sast-scan-freshness.ts`:
- Around line 884-887: Precompile and reuse contract-ID regular expressions
instead of constructing one on every call to isContractId. Add a module-level
cache keyed by the literal prefix, have isContractId retrieve or create the
corresponding pattern, and preserve the existing validation behavior for both
freshness decision validators.
🪄 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: e89dbcce-6200-47fe-837d-77feedb75d04

📥 Commits

Reviewing files that changed from the base of the PR and between 5ddeef6 and 4598312.

📒 Files selected for processing (34)
  • apps/api/prisma/migrations/20260810030000_sast_scan_freshness_retry/migration.sql
  • apps/api/prisma/schema.prisma
  • apps/api/src/scan-plane/prisma-sast-scan-freshness.store.ts
  • apps/api/src/scan-plane/prisma-sast-scanner-runtime.store.ts
  • apps/api/src/scan-plane/sandbox-runtime-attestation.service.ts
  • apps/api/src/scan-plane/sast-latest-target-authority.ts
  • apps/api/src/scan-plane/sast-retry-admission.gate.ts
  • apps/api/src/scan-plane/sast-retry-runtime-authority.ts
  • apps/api/src/scan-plane/sast-scan-freshness.service.ts
  • apps/api/src/scan-plane/sast-scan-freshness.store.ts
  • apps/api/src/scan-plane/sast-scanner-runtime.service.ts
  • apps/api/src/scan-plane/scan-plane.module.ts
  • apps/api/src/scan-plane/scanner-workspace-manifest.service.ts
  • apps/api/test/scan-plane/sast-finding-correlation-persistence.e2e-spec.ts
  • apps/api/test/scan-plane/sast-finding-lineage-persistence.e2e-spec.ts
  • apps/api/test/scan-plane/sast-scan-coverage-persistence.e2e-spec.ts
  • apps/api/test/scan-plane/sast-scan-freshness-persistence.e2e-spec.ts
  • apps/api/test/scan-plane/sast-scan-freshness.e2e-spec.ts
  • apps/api/test/scan-plane/sast-scanner-runtime.e2e-spec.ts
  • packages/shared/src/index.ts
  • packages/shared/src/types/sast-scan-freshness.ts
  • packages/shared/src/types/sast-wrapper.ts
  • packages/shared/test/sast-scan-freshness.test.mjs
  • specs/006-production-sast-runtime-design/contracts/sast-runtime.md
  • specs/006-production-sast-runtime-design/data-model.md
  • specs/006-production-sast-runtime-design/plan.md
  • specs/006-production-sast-runtime-design/quality-gates.md
  • specs/006-production-sast-runtime-design/quickstart.md
  • specs/006-production-sast-runtime-design/research.md
  • specs/006-production-sast-runtime-design/spec.md
  • specs/006-production-sast-runtime-design/tasks.md
  • specs/006-production-sast-runtime-design/threat-model.md
  • test/github-actions/active-feature.test.mjs
  • test/github-actions/ontology.test.mjs

Comment thread apps/api/prisma/migrations/20260810030000_sast_scan_freshness_retry/migration.sql Outdated
Comment thread apps/api/src/scan-plane/prisma-sast-scan-freshness.store.ts
Comment thread apps/api/src/scan-plane/prisma-sast-scan-freshness.store.ts
Comment thread apps/api/src/scan-plane/sandbox-runtime-attestation.service.ts Outdated
Comment thread apps/api/test/scan-plane/sast-scan-freshness.e2e-spec.ts Outdated
Comment thread packages/shared/src/types/sast-wrapper.ts
Comment thread packages/shared/test/sast-scan-freshness.test.mjs Outdated
@goodtu02
goodtu02 merged commit 9c018f5 into dev Aug 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feat 새로운 기능 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: 006 stale-scan denial 및 bounded retry 구현

1 participant