' 07-secret-preflight-flow.puml — Stage 1.5 sequence
' For: docs/architecture/07-security-architecture.md
' Last revised: 2026-04-30
'
' v0.5.0 — Stage 1.5 between INGEST and PLAN. PathspecFilter runs first
' so a `.gitignore`-excluded `.env` never reaches the scanner. Block-by-
' default; --allow-secrets downgrades the abort to a WARN line.

@startuml
title Stage 1.5 — Pre-flight (filter then scan)

skinparam backgroundColor #FFFFFF
skinparam sequence {
  ParticipantBackgroundColor #7C3AED
  ParticipantFontColor #FFFFFF
  ActorBackgroundColor #F59E0B
  LifeLineBorderColor #6B7280
  ArrowColor #7C3AED
}

actor "Developer / CI" as dev
participant "main.py\n_run_preflight_stage" as main
participant "run_preflight\n(use case, Layer 2)" as uc
participant "PathspecFilterAdapter\n(.gitignore + .spectraignore)" as pf
participant "RegexSecretScanner\n(curated patterns)" as rs

dev -> main: --honor_gitignore (default True)\n--allow-secrets (default False)
main -> uc: run_preflight(repo_dir, file_tree,\nworkspace_filter, secret_scanner, config)

uc -> pf: filter_files(repo_dir, file_tree)
note right of pf
  `.gitignore` (root + nested),
  optional `.spectraignore`.
  Bypassing `.gitignore` is opt-in via
  --no-gitignore (still runs `.spectraignore`).
end note
pf --> uc: filtered files\n(`.env` etc. dropped if ignored)

uc -> rs: scan(repo_dir, filtered)
note right of rs
  Tier-S patterns:
    aws_access_key, github_pat,
    anthropic_key, bearer_token,
    slack_webhook, private_key.
  Plus .env* heuristic (ALL_CAPS=value, ≥12 chars).
  ≤1MB read cap per file. Never raises.
end note
rs --> uc: tuple[SecretFinding, ...]

alt findings AND not config.allow_secrets
  uc --> main: raise SecretDetectedError(SPEC-011)
  main -> dev: ✗ Secret detected: <path>:<line> (<pattern>)\n   Bypass with --allow-secrets
else clean OR allow_secrets
  uc --> main: PreflightResult(filtered, findings)
  main -> main: log WARN per allowed secret\n(forced visibility)
  main -> main: continue to Stage 2 PLAN with filtered tree
end

note over uc
  Filtered tree is the canonical input to
  every downstream stage — an excluded path
  can never reach a prompt body or a cache key.
end note

@enduml
