データ保持ポリシー / Data Retention Policy

July 4, 2026 · View on GitHub

対象 / Scope: Reftrix Preference Profiling, Part-Level Analysis, Section Visual Embedding & v0.3.0 Tables 最終更新 / Last Updated: 2026-05-03 バージョン / Version: 2.17.0


1. 対象データ / Data in Scope

対象テーブル / Tables

テーブル / Table内容 / Description主なカラム / Key Columns
preference_profilesユーザー嗜好プロファイル / User preference profilesid, name, preference_text, preference_embedding (768-dim vector), interaction_count, created_at, updated_at
preference_signals個別フィードバック記録 / Individual feedback recordsid, profile_id (FK), signal_type, signal_weight, target_type, target_id, feedback_text, created_at
component_parts / component_part_embeddingsUIパーツ分析データ / UI part analysis dataid, web_page_id (FK, CASCADE), partType, text_embedding (768-dim vector), visual_embedding (768-dim vector, nullable), boundingBox, computedStyles, textContent, innerHTML, piiRiskLevel, cssClasses, attributes, created_at, updated_at
section_embeddingsセクション視覚ベクトル / Section visual embeddingsection_pattern_id (FK, CASCADE), vision_embedding (768-dim vector, nullable)
audit_logs監査ログ / Audit logs (GDPR Art.30)id, timestamp, action, actor, target_type, target_id (truncated), details, ip_address, result
search_logs検索ログ / Search logsid, timestamp, query (200字制限), query_type, services, result_count, profile_id (truncated), latency_ms, cache_hit
design_snapshots / design_snapshot_sectionsデザインスナップショット / Design snapshotsid, web_page_id (FK, CASCADE), snapshot_at, section_count, text_embedding, vision_embedding
motion_patterns / motion_embeddingsモーションパターン(CSS/JS アニメーション)/ Motion patterns (CSS/JS animation)id, web_page_id (FK, SetNull at DB level), type, category, trigger, animation, properties, selector, text_embedding (768-dim vector, via motion_embeddings.motion_pattern_id), created_at

v0.4.0 PR7e-β4 M2 備考 / Note (updated): motion_patterns の FK は Prisma schema では onDelete: SetNull だが、data.delete 経路は gdpr-deletion.service.ts:59-71PAGE_RELATED_PATTERN_TABLES allowlist に motion_patterns を明示的に含める ことで CASCADE delete されるように実装されている (同時に motion_embeddingsPAGE_RELATED_EMBEDDING_TABLES で削除)。従って GDPR Art.17 data.delete(webPageId) 呼び出し時に motion 関連 2 テーブルは 確実に削除 される (残存しない)。この挙動は INV-DATA-DELETE-002 standing regression で契約レベル検証済 (apps/mcp-server/tests/regression/standing/gdpr-delete/ 配下)。 v0.4.0 PR7e-β4 M2 note (updated): Although the motion_patterns FK is onDelete: SetNull at the Prisma schema level, the data.delete path explicitly includes motion_patterns in the PAGE_RELATED_PATTERN_TABLES allowlist at gdpr-deletion.service.ts:59-71 (with motion_embeddings removed via PAGE_RELATED_EMBEDDING_TABLES), so it is CASCADE-deleted. GDPR Art.17 data.delete(webPageId) therefore reliably deletes both motion-related tables (no residue). This behavior is verified at contract level by the INV-DATA-DELETE-002 standing regression suite (apps/mcp-server/tests/regression/standing/gdpr-delete/).

データの性質 / Nature of Data

  • 処理目的 / Purpose of Processing: デザイン検索結果のパーソナライズ(嗜好に基づくリランキング) / Personalization of design search results (preference-based reranking)
  • データ主体 / Data Subjects: Reftrixツールを使用する個人ユーザー(ローカルファースト・シングルユーザー構成) / Individual users of the Reftrix tool (local-first, single-user architecture)
  • 個人データの種類 / Types of Personal Data: デザイン嗜好テキスト、フィードバック評価(positive/negative/neutral)、コメント / Design preference text, feedback ratings (positive/negative/neutral), comments

2. 保持期間 / Retention Period

方針 / Policy

嗜好プロファイルデータは、ユーザーが明示的に削除するまで保持されます。自動的な期限切れや定期削除はありません。

Preference profile data is retained until the user explicitly deletes it. There is no automatic expiration or scheduled deletion.

根拠 / Rationale

Reftrixはローカルファースト・シングルユーザーのMCPツールとして設計されています。データは原則としてユーザーのローカル環境に保存され、嗜好プロファイルはユーザーが継続的にツールを使用する間、検索品質の向上に寄与します。

Reftrix is designed as a local-first, single-user MCP tool. Data is stored in the user's local environment, and the preference profile contributes to search quality improvement while the user continues using the tool.

  • GDPR Art. 5(1)(e) — 保存制限の原則: 目的に必要な期間のみ保存。ユーザーが能動的に使用し続ける限り、パーソナライズ目的で保持することは適切です。 / Storage limitation principle: data kept only as long as necessary for the purpose. Retention for personalization is appropriate while the user actively uses the tool.
  • 処理の法的根拠 / Legal Basis for Processing: 正当な利益(GDPR Art. 6(1)(f))— ユーザーが自発的にツールを使用し、検索結果のパーソナライズを求めた場合。 / Legitimate interest (GDPR Art. 6(1)(f)) — when the user voluntarily uses the tool and seeks personalized search results.

本 §2 の適用範囲 / Scope of this §2 (UB-8 / M-1): 本 §2 は 嗜好プロファイルデータ専用 のスコープです。スクリーンショット(screenshot)の保持契約は本 §2 には含まれず、§9.6「Screenshot Retention(ユーザー閲覧用)」が正典 (canonical) です(preference と screenshot の保持契約混線を防止)。screenshot の保持期間・法的根拠・削除手段は §9.6 を参照してください。

Scope of this §2 (UB-8 / M-1): This §2 is scoped to preference-profile data only. The screenshot retention contract is NOT covered here; §9.6 "Screenshot Retention (for user viewing)" is canonical (preventing conflation of the preference and screenshot retention contracts). For screenshot retention period / legal basis / deletion path, see §9.6.


3. 削除方法 / Deletion Methods

3.1 ソフトリセット / Soft Reset

preference.reset(profile_id: "<uuid>", confirm: true)
  • 動作 / Behavior: preference_text, preference_embedding をクリアし、interaction_count を 0 にリセット。preference_signals は CASCADE 削除。プロファイルレコード自体は残存。 / Clears preference_text, preference_embedding, resets interaction_count to 0. preference_signals are CASCADE deleted. The profile record itself remains.
  • 用途 / Use Case: 嗜好をリセットして最初からやり直す場合。 / When the user wants to reset preferences and start fresh.
  • 可逆性 / Reversibility: シグナルデータは不可逆的に削除されます。プロファイルの枠は保持されます。 / Signal data is irreversibly deleted. The profile shell is preserved.

3.2 完全削除(ハードデリート) / Hard Delete

preference.reset(profile_id: "<uuid>", confirm: true, hard_delete: true)
  • 動作 / Behavior: preference_profiles レコードおよび関連する preference_signals を完全に削除。データベースから全痕跡を消去。 / Permanently deletes the preference_profiles record and all associated preference_signals. All traces are erased from the database.
  • 用途 / Use Case: GDPR「忘れられる権利」(Art. 17)に基づくデータ消去要求。 / GDPR Right to Erasure (Art. 17) data deletion request.
  • 可逆性 / Reversibility: 完全に不可逆。バックアップからの復元のみ。 / Completely irreversible. Recovery only from backups.
  • 監査ログ / Audit Log: 全環境で logger.warn による監査証跡を出力(PII truncate済み)。 / Audit trail via logger.warn in all environments (PII truncated).

pgvector 11 tables 削除契約 (data.delete(webPageId)) / pgvector 11-Table Deletion Contract (data.delete(webPageId))

data.delete(page)web_pages.id を起点に 9 page-linked embedding tables + 1 benchmark (two-path OR delete) + 1 cascade-via-snapshot = 計 11 pgvector tables を同期削除する。実装は apps/mcp-server/src/services/gdpr-deletion.service.ts:46-71 の 2 つの allowlist (PAGE_RELATED_EMBEDDING_TABLES / PAGE_RELATED_PATTERN_TABLES) を T1 Canonical とする (ADR-0016 Amendment 2-A)。

data.delete(page) synchronously deletes 11 pgvector tables from the web_pages.id root: 9 page-linked embedding tables + 1 benchmark (two-path OR delete) + 1 cascade-via-snapshot. The T1 Canonical source is the two allowlists at apps/mcp-server/src/services/gdpr-deletion.service.ts:46-71 (PAGE_RELATED_EMBEDDING_TABLES / PAGE_RELATED_PATTERN_TABLES) (ADR-0016 Amendment 2-A).

#テーブル / Table削除経路 / Deletion Path
1section_embeddingsFK section_pattern_idsection_patterns.id (PAGE_RELATED_EMBEDDING_TABLES)
2component_part_embeddingsFK component_part_idcomponent_parts.id (PAGE_RELATED_EMBEDDING_TABLES)
3motion_embeddingsFK motion_pattern_idmotion_patterns.id (PAGE_RELATED_EMBEDDING_TABLES)
4js_animation_embeddingsFK js_animation_pattern_idjs_animation_patterns.id (PAGE_RELATED_EMBEDDING_TABLES)
5webgl_animation_embeddingsFK webgl_animation_pattern_idwebgl_animation_patterns.id (PAGE_RELATED_EMBEDDING_TABLES)
6motion_analysis_embeddingsFK motion_analysis_result_idmotion_analysis_results.id (PAGE_RELATED_EMBEDDING_TABLES)
7design_narrative_embeddingsFK design_narrative_iddesign_narratives.id (PAGE_RELATED_EMBEDDING_TABLES)
8background_design_embeddingsFK background_design_idbackground_designs.id (PAGE_RELATED_EMBEDDING_TABLES)
9responsive_analysis_embeddingsFK responsive_analysis_idresponsive_analyses.id (PAGE_RELATED_EMBEDDING_TABLES)
10quality_benchmarks二経路 OR delete (direct web_page_id + indirect via section_pattern_id) — gdpr-deletion.service.ts:443-450 で両経路を OR で DELETE (PAGE_RELATED_EMBEDDING_TABLES)
11design_snapshot_sectionsCASCADE via design_snapshots (PAGE_RELATED_PATTERN_TABLES); Prisma onDelete: Cascade

対象外 / Out of scope for data.delete(page): preference_profiles.preference_embedding / preference_signals.embedding は profile 経路 (deleteProfile()) で削除される。

Out of scope for data.delete(page): preference_profiles.preference_embedding / preference_signals.embedding are deleted via the profile path (deleteProfile()), not the page path.

3s SLA 契約 / 3s SLA Contract: 11 table 合計削除 latency は 3 秒以内 が契約 (GDPR Art.17 "reasonable time" 実装)。standing regression INV-DATA-DELETE-002 で測定 (M2 実測値 27.39ms、limit の 1% 未満で十分な余裕) (ADR-0016 Amendment 3-B 参照)。

3s SLA Contract: Total 11-table deletion latency is contractually within 3 seconds (implementation of GDPR Art.17 "reasonable time"). Measured by standing regression INV-DATA-DELETE-002 (M2 measured: 27.39ms, less than 1% of the limit with ample headroom) (see ADR-0016 Amendment 3-B).

v0.4.0 PR7e-β4 PR2d (LCC-M-2): Active Backfill 同時実行時の削除挙動 / Deletion behavior with active backfill in flight (PR2d, LCC-M-2)

v0.4.0 PR7e-β4 PR2d (ADR-0015 Amendment 8 LCC-M-2) で、EMBEDDING_BACKFILL_FORK_ENABLED=true の下で 7 category backfill fork child が同時実行されているときの GDPR Art.17 data.delete 同期削除の挙動を明示する:

As of v0.4.0 PR7e-β4 PR2d (ADR-0015 Amendment 8 LCC-M-2), the behavior of GDPR Art.17 data.delete synchronous deletion while 7-category backfill fork children are in flight under EMBEDDING_BACKFILL_FORK_ENABLED=true:

  • 即時削除 / Immediate deletion: data.delete (MCP tool) は呼び出し時点で該当 web_page_id の全関連レコード (component_parts / component_part_embeddings / section_patterns / section_embeddings / motion_patterns / motion_embeddings / background_designs / background_embeddings / js_animation_patterns / js_animation_embeddings / responsive_analyses / responsive_embeddings / web_pages) を Prisma transaction 内で CASCADE 削除する (gdpr-deletion.service.ts:63PAGE_RELATED_PATTERN_TABLES allowlist 経由)。

  • Active fork child SIGTERM 伝達は PR3a 以降に分離: fork child process の SIGTERM 伝達 (active backfill を最大 60 秒 graceful wait してから削除) の完全実装は PR3a 以降に持ち越される (既知ギャップ、ADR-0015 Amendment 8 LCC-M-2)。PR2d 時点では:

    • 即時削除が同期実行される (child が同じ webPage の backfill 中でも待機しない)
    • child は削除後に DB query 時点で web_pages.id 欠損を検出し、getMissingJsAnimationEmbeddings 等が空結果 → backfill.done processedCount=0 で no-op exit(0) する (Graceful Degradation)
    • GDPR Art.17 は履行される (削除は即時完了)。child の 60s heartbeat / SIGKILL escalation 待ちで resurrect は起こらない (fork child は stateless、DB 以外に永続化しない)
  • Immediate deletion: data.delete (MCP tool) CASCADE-deletes all records related to the target web_page_id within a Prisma transaction at call time (via the PAGE_RELATED_PATTERN_TABLES allowlist in gdpr-deletion.service.ts:63).

  • Active fork-child SIGTERM propagation deferred to PR3a+: Full SIGTERM propagation to active fork children (graceful 60s wait before deletion) is deferred to PR3a+ (known gap, ADR-0015 Amendment 8 LCC-M-2). At PR2d time:

    • Deletion is synchronous (does not wait even if the child is backfilling the same webPage)
    • Child detects the missing web_pages.id during post-deletion DB queries; getMissingJsAnimationEmbeddings etc. return empty results → backfill.done processedCount=0 → no-op exit(0) (Graceful Degradation)
    • GDPR Art.17 is honored (deletion is immediate). No resurrection via the child's 60s heartbeat / SIGKILL escalation (the fork child is stateless; nothing persists outside the DB)

v0.4.0 PR-D-9 cross-ref: defensive WHERE clause TOCTOU resurrection invariant / 防御的 WHERE 節 TOCTOU resurrection 不変条件

v0.4.0 PR-D-9 で PR2d LCC-M-2 の synchronous deletion contract を TOCTOU resurrection invariant で強化:

As of v0.4.0 PR-D-9, the PR2d LCC-M-2 synchronous deletion contract is reinforced by a TOCTOU resurrection invariant:

  • Defensive WHERE clause / 防御的 WHERE 節: PR-D-9-A の auto-spawn-on-enqueue 配線で embedding-backfill.service.ts:1050-1075 (getMissingResponsiveEmbeddings) + embedding-backfill.service.ts:1090-1104 (countMissingResponsiveEmbeddings) に AND EXISTS (SELECT 1 FROM web_pages WHERE id = ?::uuid) 防御節を追加。data.delete(webPageId) が backfill in-flight 中に発火しても、後続の WHERE 節評価で web_pages row 不在を検出 → backfill_status='skipped_*' (no-op) で resurrection を防止 (FIND-PLAN-LCC-02 / C-07 / TPA-IMPL-02).

  • Defensive WHERE clause: PR-D-9-A's auto-spawn-on-enqueue wiring adds the AND EXISTS (SELECT 1 FROM web_pages WHERE id = ?::uuid) defensive predicate to embedding-backfill.service.ts:1050-1075 (getMissingResponsiveEmbeddings) + embedding-backfill.service.ts:1090-1104 (countMissingResponsiveEmbeddings). When data.delete(webPageId) fires while a backfill is in-flight, the subsequent WHERE-clause evaluation detects the missing web_pages row → backfill_status='skipped_*' (no-op) prevents resurrection (FIND-PLAN-LCC-02 / C-07 / TPA-IMPL-02).

  • Contract verification / 契約検証: INV-EMBEDDING-INTEGRITY-001 case #5 (tests/regression/standing/large-page/inv-embedding-integrity-001-bbox-and-responsive.test.ts) で 4-phase TOCTOU mirror として contract 化 (case landing per UNB-IMPL-2; INV-DATA-DELETE-002 cross-tag).

  • Contract verification: Contract-tested by INV-EMBEDDING-INTEGRITY-001 case #5 as a 4-phase TOCTOU mirror (tests/regression/standing/large-page/inv-embedding-integrity-001-bbox-and-responsive.test.ts); case landed per UNB-IMPL-2; INV-DATA-DELETE-002 cross-tag.

  • GDPR / 法的整合 / Legal alignment:

    • GDPR Art.17(1)(a) Right to Erasure: synchronous deletion contract preserved (immediate deletion, no graceful wait); defensive WHERE prevents post-deletion resurrection
    • GDPR Art.32(1)(b) integrity of processing: defensive WHERE clause is a state-of-the-art technical measure ensuring "ongoing confidentiality, integrity, availability and resilience of processing systems and services"
    • APPI 第23条 (安全管理措置) / Article 23 (Security control measures): technical safeguarding measure scope extended to backfill auto-spawn boundary
  • Cross-ref: ADR-0011 Amendment 2 (Vision unload precondition + autospawn handshake); ADR-0018 §Decision 1 Supplement (bbox_unresolvable SkipReason + audit_logs.action SSOT); Plan v1.1 §4.2 / §4.3 / §6.2 (case #5); Registry §11.6 OBS-3 (this docs-sync absorption).

監査証跡の例 / Audit Trail Example

[WARN] [PreferenceProfileService] Deleting profile (hard delete / GDPR erasure)
  { profileId: "01234567...", action: "hard_delete" }
[WARN] [PreferenceProfileService] Profile hard delete completed (GDPR erasure)
  { profileId: "01234567...", action: "hard_delete_completed" }

4. データエクスポート(データポータビリティ) / Data Export (Data Portability)

GDPR Art. 20 — データポータビリティの権利 / Right to Data Portability

preference.get(profile_id: "<uuid>", include_signals: true)
  • 動作 / Behavior: プロファイルの全データ(profile_id, name, preference_text, interaction_count, created_at, updated_at)に加え、全シグナルデータ(signal_type, signal_weight, target_type, target_id, feedback_text, created_at)を構造化JSONで返却。 / Returns all profile data plus all signal data in structured JSON format.
  • 出力形式 / Output Format: JSON(機械可読、構造化、一般的に使用される形式) / JSON (machine-readable, structured, commonly used format)
  • 用途 / Use Case: ユーザーが自身のデータを確認・取得する場合。別システムへのデータ移行。 / When users want to review/retrieve their data. Data migration to another system.

エクスポートデータ構造 / Export Data Structure

{
  "success": true,
  "data": {
    "profile_id": "uuid",
    "name": "default",
    "preference_text": "...",
    "interaction_count": 5,
    "created_at": "2026-03-08T00:00:00.000Z",
    "updated_at": "2026-03-08T12:00:00.000Z",
    "signals": [
      {
        "id": "uuid",
        "signal_type": "hearing_positive",
        "signal_weight": 1.0,
        "target_type": "web_page",
        "target_id": "uuid",
        "feedback_text": "...",
        "created_at": "2026-03-08T01:00:00.000Z"
      }
    ]
  }
}

適用されるGDPR条項 / Applicable GDPR Articles

条項 / Article内容 / SubjectReftrixでの対応 / Reftrix Implementation
Art. 5(1)(e)保存制限の原則 / Storage limitationユーザーが明示的に削除可能。自動期限切れなし(ローカルツールとして適切)。 / User can explicitly delete. No auto-expiry (appropriate for local tool).
Art. 6(1)(f)正当な利益 / Legitimate interestユーザーの自発的な使用に基づくパーソナライズ。 / Personalization based on voluntary user engagement.
Art. 13/14情報提供義務 / Information obligations本ドキュメントおよび preference.hearprofiling_notice で通知。 / Informed via this document and profiling_notice in preference.hear.
Art. 17忘れられる権利 / Right to erasurepreference.reset(hard_delete: true) で完全削除。 / Full deletion via preference.reset(hard_delete: true).
Art. 20データポータビリティの権利 / Right to data portabilitypreference.get(include_signals: true) でJSON形式エクスポート。 / JSON export via preference.get(include_signals: true).

データ処理のライフサイクル / Data Processing Lifecycle

1. 収集 / Collection
   preference.hear (Mode A) → サンプル提示 / Present samples
   preference.hear (Mode B) → フィードバック受信・プロファイル更新 / Receive feedback & update profile

2. 利用 / Usage
   検索リランキング / Search reranking (preference_embedding × search results)

3. エクスポート / Export
   preference.get (include_signals: true) → 全データJSON出力 / Full data JSON export

4. 削除 / Deletion
   preference.reset (confirm: true) → ソフトリセット / Soft reset
   preference.reset (confirm: true, hard_delete: true) → 完全削除 / Hard delete

5.1 Usage Telemetry データ保持 / Usage Telemetry Data Retention

v0.3.0 追加 / Added in v0.3.0

対象データ / Data in Scope

MCPツール利用計測ログ(オプトイン)。TOOL_USAGE_LOG_ENABLED=true 設定時のみ生成。

MCP tool usage telemetry log (opt-in). Only generated when TOOL_USAGE_LOG_ENABLED=true.

記録フィールド / Recorded Fields

フィールド / Field内容 / DescriptionPII保存形式 / Format
toolツール名 / Tool nameNoText
at実行時刻 / Execution timestampNoISO 8601
durationMs処理時間 / Duration in msNoNumber
success成否 / Success/failureNoBoolean

PIIフィールドは一切含まない: args, requestId, apiKey, error.message, query, profileId, url 等は記録対象外。

No PII fields included: args, requestId, apiKey, error.message, query, profileId, url, etc. are excluded.

保持期間 / Retention Period

  • ファイルサイズ上限 / File size limit: 100MB(超過時にローテーション / rotated on excess)
  • ローテーション世代数 / Rotation generations: 1世代(.old サフィックス / .old suffix)
  • 推奨保持期間 / Recommended retention: 90日(運用者がlogrotate等で管理 / managed by operator via logrotate etc.)
  • 削除方法 / Deletion: ログファイルを直接削除 / Delete the log file directly

データ処理のライフサイクル / Data Processing Lifecycle

1. 生成 / Generation
   handleToolCall() → buildTelemetryEntry() → 4フィールドのみ / 4 fields only

2. 保存 / Storage
   logToolUsage() → appendFileSync → logs/tool-usage.jsonl (fire-and-forget)

3. ローテーション / Rotation
   100MB超過時 → tool-usage.jsonl.old にリネーム / Renamed to .old on excess

4. 削除 / Deletion
   運用者が手動削除 / Operator manual deletion

6. セキュリティ措置 / Security Measures

措置 / Measure実装状況 / Implementation
入力バリデーション / Input validationZodスキーマによる全入力検証 / All inputs validated via Zod schemas
エラーメッセージサニタイズ / Error message sanitizationsanitizeErrorMessage() でDB構造の漏洩防止 / sanitizeErrorMessage() prevents DB structure leakage
PII配慮ログ / PII-aware loggingprofileId.slice(0, 8) + '...' でtruncate / Truncated with profileId.slice(0, 8) + '...'
監査証跡 / Audit trailhard_delete は全環境で logger.warn 出力 / hard_delete outputs logger.warn in all environments
CASCADE削除 / CASCADE deletionpreference_signals はプロファイル削除時に自動削除 / preference_signals auto-deleted on profile deletion

6.1 運用手順: Worker 二重稼働防止 (v0.4.0 PR7d-2+) / Operational procedures: Worker Dual-Run Prevention

AGPL-3.0 ライセンスに基づく透明性義務に従い、Worker プロセス運用の最新手順を以下に開示する。

Per AGPL-3.0 transparency obligations, the following documents the current Worker operational procedures.

  • 通常運用 / Normal operation: MCP サーバー起動時に WorkerSupervisor が page-analyze Worker を自動 fork する。手動起動は不要。 / The MCP server auto-forks the page-analyze Worker via WorkerSupervisor. Manual startup is unnecessary.
  • Redis ベース dual-run guard: 手動 pnpm worker:start:page 実行時、Redis key reftrix:worker:active:page に既存 lock が検出された場合は exit(1) する(二重稼働による Queue 競合消費と embeddingBackfillStatus race-condition 上書き防止、GDPR Art.32(1)(b) 完全性要件、ADR-0011 参照)。 / Manual pnpm worker:start:page fails with exit(1) on detecting an existing lock in Redis key reftrix:worker:active:page (preventing dual-consumption and race-condition overwrites; GDPR Art.32(1)(b), see ADR-0011).
  • Fail-open / fail-closed 判別 (PR7d-3 SEC M-1): Redis 不可到達時は fail-open (warn + 起動継続) で operator を締め出さない。既存 lock 検出 / race-lost 時は fail-closed (exit(1)) で二重稼働を構造的に防止する。この判別は WorkerActiveLockService.tryAcquireLock() / probeExistingLock() の discriminated union API で明示的に区別される。 / PR7d-3 SEC M-1: Redis-unreachable is fail-open (warn + continue) so operators are not trapped; existing-lock / race-lost is fail-closed (exit(1)). The distinction is explicit via discriminated union APIs.
  • Opt-out (明示的手動所有) / Explicit manual-owner opt-out: バッチスクリプト等で意図的に Worker を手動所有する場合、REFTRIX_ALLOW_MANUAL_WORKER=true を設定することで dual-run guard を bypass できる。推奨用法は apps/mcp-server/scripts/_worker-spawn-helper.ts::spawnPageAnalyzeWorkerChild() 経由(自動付与 + BullMQ getWorkers() pre-flight probe)。 / Set REFTRIX_ALLOW_MANUAL_WORKER=true to bypass the guard for intentional manual ownership. Use spawnPageAnalyzeWorkerChild() helper (auto-inject + pre-flight probe).
  • 自動設定 env var / Auto-managed env vars: REFTRIX_WORKER_IS_CHILD=1REFTRIX_WORKER_SUPERVISOR_BOOT_TOKEN=<UUID> は WorkerSupervisor が fork 時に自動注入する。operator が手動設定すると dual-run guard が誤って bypass されるため 手動設定禁止。 / REFTRIX_WORKER_IS_CHILD=1 and REFTRIX_WORKER_SUPERVISOR_BOOT_TOKEN=<UUID> are injected by WorkerSupervisor on fork. Do NOT set these manually — doing so would incorrectly bypass the guard.
  • TTL cron 削除の audit_logs 記録 (PR7d-3 LCC MEDIUM-2、retired in PR-SS-B / ADR-0041): Screenshot TTL cron (cleanupExpired, 7d default) は削除件数 > 0 実行を audit_logsaction=screenshot_ttl_cleanup として記録していた (GDPR Art.30 処理活動記録)。PR-SS-B (ADR-0041) で TTL cron + cleanupExpired 構造ごと撤去され、Screenshot 保持は data.delete まで (INV-SCREENSHOT-RETENTION-001)。撤去後は screenshot_ttl_cleanup を emit しない (INV-DATA-DELETE-002-B negative、§9.6 参照)。歴史的 audit_logs 行の有効性は不変。 / PR7d-3 LCC MEDIUM-2: the Screenshot TTL cron (cleanupExpired, 7d default) used to record deletedCount > 0 runs in audit_logs (GDPR Art.30). Removed together with the TTL cron + cleanupExpired structure in PR-SS-B (ADR-0041); Screenshot retention is now until data.delete (INV-SCREENSHOT-RETENTION-001), and after removal no screenshot_ttl_cleanup is emitted (INV-DATA-DELETE-002-B negative, see §9.6). Historical audit_logs rows remain valid.

6.2 Phase 0 Stale Row Cleanup Cron (v0.4.0 PR7e PR-B / LCC-M3-03)

PHASE0_EARLY_INSERT=true(opt-in)を有効化した環境では、Phase 0 (ingest) で robots.txt ブロック / SSRF 検証失敗 / DNS NXDOMAIN 等により早期失敗した場合でも、オーケストレーターが先行 INSERT した web_pages 行が DB に残存する(failure-path の markAnalysisFailedanalysisStatus='failed' に更新される)。これらの行を TTL ベースで定期削除する仕組みが Phase 0 Cleanup Cron である。

When PHASE0_EARLY_INSERT=true (opt-in) is enabled, rows inserted by the orchestrator's W0 upsert remain in DB even when Phase 0 (ingest) fails early due to robots.txt blocks / SSRF validation / DNS NXDOMAIN. The failure-path markAnalysisFailed transitions them to analysisStatus='failed'. The Phase 0 Cleanup Cron removes these rows via a TTL policy.

項目 / Item値 / Value
対象条件 / Deletion criteriaanalysisStatus='failed' AND lastAnalyzedPhase IS NULL AND analysisCompletedAt < NOW() - TTL
保持期間 / Retention7 日(環境変数 PHASE0_CLEANUP_OLDER_THAN_MS で上書き可能) / 7 days (override via PHASE0_CLEANUP_OLDER_THAN_MS)
cron 間隔 / cron interval24 時間(環境変数 PHASE0_CLEANUP_INTERVAL_MS) / 24 hours (override via PHASE0_CLEANUP_INTERVAL_MS)
バッチ上限 / Batch cap1000 件/回(環境変数 PHASE0_CLEANUP_MAX_BATCH_SIZE、絶対上限 100,000) / 1000/run (override via PHASE0_CLEANUP_MAX_BATCH_SIZE, absolute cap 100,000)
audit_logs 記録action=phase0_stale_cleanup, actor=system:phase0-cleanup-cron, targetType=web_page, deletedCount > 0 のみ記録 (GDPR Art.30) / recorded as action=phase0_stale_cleanup when deletedCount > 0 (GDPR Art.30)
実装 / Implementationapps/mcp-server/src/cron/phase0-cleanup-cron.ts + apps/mcp-server/src/services/phase0-cleanup.service.ts (旧 screenshot TTL cron と同型の setInterval パターンを踏襲。screenshot TTL cron 自体は PR-SS-B / ADR-0041 で撤去済) / Follows the same setInterval pattern as the former screenshot TTL cron (the screenshot TTL cron itself was removed in PR-SS-B / ADR-0041)

対象外 / Excluded: lastAnalyzedPhase IS NOT NULL(Phase 1 以降に進んだ失敗)の行は削除対象外。ユーザーが analysisStatus='failed' を確認する必要があるため、通常の(非 Phase 0)失敗は手動削除 or data.delete 経由で消す。 / Rows with lastAnalyzedPhase IS NOT NULL (failed after Phase 1+) are NOT deleted — users need to see these as analysisStatus='failed' and remove them via manual deletion or data.delete.

保持期間の根拠 / Retention rationale: 7 日は旧 screenshot TTL (PR-SS-B / ADR-0041 で撤去済) と揃えた運用上の基準として採用された値。Phase 0 層失敗は再試行で追いつく性質のものであり、長期保持の必要がない。 / 7 days was adopted as an operational baseline matching the former screenshot TTL (removed in PR-SS-B / ADR-0041). Phase 0-layer failures are typically retried, so long retention provides no value.

6.3 data.delete(webPageId) CASCADE 範囲 / CASCADE Scope for data.delete(webPageId)

GDPR Art.17 Right to Erasure 実装 (data.delete ツール) における web_pages 行削除時の CASCADE / SetNull 範囲を以下に明記する。本節は FIND-PR-B-004 (PR-B Phase 3 docs-sync) 着地として追記。

The following documents the CASCADE / SetNull scope when a web_pages row is deleted via the GDPR Art.17 Right to Erasure implementation (data.delete tool). Added as landing for FIND-PR-B-004 (PR-B Phase 3 docs-sync).

Direct CASCADE (5 tables) — 親行削除と同時に DB レベルで自動削除 / Auto-deleted at DB level when parent is deleted

テーブル / TableFK カラム / FK columnschema.prisma onDelete備考 / Notes
section_patternsweb_page_idCascadeセクション構造パターン / Section structure patterns
design_narrativesweb_page_idCascadeナラティブ分析 / Narrative analysis
responsive_analysesweb_page_idCascadeレスポンシブ分析 / Responsive analysis
component_partsweb_page_idCascadeUI パーツ (16 types) / UI parts (16 types)
design_snapshotsweb_page_idCascadeデザインスナップショット / Design snapshots

Indirect CASCADE (5 tables) — Direct CASCADE 親経由で連鎖削除 / Chain-deleted via Direct CASCADE parent

テーブル / Table親テーブル / ParentFK カラム / FK column備考 / Notes
section_embeddingssection_patterns (CASCADE)section_pattern_idセクション視覚/テキストベクトル / Section embeddings
design_narrative_embeddingsdesign_narratives (CASCADE)design_narrative_idナラティブ埋め込み / Narrative embeddings
responsive_analysis_embeddingsresponsive_analyses (CASCADE)responsive_analysis_idレスポンシブ埋め込み / Responsive embeddings
component_part_embeddingscomponent_parts (CASCADE)component_part_idパーツ視覚/テキストベクトル / Part embeddings
design_snapshot_sectionsdesign_snapshots (CASCADE)snapshot_idスナップショット内セクション / Sections within snapshots

SetNull (6 tables, preserved) — Prisma schema レベルでは保持、ただし data.delete 経路は allowlist で明示削除 / Preserved at Prisma schema level; data.delete path explicitly deletes via allowlist

テーブル / TableFK カラム / FK columnschema.prisma onDeletedata.delete 挙動 / data.delete behavior
motion_patternsweb_page_idSetNullPAGE_RELATED_PATTERN_TABLES allowlist で明示削除 (gdpr-deletion.service.ts:59-71) / Explicitly deleted via PAGE_RELATED_PATTERN_TABLES allowlist
js_animation_patternsweb_page_idSetNullPAGE_RELATED_PATTERN_TABLES allowlist で明示削除 / Explicitly deleted via allowlist
webgl_animation_patternsweb_page_idSetNullPAGE_RELATED_PATTERN_TABLES allowlist で明示削除 / Explicitly deleted via allowlist
motion_analysis_resultsweb_page_idSetNullPAGE_RELATED_PATTERN_TABLES allowlist で明示削除 / Explicitly deleted via allowlist
quality_evaluationsweb_page_idSetNullPAGE_RELATED_PATTERN_TABLES allowlist で明示削除 / Explicitly deleted via allowlist
background_designsweb_page_idSetNullPAGE_RELATED_PATTERN_TABLES allowlist で明示削除 / Explicitly deleted via allowlist

契約的保証 / Contractual guarantee: data.delete(webPageId) は上記 Direct CASCADE 5 + Indirect CASCADE 5 + SetNull 6 (allowlist) の 16 テーブル全てで確実な削除 を保証する (残存ゼロ)。GDPR Art.17 同期削除 SLA 3s (実測 27.39ms)。本契約は INV-DATA-DELETE-002 standing regression で契約レベル検証済 (apps/mcp-server/tests/regression/standing/gdpr-delete/)。

Contractual guarantee: data.delete(webPageId) guarantees reliable deletion across all 16 tables above (5 Direct CASCADE + 5 Indirect CASCADE + 6 SetNull via allowlist), with no residue. GDPR Art.17 synchronous deletion SLA: 3s (measured 27.39ms). Verified at contract level by INV-DATA-DELETE-002 standing regression.

関連実装 / Related implementation:

  • apps/mcp-server/src/services/gdpr-deletion.service.tsPAGE_RELATED_PATTERN_TABLES allowlist (SetNull テーブルの明示削除 / explicit deletion of SetNull tables), PAGE_RELATED_EMBEDDING_TABLES allowlist (embedding 削除 / embedding deletion)
  • packages/database/prisma/schema.prisma — FK onDelete 設定 / FK onDelete declarations
  • apps/mcp-server/tests/regression/standing/gdpr-delete/ — INV-DATA-DELETE-002 契約テスト / contract tests

6.4 Phase 0 ingest-fail 二層 retention 契約 + URL PII edge case (PR-INGEST-FAIL-ROW / ADR-0016 Amendment 6)

PR-INGEST-FAIL-ROW (BUG-2) で PHASE0_EARLY_INSERT default が false→true に flip し、Phase 0 (ingest) fetch fail でも web_pages 行が terminal analysisStatus='failed' で残存するようになった (NOROW closure)。これに伴い Phase 0 ingest-fail データには 二層 retention 契約 (CONS-6、SEC + LCC 両発行元 sign-off) が適用される。

In PR-INGEST-FAIL-ROW (BUG-2), the PHASE0_EARLY_INSERT default flipped false→true, so a web_pages row now persists with a terminal analysisStatus='failed' even when Phase 0 (ingest) fetch fails (NOROW closure). Accordingly, a two-tier retention contract (CONS-6, with SEC + LCC originator sign-off) applies to Phase 0 ingest-fail data.

層 / Tier対象 / Target保持 / Retention削除機構 / Deletion mechanism法的根拠 / Legal basis
row = transient (7d)Phase 0 fail の web_pages failed 行 / Phase 0-fail web_pages failed row7 日 / 7 daysPhase 0 Cleanup Cron (action=phase0_stale_cleanuplastAnalyzedPhase IS NULL filter、§6.2) / Phase 0 Cleanup Cron (lastAnalyzedPhase IS NULL filter, §6.2)GDPR Art.5(1)(c) データ最小化 / data minimisation
audit = permanent (365d)audit_logs.action='worker_restart_during_inflight_phase' (actor SSOT AUDIT_ACTOR_PAGE_ANALYZE_WORKER)365 日 / 365 days通常の audit_logs retention(手動削除なし)/ Standard audit_logs retention (no manual deletion)GDPR Art.30 processing-record

二層の分離理由: failed row 自体は再試行で追いつく性質のため transient (7d) で十分だが、fetch fail が発生したという 処理事実 (processing record) は GDPR Art.30 の監査証跡として permanent (365d) で保持する。両層は独立に管理される (row 削除後も audit_logs は残存)。

Rationale for the split: the failed row itself is transient (7d is sufficient, since Phase 0 failures are typically retried), but the processing record that a fetch fail occurred is retained permanently (365d) as a GDPR Art.30 audit trail. The two tiers are managed independently (audit_logs survive even after the row is deleted).

URL query param PII edge case (有力説 / leading hypothesis): early INSERT は fetch 前に web_pages.url を記録するため、query param に PII を含む URL (例: https://example.com/?email=user@example.com) が DB に残存しうる edge case がある。この row lifecycle は §6.2 の 7d transient cleanup cron とは独立に、data.delete(webPageId) cascade (§6.3 の 16-table CASCADE、onDelete: Cascade schema.prisma:2047 含む) で GDPR Art.17 即時 erasure が可能 (7d cron 待ちは不要)。

URL query-param PII edge case (leading hypothesis): because the early INSERT records web_pages.url before fetch, a URL whose query params carry PII (e.g. https://example.com/?email=user@example.com) may persist in the DB. This row lifecycle supports immediate GDPR Art.17 erasure via data.delete(webPageId) cascade (the 16-table CASCADE in §6.3, including onDelete: Cascade at schema.prisma:2047), independently of the §6.2 7d transient cleanup cron (no need to wait for the 7d cron).

免責事項 / Disclaimer: 本節の retention 契約および URL query param PII edge case の法的記述は一般的な情報提供を目的とし、特定事案への法的助言を構成するものではありません。具体判断には資格を有する弁護士にご相談ください。法的調査日: 2026-05-29 (AGPL §5(a) 更新日: 2026-05-30)。 / The retention contract and URL query-param PII edge-case statements in this section are for general informational purposes and do not constitute legal advice for specific cases. Consult a qualified attorney for specific judgments. Legal Research Date: 2026-05-29 (AGPL §5(a) modification date: 2026-05-30).

関連 / Cross-ref: ADR-0016 Amendment 6 §Decision 5 (二層 retention 契約) / §6.2 Phase 0 Stale Row Cleanup Cron / §6.3 data.delete(webPageId) CASCADE 範囲 / IO Impl Decision V0 anchor 019e7284-fa61.


7. 連絡先 / Contact

データ保護に関する質問やデータ削除要求については、プロジェクトのGitHubリポジトリをご参照ください。

For questions about data protection or data deletion requests, please refer to the project's GitHub repository.

  • GitHub: https://github.com/TKMD/ReftrixMCP
  • Issues: データ保護に関する問い合わせは GitHub Issues から送信できます。 / Data protection inquiries can be submitted via GitHub Issues.

免責事項 / Disclaimer

本ドキュメントは一般的な法的情報の提供を目的としており、特定の事案に対する法的助言を構成するものではありません。 具体的な法的判断が必要な場合は、資格を有する弁護士にご相談ください。

This document is intended to provide general legal information and does not constitute legal advice for any specific case. If specific legal judgment is needed, please consult a qualified attorney.

法的調査日 / Legal Research Date: 2026-04-20


8. Part-Level Analysis データ保持 / Part-Level Analysis Data Retention

対象データ / Data in Scope

テーブル / Tableカラム / Columns内容 / Description
component_parts / component_part_embeddingspartType, text_embedding, visual_embedding, boundingBox, computedStylesUIパーツの構造・スタイル・ベクトルデータ(16種類: button, link, image, video, form, input, heading, card, navigation, footer, cta, hero_image, icon, badge, tag, avatar) / UI part structure, style, and vector data (16 types)
component_parts / component_part_embeddingstextContent, innerHTMLパーツのテキスト内容・HTML構造(part.inspect でopt-in取得) / Part text content and HTML structure (opt-in retrieval via part.inspect)
component_parts / component_part_embeddingspiiRiskLevel, cssClasses, attributesPIIリスク判定結果、CSSクラス名、HTML属性 / PII risk assessment result, CSS class names, HTML attributes

保持方針 / Retention Policy

component_parts / component_part_embeddingsweb_pages テーブルに CASCADE 外部キーで紐づいています。保持期間は親レコード(web_pages)と同一です。

component_parts / component_part_embeddings is linked to the web_pages table via CASCADE foreign key. The retention period is the same as the parent record (web_pages).

  • 自動期限切れ / Auto-expiry: なし / None
  • 再分析時の動作 / Behavior on re-analysis: page.analyze 再実行時に clean-slate パターン(deleteMany + create)で上書き / Overwritten using the clean-slate pattern (deleteMany + create) on page.analyze re-execution
  • 手動削除 / Manual deletion: WebPageレコードの削除により CASCADE で自動実行 / Automatically executed via CASCADE when a WebPage record is deleted

PII保護 / PII Protection

piiRiskLevel='high' と判定されたパーツ(フォーム入力、パスワードフィールド等)では、visual_embedding の生成がスキップされます(カラム値は null)。これにより、PII を含む可能性のあるパーツのスクリーンショットからベクトルが生成されることを防止します。

Parts assessed as piiRiskLevel='high' (form inputs, password fields, etc.) have their visual_embedding generation skipped (column value is null). This prevents vector generation from screenshots of parts that may contain PII.

GDPR対応 / GDPR Compliance

条項 / Article内容 / Subject対応 / Implementation
Art. 17忘れられる権利 / Right to erasureWebPage削除時に全関連 component_parts / component_part_embeddings がCASCADE削除。テキスト内容、ベクトル、バウンディングボックス、スタイル情報を含むすべてのデータが物理削除される。 / All associated component_parts / component_part_embeddings are CASCADE deleted when WebPage is deleted. All data including text content, vectors, bounding boxes, and style information is permanently removed.
Art. 5(1)(c)データ最小化の原則 / Data minimisationpiiRiskLevel='high' パーツの visual embedding スキップにより、PII関連データの収集を最小化。 / Minimises PII-related data collection by skipping visual embedding for piiRiskLevel='high' parts.

9. Section Visual Embedding データ保持 / Section Visual Embedding Data Retention

対象データ / Data in Scope

テーブル / Tableカラム / Columns内容 / Description
section_embeddingsvision_embeddingセクションのスクリーンショットから生成された768次元ベクトル(DINOv2 ViT-B/14、L2正規化済み)。セクション高さ < 10px の場合はスキップ(null)。 / 768-dimensional vector generated from section screenshot (DINOv2 ViT-B/14, L2-normalized). Skipped (null) when section height < 10px.
section_embeddingsvision_skip_reasonセクション visual embedding の terminal-skip マーカー(nullable TEXT、enum 値のみ: section_visual_uncroppable / section_visual_duplicate / section_visual_pii_excluded / section_visual_blank / section_visual_no_position、PR-BT-2 + PR-C4 + secvisual-blank-terminal)。非 NULL = 構造的に embedding 生成不可ゆえ pending から除外。詳細は下記「Section Visual Terminal-Skip マーカー」を参照。 / Section visual embedding terminal-skip marker (nullable TEXT, enum-only: section_visual_uncroppable / section_visual_duplicate / section_visual_pii_excluded / section_visual_blank / section_visual_no_position, PR-BT-2 + PR-C4 + secvisual-blank-terminal). Non-NULL = structurally unable to generate the embedding, hence excluded from pending. See "Section Visual Terminal-Skip Marker" below.

保持方針 / Retention Policy

section_embeddingssection_patternsweb_pages テーブルに CASCADE 外部キーで紐づいています。保持期間は親レコード(web_pages)と同一です。

section_embeddings is linked via CASCADE foreign key through section_patternsweb_pages. The retention period is the same as the parent record (web_pages).

  • 自動期限切れ / Auto-expiry: なし / None
  • 再分析時の動作 / Behavior on re-analysis: page.analyze 再実行時に clean-slate パターン(deleteMany + create)で上書き / Overwritten using the clean-slate pattern (deleteMany + create) on page.analyze re-execution
  • 手動削除 / Manual deletion: WebPageレコードの削除により CASCADE で自動実行 / Automatically executed via CASCADE when a WebPage record is deleted

PII保護 / PII Protection

piiRiskLevel='high' と判定されたパーツ(component_parts)を含むセクションでは、vision_embedding の生成がスキップされます(カラム値は null)。これにより、PII を含む可能性のあるセクション領域のスクリーンショットからベクトルが生成されることを防止します(GDPR Art. 5(1)(c) データ最小化の原則)。

Sections containing parts (component_parts) assessed as piiRiskLevel='high' have their vision_embedding generation skipped (column value is null). This prevents vector generation from screenshots of section areas that may contain PII (GDPR Art. 5(1)(c) data minimisation principle).

フォールバック機構の透明性 / Fallback Mechanism Transparency

screenshotBase64の高さ範囲外セクションに対しては、SectionScreenshotFallbackService によるPlaywright個別スクリーンショット取得(フォールバック)が実行される場合がある。取得されたスクリーンショットはメモリ上でのみ一時保持され、DBには保存されない。最終成果物(vision_embedding)は通常パスと同一のパイプラインで処理される。

For sections outside the screenshotBase64 height range, SectionScreenshotFallbackService may capture individual screenshots via Playwright (fallback). Captured screenshots are held temporarily in memory only and are not persisted to the database. The final artifact (vision_embedding) is processed through the same pipeline as the normal path.

重複ベクトル検出(コサイン類似度 > 0.995)時は、vision_embedding の保存をスキップする(カラム値は null)。これはデータ最小化原則(GDPR Art. 5(1)(c))に沿った挙動であり、Hybrid Searchの品質向上に寄与する。

Duplicate vector detection (cosine similarity > 0.995) skips vision_embedding storage (column value remains null). This aligns with the data minimisation principle (GDPR Art. 5(1)(c)) and improves Hybrid Search quality.

Blank Image Detection + Dynamic Fallback (v0.1.9) / 白画像検出 + 動的フォールバック

fullPage screenshotでLazy Loading未描画セクションが白画像として取得された場合、isBlankImage()(Sharp stats RGB stddev < 5.0)で検出し、Section Screenshot Fallback(Playwright個別キャプチャ)で再取得する。

When fullPage screenshot captures lazy-loading unrendered sections as blank images, isBlankImage() (Sharp stats RGB stddev < 5.0) detects them, and Section Screenshot Fallback (Playwright individual capture) re-acquires the screenshots.

データフロー / Data Flow: 白画像検出 → Playwrightキャプチャ(メモリ上一時保持)→ DINOv2推論 → vision_embedding DB保存。画像バッファはメモリ上でのみ一時保持され、DINOv2処理後に参照解除される(GDPR Art. 5(1)(c)(e) データ最小化・保存制限の原則)。

Blank image detection → Playwright capture (held in memory only) → DINOv2 inference → vision_embedding saved to DB. Image buffers are held temporarily in memory only and dereferenced after DINOv2 processing (GDPR Art. 5(1)(c)(e) data minimisation and storage limitation principles).

動的Fallback対象セクション数上限 / Dynamic Fallback Section Cap: MAX_DYNAMIC_FALLBACK_SECTIONS = 20。通常Fallbackとの合計で50件上限を維持。highPiiSectionIds フィルタにより piiRiskLevel='high' パーツを含むセクションは除外される。

MAX_DYNAMIC_FALLBACK_SECTIONS = 20. Combined with normal fallback, the total cap of 50 sections is maintained. highPiiSectionIds filter excludes sections containing piiRiskLevel='high' parts.

Phase 5 一時ファイル(RAW decode最適化) / Phase 5 Temporary Files (RAW Decode Optimization)

Phase 5 メモリ制御において、DINOv2推論前のRAWピクセルデコード処理でメモリ圧力を軽減するため、一時ファイル(PNGクロップ画像 + RAWピクセルデータ)がOS一時ディレクトリ(os.tmpdir())に書き出される場合がある。

In Phase 5 memory control, to reduce memory pressure during RAW pixel decoding before DINOv2 inference, temporary files (PNG crop images + RAW pixel data) may be written to the OS temporary directory (os.tmpdir()).

一時ファイルの詳細 / Temporary File Details:

項目 / Item内容 / Description
ディレクトリ名 / Directory namereftrix-phase5-{random}fs.mkdtempSync() でランダム生成) / reftrix-phase5-{random} (randomly generated via fs.mkdtempSync())
ファイル内容 / File contentsセクションスクリーンショットのPNGクロップ画像、RAWピクセルデータ / Section screenshot PNG crop images, RAW pixel data
ファイルパーミッション / File permissions0o600(所有者のみ読み書き可能) / 0o600 (owner read/write only)
ディレクトリパーミッション / Directory permissions0o700(所有者のみアクセス可能) / 0o700 (owner access only)
ライフサイクル / LifecyclePhase 5完了後に finally ブロックで確実に削除。異常終了時も削除される。 / Reliably deleted in finally block after Phase 5 completion. Also deleted on abnormal termination.
PII保護 / PII protectionpiiRiskLevel='high' セクションはvisual embeddingスキップのため一時ファイルも生成されない(GDPR Art. 5(1)(c)) / piiRiskLevel='high' sections are skipped for visual embedding, so no temporary files are generated (GDPR Art. 5(1)(c))

データフロー / Data Flow: セクションクロップ画像 → PNGファイル書き出し(os.tmpdir()/reftrix-phase5-{random}/)→ RAWピクセルデコード → RAWファイル書き出し → DINOv2推論 → vision_embedding DB保存 → 一時ディレクトリ全体を再帰削除(finally 保証)。

Section crop image → PNG file write (os.tmpdir()/reftrix-phase5-{random}/) → RAW pixel decode → RAW file write → DINOv2 inference → vision_embedding saved to DB → entire temporary directory recursively deleted (finally guaranteed).

セキュリティ措置 / Security Measures: 一時ファイルは制限的パーミッション(ファイル: 0o600、ディレクトリ: 0o700)で作成され、他ユーザーからのアクセスを防止する。ディレクトリ名は fs.mkdtempSync() によりランダム生成され、予測不可能なパスとなる。

Temporary files are created with restrictive permissions (files: 0o600, directories: 0o700) to prevent access from other users. The directory name is randomly generated by fs.mkdtempSync(), resulting in an unpredictable path.

GDPR対応 / GDPR Compliance

条項 / Article内容 / Subject対応 / Implementation
Art. 17忘れられる権利 / Right to erasureWebPage削除時に全関連 section_embeddingsvision_embedding + vision_skip_reason 列を含む)がCASCADE削除。ベクトルデータを含むすべてのデータが物理削除される。 / All associated section_embeddings (including the vision_embedding + vision_skip_reason columns) are CASCADE deleted when WebPage is deleted. All data including vector data is permanently removed.
Art. 5(1)(c)データ最小化の原則 / Data minimisationpiiRiskLevel='high' パーツを含むセクションの visual embedding スキップにより、PII関連データの収集を最小化。 / Minimises PII-related data collection by skipping visual embedding for sections containing piiRiskLevel='high' parts.

Section Visual Terminal-Skip マーカー(vision_skip_reason, PR-BT-2) / Section Visual Terminal-Skip Marker (vision_skip_reason, PR-BT-2)

section_embeddings.vision_skip_reason(nullable TEXT、PR-BT-2 で追加)は、Phase 5 Queue-based Backfill において visual embedding が構造的に生成できないセクションを terminal-skip として行レベルで記録する。part_visualcomponent_part_embeddings.visual_skip_reason(ADR-0018 Amendment 7)と対称な仕組みであり、pending クエリ(sectionVisualPendingExclusionPredicate)で除外され、ページが永久 pending に留まることを防ぐ。

section_embeddings.vision_skip_reason (nullable TEXT, added in PR-BT-2) records at the row level which sections are structurally unable to have a visual embedding generated during Phase 5 Queue-based Backfill, as a terminal-skip. It is symmetric with part_visual's component_part_embeddings.visual_skip_reason (ADR-0018 Amendment 7), is excluded by the pending query (sectionVisualPendingExclusionPredicate), and prevents a page from staying permanently pending.

値 / Value意味 / Meaning
section_visual_uncroppablescreenshot 高さを超える off-range セクション(backfill worker は per-section Playwright capture を起動しないため crop 不能)。isOutOfRange && !fallbackEnabled 時のみ記録。 / Off-range section taller than the screenshot (uncroppable; the backfill worker launches no per-section Playwright capture). Written only when isOutOfRange && !fallbackEnabled.
section_visual_duplicate同一 type の sibling が cosine > 0.995 で視覚的代表を保持するため embedding が真に不要な dedup-skip セクション(Type-aware dedup 契約)。!fallbackEnabled 時のみ記録。 / Dedup-skipped section whose embedding is genuinely unnecessary because a same-type sibling at cosine > 0.995 represents the visual (Type-aware dedup contract). Written only when !fallbackEnabled.
section_visual_pii_excludedPR-C4 (ADR-0018 Amendment, PII 非対称 closure)component_parts.pii_risk_level='high' パーツを含むため work 側で visual embedding を意図的に非生成にした high-PII セクション(GDPR Art.5(1)(c) データ最小化)。SSOT helper emitSectionVisualPiiExcludedMarkersForPage が work-loop + backfill 両 path で当該 page の high-PII pending section に bulk write(冪等)し、発火時に GDPR Art.30 audit action embedding_section_visual_pii_excluded を emit。sectionVisualPendingExclusionPredicate の PII NOT EXISTS(Path A)と orthogonal な二重防御。 / High-PII section whose visual embedding was intentionally not generated on the work side because it contains a component_parts.pii_risk_level='high' part (GDPR Art.5(1)(c) data-minimisation). The SSOT helper emitSectionVisualPiiExcludedMarkersForPage bulk-writes it (idempotent) on both the work-loop and backfill paths and, on fire, emits the GDPR Art.30 audit action embedding_section_visual_pii_excluded. Orthogonal dual-defense with the predicate's PII NOT EXISTS (Path A).
section_visual_blanksecvisual-blank-terminal (Plan V1 §4)。backfill path(fallbackEnabled === false)で crop が white/uniform(isBlank === true)の section。描画されたが視覚内容を持たず、backfill worker は dynamic fallback re-capture queue を drain しないため embedding は構造的に不能 = terminal。非 PII の degraded-coverage technical terminal(描画済だが空 = uniform/blank crop)であり、section_visual_pii_excluded(PII 除外)とは意味が異なる。 / secvisual-blank-terminal (Plan V1 §4). A section whose crop is white/uniform (isBlank === true) on the backfill path (fallbackEnabled === false). The section was rendered but carries no visual content, and the backfill worker does not drain the dynamic fallback re-capture queue, so the embedding is structurally impossible = terminal. A NON-PII degraded-coverage technical terminal (rendered-but-empty = uniform/blank crop), distinct in meaning from section_visual_pii_excluded (PII exclusion).
section_visual_no_positionsecvisual-blank-terminal (Plan V1 §4)。backfill path(fallbackEnabled === false)で sectionPositionMap に position が無い、または height < 10(degenerate geometry)の section。crop 領域を決定できず embedding 構造的に不能 = terminal。非 PII の degraded-coverage technical terminal(layoutInfo.position geometry 欠落/退化)であり、section_visual_pii_excluded(PII 除外)とは意味が異なる。 / secvisual-blank-terminal (Plan V1 §4). A section absent from sectionPositionMap (no position) or with height < 10 (degenerate geometry) on the backfill path (fallbackEnabled === false). The crop region cannot be determined, so the embedding is structurally impossible = terminal. A NON-PII degraded-coverage technical terminal (absent/degenerate layoutInfo.position geometry), distinct in meaning from section_visual_pii_excluded (PII exclusion).

プライバシー(GDPR Art.4(1) 非該当・enum-only no-PII) / Privacy (GDPR Art.4(1) non-applicability, enum-only no-PII): vision_skip_reason は上記 5 つの enum 値のいずれか(または NULL)のみを格納し、PII を一切含まない(個人を識別する情報ではなく、section_embeddings 行の処理状態を示す技術的メタデータであり、GDPR Art.4(1) の「personal data」に該当しない)。本記述は、先行 migration packages/database/prisma/migrations/20260523090000_add_part_visual_skip_reason/migration.sqlPrivacy ブロック(lines 20-23)への direct citation であり、component_part_embeddings.visual_skip_reason で確立済みの Privacy 契約(enum-only / no-PII / CASCADE-deleted / Art.17 subsumed)を section_embeddings.vision_skip_reason に mirror したものである。section_visual_pii_excluded は GDPR Art.5(1)(c) データ最小化 + Art.30 processing-records を強化する(high-PII section の visual を生成しなかった理由を記録する)。secvisual-blank-terminal で追加された section_visual_blank / section_visual_no_position の 2 値は 非 PII の degraded-coverage technical terminal(描画済だが空 / geometry 欠落により embedding が構造的に不能)であり、GDPR Art.4(1) personal data に該当せず、section_visual_pii_excluded の PII 除外(Art.5(1)(c) data-minimisation)とは意味が異なる(混同してはならない)。(ADR-0018 Amendment 13) さらに screenshot_truncated(非 terminal / bounded-retryable)+ screenshot_truncated_expired(terminal)の 2 値が additive 追加され、section CHECK は 5→7 値に拡張された(writable set = terminal subset ∪ {screenshot_truncated})。両値も 非 PII の technical metadata であり、詳細は上記「Truncated-Screenshot Skip Reasons」サブセクションを参照。

vision_skip_reason stores only one of the five enum values above (or NULL) and contains no PII (it is technical metadata describing the processing state of a section_embeddings row, not information identifying an individual; it does not constitute "personal data" under GDPR Art.4(1)). This statement is a direct citation of the Privacy block (lines 20-23) of the precedent migration packages/database/prisma/migrations/20260523090000_add_part_visual_skip_reason/migration.sql, mirroring onto section_embeddings.vision_skip_reason the Privacy contract already established for component_part_embeddings.visual_skip_reason (enum-only / no-PII / CASCADE-deleted / Art.17 subsumed). section_visual_pii_excluded strengthens GDPR Art.5(1)(c) data-minimisation + Art.30 processing-records (it records why the high-PII section's visual was not generated). The two values added by secvisual-blank-terminal — section_visual_blank / section_visual_no_position — are NON-PII degraded-coverage technical terminals (rendered-but-empty / absent geometry making the embedding structurally impossible); they are NOT GDPR Art.4(1) personal data and differ in meaning from the section_visual_pii_excluded PII exclusion (Art.5(1)(c) data-minimisation), and MUST NOT be conflated with it. (ADR-0018 Amendment 13) Additionally, screenshot_truncated (non-terminal / bounded-retryable) + screenshot_truncated_expired (terminal) were additively added, extending the section CHECK 5→7 values (writable set = terminal subset ∪ {screenshot_truncated}); both are also NON-PII technical metadata — see the "Truncated-Screenshot Skip Reasons" subsection above for details.

保持・削除(CASCADE / Art.17 subsumed) / Retention & deletion (CASCADE / Art.17 subsumed): vision_skip_reasonsection_embeddings 行の一カラムであり、独立した保持期間を持たない。親 section_patternsweb_pages 行と共に CASCADE 削除され、GDPR Art.17 data.delete(行削除)に subsumed(追加の erasure ステップは不要)。再分析時は §9 の clean-slate パターン(deleteMany + create)で上書きされる。

vision_skip_reason is a single column of the section_embeddings row with no independent retention horizon. It is CASCADE-deleted together with the parent section_patternsweb_pages row, and is subsumed by GDPR Art.17 data.delete (row deletion; no additional erasure step is required). On re-analysis it is overwritten via the §9 clean-slate pattern (deleteMany + create).

Cross-ref: ADR-0018 Amendment 9 (section_visual terminal-skip symmetry) / ADR-0018 Amendment (PR-C4) (section_visual_pii_excluded PII 非対称 closure) / precedent migration packages/database/prisma/migrations/20260523090000_add_part_visual_skip_reason/migration.sql §Privacy (lines 20-23, FIND-RA-LCC-L-01 citation anchor) / migration packages/database/prisma/migrations/20260524192012_add_section_visual_skip_reason/migration.sql §Privacy (lines 28-34, initial 2-value) / migration packages/database/prisma/migrations/20260530120000_add_section_visual_pii_excluded_skip_reason/migration.sql §Privacy (adds section_visual_pii_excluded) / new migration packages/database/prisma/migrations/20260531090000_add_section_visual_blank_no_position_skip_reasons/migration.sql §Privacy (secvisual-blank-terminal: adds section_visual_blank + section_visual_no_position, 3→5 value CHECK) / apps/mcp-server/src/workers/phases/types.ts (EMBEDDING_SECTION_VISUAL_SKIP_REASONS SSOT) / IO Impl Decision anchor 019e59d8 / PR-C4 IO Impl Decision V3 (Finding Registry V4) / secvisual-blank-terminal IO Impl Decision V1 anchor 019e7f6c-9ff2 (Finding Registry V1) / INV-SECTION-VISUAL-BLANK-TERMINAL-015 (large-page).

embedding_section_visual_pii_excluded audit action (audit_logs.action, PR-C4) / embedding_section_visual_pii_excluded 監査アクション (audit_logs.action, PR-C4)

JP: PR-C4 (ADR-0018 Amendment, PII 非対称 closure) は新 audit_logs.action 固定 string embedding_section_visual_pii_excluded (SSOT AUDIT_ACTION_EMBEDDING_SECTION_VISUAL_PII_EXCLUDEDapps/mcp-server/src/audit/audit-actions.ts) を追加した。SSOT helper emitSectionVisualPiiExcludedMarkersForPage (phase-5-embedding.ts) が当該 page の high-PII pending section に section_visual_pii_excluded marker を bulk write し、marker 件数が 1 件以上のとき本 action を emit する(GDPR Art.5(1)(c) data-minimisation により high-PII section の visual を意図的に非生成にした processing record = GDPR Art.30 監査証跡)。

  • 保持期間 / Retention: 365 日(permanent audit tier)。既存 audit_logs retention 契約を継承(手動削除なし、worker_restart_during_inflight_phase / embedding_part_visual_skipped 等と同方針)。GDPR Art.30 processing-records 法的根拠。
  • actor: AUDIT_ACTOR_PAGE_ANALYZE_WORKERsystem:page-analyze-worker、work-side fork-child path、SSOT import、INV-AUDIT-EMIT-SSOT-IMPORT-001 で hardcoded literal 0 件を AST sweep)。
  • PII 保護 / PII protection: detailsPII-free で enum/numeric のみ(skipReason: "section_visual_pii_excluded" + excludedSectionCount: <number> のみ。生 error message / URL / user identifier を含まない)。targetIdAuditLogService.log() 内部で truncateAuditTargetId により truncate(8 文字 + ...、full UUID 非露出、CWE-209 / SEC-RV1-03 / U1。canonical CWE-209 PII Protection Pattern 準拠、`
  • 削除 / Deletion: audit_logs 行として独立 retention(365d)。親 web_pages 削除(data.delete)後も processing record として残存(GDPR Art.30 監査証跡の独立保持。§6.4 二層 retention 契約と同方針)。

EN: PR-C4 (ADR-0018 Amendment, PII asymmetry closure) adds the new audit_logs.action fixed-string embedding_section_visual_pii_excluded (SSOT AUDIT_ACTION_EMBEDDING_SECTION_VISUAL_PII_EXCLUDED, apps/mcp-server/src/audit/audit-actions.ts). The SSOT helper emitSectionVisualPiiExcludedMarkersForPage (phase-5-embedding.ts) bulk-writes the section_visual_pii_excluded marker to the page's high-PII pending sections and emits this action when one or more sections were marked (the processing record that the high-PII section's visual was intentionally not generated for GDPR Art.5(1)(c) data-minimisation = a GDPR Art.30 audit trail). Retention: 365 days (permanent audit tier), inheriting the existing audit_logs retention contract (no manual deletion). Actor: AUDIT_ACTOR_PAGE_ANALYZE_WORKER (SSOT import; INV-AUDIT-EMIT-SSOT-IMPORT-001 AST-sweeps for zero hardcoded literals). PII protection: details is PII-free, enum/numeric only (skipReason + excludedSectionCount; no raw error messages, URLs, or user identifiers); targetId is truncateAuditTargetId-truncated inside AuditLogService.log() (8 chars + ..., full UUID never exposed; CWE-209 / SEC-RV1-03 / U1; canonical CWE-209 PII Protection Pattern). Deletion: an independent audit_logs row (365d) that survives parent web_pages deletion (data.delete) as a GDPR Art.30 processing record (independent retention, same as the §6.4 two-tier contract).

Cross-ref: ADR-0018 Amendment (PR-C4) §Decision 2 / apps/mcp-server/src/audit/audit-actions.ts (AUDIT_ACTION_EMBEDDING_SECTION_VISUAL_PII_EXCLUDED SSOT) / apps/mcp-server/src/workers/phases/phase-5-embedding.ts (emitSectionVisualPiiExcludedMarkersForPage) / §"Canonical CWE-209 PII Protection Pattern" + §"Worker actor naming SSOT" /INV-AUDIT-EMIT-SSOT-IMPORT-001` (gdpr-delete standing) / PR-C4 IO Impl Decision V3 (Finding Registry V4).

Part Visual Residual Terminal-Skip マーカー(visual_skip_reason='bbox_unresolvable', PR-BT-4) / Part Visual Residual Terminal-Skip Marker (visual_skip_reason='bbox_unresolvable', PR-BT-4)

JP: component_part_embeddings.visual_skip_reason(nullable TEXT、ADR-0018 Amendment 7 / PR-F で追加済の既存列)は、PR-BT-4 (ADR-0018 Amendment 10 Decision 10.2) で backfill PartVisualProcessor の residual bbox-zero path からも 'bbox_unresolvable' enum 値で書き込まれるようになった。これにより、bbox が解決できず DINOv2 crop を生成できない residual part が visual_embedding IS NULL の永久 pending に留まらず terminal-skip に到達する。新 migration なし — 既存 visual_skip_reason 列 (enum: bbox_unresolvable / bbox_invalid / NULL) を再利用するため、retention horizon・PII 分類・CASCADE/Art.17 契約はすべて 不変

  • 保持期間(不変) / Retention (unchanged): visual_skip_reasoncomponent_part_embeddings 行の一カラムであり独立 retention horizon を持たない。親 component_partsweb_pages 行と共に CASCADE 削除され、GDPR Art.17 data.delete(行削除)に subsumed(追加 erasure ステップ不要)。
  • プライバシー(GDPR Art.4(1) 非該当・enum-only no-PII、不変) / Privacy (unchanged): 'bbox_unresolvable' は enum 値のいずれか(または NULL)のみを格納し PII を一切含まない(個人識別情報ではなく component_part_embeddings 行の処理状態を示す技術的メタデータ、GDPR Art.4(1) 非該当)。これは ADR-0018 Amendment 7 で bbox_invalid / bbox_unresolvable について確立済みの Privacy 契約を PR-BT-4 が新経路(residual path)で再利用したものであり、新しい PII surface を導入しない。

EN: component_part_embeddings.visual_skip_reason (nullable TEXT, the existing column added in ADR-0018 Amendment 7 / PR-F) is now also written with the 'bbox_unresolvable' enum value from the backfill PartVisualProcessor residual bbox-zero path in PR-BT-4 (ADR-0018 Amendment 10 Decision 10.2), so residual parts whose bbox cannot be resolved (no DINOv2 crop) reach a terminal-skip instead of staying visual_embedding IS NULL permanently pending. No new migration — it reuses the existing visual_skip_reason column (enum: bbox_unresolvable / bbox_invalid / NULL), so the retention horizon, PII classification, and CASCADE/Art.17 contract are all unchanged. Retention: a single column of the component_part_embeddings row with no independent retention horizon, CASCADE-deleted with the parent component_partsweb_pages row, and subsumed by GDPR Art.17 data.delete. Privacy: enum-only / no-PII (technical metadata describing the processing state of the row, not personal data under GDPR Art.4(1)) — PR-BT-4 reuses the Privacy contract already established in ADR-0018 Amendment 7 for bbox_invalid / bbox_unresolvable via a new path (the residual path), introducing no new PII surface.

Cross-ref: ADR-0018 Amendment 10 Decision 10.2 (residual Layer-1 marker, no Layer-2 propagation) / ADR-0018 Amendment 7 (visual_skip_reason column origin) / INV-BACKFILL-PART-RESIDUAL-MARKER-009 (large-page standing) / apps/mcp-server/src/queues/embedding-backfill-processors.ts (PartVisualProcessor residual writePartVisualTerminalSkipMarker) / IO Impl Decision V0 anchor 019e65a3-aaf7 (F-IMPL-L-09 docs-sync landing).

Truncated-Screenshot Skip Reasons (screenshot_truncated / screenshot_truncated_expired, ADR-0018 Amendment 13) / 切り詰めスクリーンショット skip reason

JP: ADR-0018 Amendment 13 (visual-backfill truncated-screenshot data-loss fix, PR-A) は part/section 双方の visual skip-reason 列に 2 つの新 enum 値を additive migration (20260608120000_add_screenshot_truncated_skip_reasons) で追加した。これにより SSOT EMBEDDING_SKIP_REASONS の baseline は 25 → 27 値 (screenshot_truncated + screenshot_truncated_expired)、part CHECK は 2→4 値、section CHECK は 5→7 値に additive 拡張された。WebGL サイトで Phase 0 が viewport-only (1920x1080) truncated screenshot を永続化したことに起因する off-screen part/section が、従来は無条件 terminal bbox_unresolvable / section_visual_uncroppable に収束し回復可能な data-loss が恒久 terminal 化していた問題を、truncation-gated + flag-gated な bounded-retryable 分類に改訂する。

値 / Valueterminal?意味 / Meaning
screenshot_truncated❌ 非 terminal (bounded-retryable)off-screen AND isScreenshotTruncated() AND fallbackEnabled のとき記録。3-way pending 述語で pending を維持し、skipped_fork_error retry bucket で再投入される。SCREENSHOT_TRUNCATED_RETRY_CAP (=5) 超過で screenshot_truncated_expired に収束。 / Written when off-screen AND truncated AND flag-ON; stays pending via the 3-way predicate, re-enqueued in the skipped_fork_error bucket; converges at retry cap=5.
screenshot_truncated_expired✅ terminalscreenshot_truncated 行が bounded budget (cap=5) を超過したときに reconciliation cron が収束させる terminal。page-level は not_required (≠failed) に pin され、false-failed-pin を構造的に排除。 / Terminal to which the reconciliation cron converges a screenshot_truncated row over the bounded budget; pins the page to not_required (NOT failed), structurally eliminating the false-failed pin.

プライバシー (GDPR Art.4(1) 非該当・enum-only no-PII) / Privacy (GDPR Art.4(1) non-applicability, enum-only no-PII): screenshot_truncated / screenshot_truncated_expiredvisual_skip_reason / vision_skip_reason の enum 値であり、PII を一切含まない (個人を識別する情報ではなく *_embeddings 行の処理状態を示す technical metadata、GDPR Recital 26 / Art.4(1) 非該当)。screenshot_truncated は GDPR Art.5(1)(d) accuracy 適合 (「visual 未生成だが bounded-retry 中」を正確記録)、screenshot_truncated_expired も accuracy 適合 (「budget 超過で生成不能を確定記録 = coverage 0」)。section_visual_pii_excluded (Art.5(1)(c) PII 除外) とは意味が異なり混同してはならない (NON-PII degraded-coverage technical terminal)。

保持・削除 (CASCADE / Art.17 subsumed、不変) / Retention & deletion (CASCADE / Art.17 subsumed, unchanged): 両値は component_part_embeddings / section_embeddings 行の一カラムであり独立 retention horizon を持たない。親 component_parts / section_patternsweb_pages 行と共に CASCADE 削除され、GDPR Art.17 data.delete (行削除) に subsumed (追加 erasure ステップ不要)。再分析時は §9 の clean-slate パターンで上書き。新 retention tier を一切導入しない

part vs section visual 回復の正確な姿 (TPA-IMPL-B-L-01) / Part vs section visual recovery honesty: classification / retry / terminal の state-machine は part/section 対称だが、実 visual 回復は非対称である。flag ON + section fallback 着地後、section visual のみ genuine 回復する (backfill section fallback が Playwright で per-section 再capture → DINOv2 visual embedding を実再生成)。part visual は genuine 回復しない — part loop は永続 truncated screenshot から crop し、section fallback 再capture バッファは part loop に書き戻されないため、part の screenshot_truncated 行は bounded budget を消費して screenshot_truncated_expired terminal に収束する (coverage 0)。part の coverage 0 は data-loss ではなく「truncated screenshot から part visual を構造的に生成不能」という事実の GDPR Art.5(1)(d) accuracy 適合な正確記録である (no-fake-success: partial を success 扱いしない)。詳細は ADR-0018 Amendment 13 §8.10 が canonical。

EN: ADR-0018 Amendment 13 (visual-backfill truncated-screenshot data-loss fix, PR-A) additively added 2 new enum values to both the part and section visual skip-reason columns via migration 20260608120000_add_screenshot_truncated_skip_reasons, growing the SSOT EMBEDDING_SKIP_REASONS baseline from 25 → 27 values (screenshot_truncated + screenshot_truncated_expired), the part CHECK 2→4 values, and the section CHECK 5→7 values. It revises the prior unconditional terminal (bbox_unresolvable / section_visual_uncroppable) classification — which had converged recoverable data-loss to a permanent terminal on WebGL sites where Phase 0 persists a viewport-only (1920x1080) truncated screenshot — into a truncation-gated + flag-gated bounded-retryable classification. screenshot_truncated is non-terminal (bounded-retryable: stays pending via the 3-way predicate, re-enqueued in the skipped_fork_error bucket, converges at SCREENSHOT_TRUNCATED_RETRY_CAP=5); screenshot_truncated_expired is terminal (the reconciliation cron pins the page to not_required, NOT failed). Privacy: both are enum-only visual_skip_reason / vision_skip_reason values with no PII (technical metadata, GDPR Recital 26 / Art.4(1) non-applicable); both are GDPR Art.5(1)(d) accuracy-compliant; distinct in MEANING from section_visual_pii_excluded (an Art.5(1)(c) PII exclusion) and MUST NOT be conflated. Retention & deletion: a single column with no independent retention horizon, CASCADE-deleted with the parent row, subsumed by GDPR Art.17 data.delete; no new retention tier is introduced. Part vs section visual recovery honesty (TPA-IMPL-B-L-01): the classification / retry / terminal state-machine is symmetric between part and section, but genuine visual recovery is asymmetric — after flag ON + section fallback, only the section visual is genuinely recovered (re-captured per-section via Playwright → DINOv2 regenerated), while the part visual is NOT (the section fallback's re-capture buffer is not written back into the part loop, so the part's screenshot_truncated rows consume the bounded budget and converge to screenshot_truncated_expired at coverage-0); the part's coverage-0 is NOT data-loss but the GDPR Art.5(1)(d) accuracy-compliant accurate record of the structural impossibility (no-fake-success). The accurate statement is canonical in ADR-0018 Amendment 13 §8.10.

Cross-ref: ADR-0018 Amendment 13 §8.2 / §8.3 / §8.5 / §8.8 / §8.10 / migration packages/database/prisma/migrations/20260608120000_add_screenshot_truncated_skip_reasons/migration.sql (part 2→4 / section 5→7 CHECK additive) / apps/mcp-server/src/workers/phases/types.ts (EMBEDDING_SKIP_REASONS 25→27 SSOT + 3-way predicates) / apps/mcp-server/src/workers/phases/screenshot-truncation.ts (isScreenshotTruncated leaf) / apps/mcp-server/src/services/backfill-reconciliation.service.ts (convergeTruncatedRowsOverRetryCap) / INV-SCHEMA-ENUM-004 (CHECK == writable set, enum 25→27) / INV-TRUNCATED-RETRY-BOUNDED-TERMINAL / INV-TRUNCATED-RETRYABLE-EXCLUDES-PII-TERMINAL (large-page / schema-enum-sync standing) / IO Impl Decision V2 = APPROVE (visual-backfill-fork-failure-impl-finding-registry-v2.md).

backfill_screenshot_truncated_expired audit action (audit_logs.action, ADR-0018 Amendment 13) / backfill_screenshot_truncated_expired 監査アクション

JP: ADR-0018 Amendment 13 §7.3 (M-09) は新 audit_logs.action 固定 string backfill_screenshot_truncated_expired (SSOT AUDIT_ACTION_BACKFILL_SCREENSHOT_TRUNCATED_EXPIREDapps/mcp-server/src/audit/audit-actions.ts) を追加した。reconciliation cron が screenshot_truncated part/section 行を SCREENSHOT_TRUNCATED_RETRY_CAP (=5) 超過で screenshot_truncated_expired terminal に収束させたとき emit する (GDPR Art.30 processing-records: bounded budget 超過で visual 生成不能を確定記録した processing record)。

  • 保持期間 / Retention: 365 日 (permanent audit tier)。既存 audit_logs retention 契約を継承 (手動削除なし、§11 §"Backfill auto-start audit actions" 等と同方針)。
  • actor: AUDIT_ACTOR_BACKFILL_RECONCILIATION_CRON (system:backfill-reconciliation-cron、SSOT import、INV-AUDIT-EMIT-SSOT-IMPORT-001 AST sweep で production bare literal 0)。
  • PII 保護 / PII protection: detailsPII-free で numeric/enum のみ (raw error message / URL / user identifier 不含)。targetIdtruncateAuditTargetId 適用 (8 文字 + ...、full UUID 非露出、CWE-209、canonical CWE-209 PII Protection Pattern ` 準拠)。
  • 削除 / Deletion: audit_logs 行として独立 retention (365d)。親 web_pages 削除後も processing record として残存 (GDPR Art.30 監査証跡の独立保持)。

EN: ADR-0018 Amendment 13 §7.3 (M-09) adds the new audit_logs.action fixed-string backfill_screenshot_truncated_expired (SSOT AUDIT_ACTION_BACKFILL_SCREENSHOT_TRUNCATED_EXPIRED). The reconciliation cron emits it when a screenshot_truncated part/section row exceeds SCREENSHOT_TRUNCATED_RETRY_CAP (=5) and is converged to the screenshot_truncated_expired terminal (the GDPR Art.30 processing-record that the visual is definitively ungenerable past the bounded budget). Retention: 365 days (permanent audit tier), inheriting the existing audit_logs retention contract. Actor: AUDIT_ACTOR_BACKFILL_RECONCILIATION_CRON (system:backfill-reconciliation-cron, SSOT import; INV-AUDIT-EMIT-SSOT-IMPORT-001 AST-sweeps for zero hardcoded literals). PII protection: details is PII-free, numeric/enum only; targetId is truncateAuditTargetId-truncated (8 chars + ...; CWE-209; canonical CWE-209 PII Protection Pattern). Deletion: an independent audit_logs row (365d) surviving parent web_pages deletion as a GDPR Art.30 processing record.

backfill_truncated_screenshot_repaired audit action (audit_logs.action, ADR-0018 Amendment 13) / backfill_truncated_screenshot_repaired 監査アクション

JP: ADR-0018 Amendment 13 §7.4 (2B repair) は新 audit_logs.action 固定 string backfill_truncated_screenshot_repaired (SSOT AUDIT_ACTION_BACKFILL_TRUNCATED_SCREENSHOT_REPAIRED) を追加した。one-shot repair script (apps/mcp-server/scripts/repair-truncated-screenshot-terminals.ts) が dry-run / --confirm 両モードで、truncated page 上の truncation-origin terminal marker (bbox_unresolvable / section_visual_uncroppable) を bounded-retryable screenshot_truncated に revert したときに emit する (GDPR Art.30 processing-records)。high-PII 行は常に除外 (Plan §5.3 H-02)。repair scope は truncation-origin off-screen のみであり、bbox_invalid (JSDOM-origin、screenshot 高さ非依存) は非回復対象 (誤 repair で false retryable を作らない)。

  • 保持期間 / Retention: 365 日 (permanent audit tier)、既存 audit_logs retention 契約継承。
  • actor: AUDIT_ACTOR_BACKFILL_TRUNCATED_REPAIR (system:backfill-truncated-repair、SSOT import)。
  • PII 保護 / PII protection: detailsPII-free で count/enum のみ。targetIdtruncateAuditTargetId 適用 (CWE-209)。
  • 削除 / Deletion: audit_logs 行として独立 retention (365d)、親 web_pages 削除後も残存。

EN: ADR-0018 Amendment 13 §7.4 (2B repair) adds the new audit_logs.action fixed-string backfill_truncated_screenshot_repaired (SSOT AUDIT_ACTION_BACKFILL_TRUNCATED_SCREENSHOT_REPAIRED). The one-shot repair script (apps/mcp-server/scripts/repair-truncated-screenshot-terminals.ts) emits it in both dry-run and --confirm modes when truncation-origin terminal markers (bbox_unresolvable / section_visual_uncroppable) on truncated pages are reverted to the bounded-retryable screenshot_truncated (GDPR Art.30 processing-records); high-PII rows are always excluded (Plan §5.3 H-02). The repair scope is truncation-origin off-screen only; bbox_invalid (JSDOM-origin, screenshot-height-independent) is NOT recovered (no false retryable from a mistaken repair). Retention: 365 days (permanent audit tier). Actor: AUDIT_ACTOR_BACKFILL_TRUNCATED_REPAIR (system:backfill-truncated-repair, SSOT import). PII protection: details is PII-free, count/enum only; targetId is truncateAuditTargetId-truncated (CWE-209). Deletion: an independent audit_logs row (365d) surviving parent web_pages deletion.

Cross-ref: ADR-0018 Amendment 13 §7.3 / §7.4 / §8.4 / apps/mcp-server/src/audit/audit-actions.ts (AUDIT_ACTION_BACKFILL_SCREENSHOT_TRUNCATED_EXPIRED / AUDIT_ACTION_BACKFILL_TRUNCATED_SCREENSHOT_REPAIRED / AUDIT_ACTOR_BACKFILL_TRUNCATED_REPAIR SSOT) / apps/mcp-server/src/services/backfill-reconciliation.service.ts (convergeTruncatedRowsOverRetryCap) / apps/mcp-server/scripts/repair-truncated-screenshot-terminals.ts (one-shot repair) / §"Canonical CWE-209 PII Protection Pattern" + §"Worker actor naming SSOT" /INV-AUDIT-EMIT-SSOT-IMPORT-001` (gdpr-delete standing) / IO Impl Decision V2 = APPROVE.

Phase 5 Per-Sub-Phase Fork — retention tier 不変 / Phase 5 Per-Sub-Phase Fork — Retention Tier Unchanged (PR-BT-5)

JP: PR-BT-5 (M-1-RSS、ADR-0039) の Phase 5 per-sub-phase fork-only model + recycle-disable fix は、embedding データ保持に関して 新しい retention tier を一切導入しない。各 sub-phase fork は単一 model を load → 1 sub-phase 処理 → exit(0) する処理アーキテクチャの変更にすぎず、出力ベクトル (*_embeddings.text_embedding / vision_embedding)・DB schema・GDPR 削除経路 (data.delete CASCADE)・screenshot persistence・TTL cron・audit_logs retention (365d) はすべて 不変。Phase 5 fork の stdout/stderr / crash 採取は per-job sub-child crash capture (Node.js process.report、§11.12) を継承し、本 PR は独立した retention horizon を新設しない。recycle-disable (PIPELINE_RECYCLE_THRESHOLD="0" fork-child override) は in-process メモリ管理 (arena 回収タイミング) のみに作用し、永続データの保持期間には一切影響しない。

EN: The PR-BT-5 (M-1-RSS, ADR-0039) Phase 5 per-sub-phase fork-only model + recycle-disable fix introduces no new retention tier for embedding data. It is purely a processing-architecture change (each sub-phase fork loads one model → processes one sub-phase → exit(0)); the output vectors (*_embeddings.text_embedding / vision_embedding), DB schema, GDPR deletion path (data.delete CASCADE), screenshot persistence, TTL cron, and audit_logs retention (365d) are all unchanged. Phase 5 fork stdout/stderr / crash capture inherits the per-job sub-child crash capture (Node.js process.report, §11.12); this PR does not establish an independent retention horizon. The recycle-disable (PIPELINE_RECYCLE_THRESHOLD="0" fork-child override) affects only in-process memory management (arena-reclamation timing) and has no effect on the retention horizon of any persisted data.

Cross-ref: ADR-0039 §AGPL §5(a)/(b) (DATA_RETENTION retention tier unchanged, LCC-L-01) / §Consequences Neutral (DB schema / GDPR deletion / screenshot persistence / TTL cron unchanged) / apps/mcp-server/src/workers/phases/shared/fork-common.ts:347 (recycle-disable fork-child override) / DATA_RETENTION §11.12 (per-job sub-child crash capture) / IO Impl Decision V0 anchor 019e65a3-aaf7 (F-IMPL-L-09 docs-sync landing).

Screenshot 永続化(v0.4.0 Queue-based Backfill) / Screenshot Persistence (v0.4.0 Queue-based Backfill)

Phase 5 Queue-based Backfill(v0.4.0 PR1)では、Phase 0 Ingest で取得した fullPage screenshot を page.analyze 完了後も保持する。これは非同期 backfill worker が元の screenshot にアクセスできるようにするため。従来の /tmp/reftrix-phase5-*/screenshot.png は Phase 5 終了時に削除されるため、非同期 backfill から参照できなかった。

Phase 5 Queue-based Backfill (v0.4.0 PR1) retains the fullPage screenshot captured in Phase 0 Ingest after page.analyze completes, allowing async backfill workers to access the original screenshot. The previous /tmp/reftrix-phase5-*/screenshot.png was deleted at Phase 5 end and was unreachable from async backfill.

保存先 / Storage Location:

項目 / Item内容 / Description
ルート / Root${REFTRIX_SCREENSHOT_ROOT}/phase5/(デフォルト /tmp/reftrix-screenshots/phase5/) / ${REFTRIX_SCREENSHOT_ROOT}/phase5/ (default /tmp/reftrix-screenshots/phase5/)
ファイル名 / Filename<webPageId>.png(webPageId は UUID v4/v7 正規表現で検証) / <webPageId>.png (webPageId validated by strict UUID v4/v7 regex)
DB カラム / DB Columnweb_pages.screenshot_storage_path TEXT NULL(絶対パスを保持、削除時は NULL 化) / web_pages.screenshot_storage_path TEXT NULL (stores absolute path, nulled on deletion)
DB カラム (PR2) / DB Column (PR2)web_pages.embedding_backfill_status EmbeddingBackfillStatus NOT NULL DEFAULT 'not_required'(PR2 v0.4.0: embedding skip/backfill 状態を追跡) / web_pages.embedding_backfill_status EmbeddingBackfillStatus NOT NULL DEFAULT 'not_required' (PR2 v0.4.0: tracks embedding skip/backfill status). Values: not_required / queued / in_progress / completed / failed / skipped_memory_pressure / skipped_fork_error. Partial index idx_web_pages_embedding_backfill_status WHERE status != 'not_required' for fast backfill queue lookup. PII 非該当 / Not PII: embedding_backfill_status は Phase 5 の処理状態メタデータ(enum 7値)であり、個人を識別可能な情報を含まない(GDPR Recital 26 の「識別不能情報」に該当)。独立した保持期間なし — web_pages 行の CASCADE DELETE で自動消去される(data.delete / GDPR Art.17 削除パスを含む)。 / embedding_backfill_status is processing-state metadata (7-value enum) for Phase 5 and contains no personally identifiable information (qualifies as "non-identifiable information" under GDPR Recital 26). No independent retention period — it is automatically removed via CASCADE DELETE on the web_pages row (including the data.delete / GDPR Art.17 erasure paths).
ファイルパーミッション / File permissions0o600(所有者のみ読み書き) / 0o600 (owner read/write only)
ディレクトリパーミッション / Directory permissions0o700(所有者のみアクセス可能) / 0o700 (owner access only)
書き込み方式 / Write strategyAtomic rename(<file>.tmp-<pid>-<rand> に書き込み後 rename()) / Atomic rename (write to <file>.tmp-<pid>-<rand>, then rename())
サイズ上限 / Size capデフォルト 50MB(SCREENSHOT_MAX_BYTES でオーバーライド可、絶対上限 500MB) / Default 50MB (override via SCREENSHOT_MAX_BYTES, absolute cap 500MB)
保持期間 / Retentiondata.delete まで(PR-SS-B / ADR-0041 で 7d TTL cron を構造ごと撤去、INV-SCREENSHOT-RETENTION-001。旧: 7d TTL の日次 cron 自動削除) / until data.delete (the 7d TTL cron was removed together with its structure in PR-SS-B / ADR-0041, INV-SCREENSHOT-RETENTION-001; formerly: 7-day TTL daily-cron deletion)

保持期間の根拠 / Retention Rationale: 7d TTL cron は当初 Queue-based Backfill の想定最長リトライウィンドウ (7 日) を根拠に screenshot を一括削除していた。PR-SS-B (ADR-0041) で TTL 構造を撤去し保持を data.delete までの trigger-event 明示型に再定義 したため、time-based の自動一括削除は行われなくなった (保持契約自体は ADR-0041 Decision 7 / §9.6 が正典)。

The 7d TTL cron originally bulk-deleted screenshots based on the assumed 7-day maximum retry horizon for Queue-based Backfill. PR-SS-B (ADR-0041) removed the TTL structure and redefined retention as the trigger-event-explicit "until data.delete", so no time-based automatic bulk deletion occurs (the retention contract itself is canonical in ADR-0041 Decision 7 / §9.6).

削除経路 / Deletion Paths:

PR-SS-B / ADR-0041 retired-sync note: Screenshot TTL cron (screenshot-cleanup-cron.ts module + cleanupExpired orchestrator + screenshot_ttl_cleanup audit emit) は PR-SS-B で構造ごと撤去された。以下の削除経路 2/3 (TTL cron) と削除経路マトリクスの「PR6 TTL cron」行・PR7c/PR7d narrative の dated 記述は 歴史的記録 として保存するが、現行の削除経路は GDPR data.delete 同期削除のみ (保持 = data.delete まで、INV-SCREENSHOT-RETENTION-001 / INV-DATA-DELETE-002-B negative)。/ The Screenshot TTL cron (the screenshot-cleanup-cron.ts module + cleanupExpired orchestrator + screenshot_ttl_cleanup audit emit) was removed together with its structure in PR-SS-B. The TTL-cron deletion path (2/3 below), the "PR6 TTL cron" row of the deletion-path matrix, and the dated PR7c/PR7d narrative are retained as historical records, but the current deletion path is GDPR data.delete synchronous deletion only (retention = until data.delete, INV-SCREENSHOT-RETENTION-001 / INV-DATA-DELETE-002-B negative).

  1. GDPR Art. 17 削除(現行の唯一の削除経路) / GDPR Art. 17 erasure (the current sole deletion path): data.delete target=page|all_user_data 実行時、GdprDeletionService.deletePage() / deleteAllUserData()ScreenshotPersistenceService.deleteScreenshot() を呼び出し、DB 行削除と合わせて永続化 PNG を物理削除する。DB トランザクション成功後に best-effort で削除し、ファイル削除失敗時も DB 変更は巻き戻さない。PR-SS-B (ADR-0041) で TTL cron が撤去されたため、ファイル削除失敗時の残存 PNG (orphan) は time-based TTL では回収されずdata.delete の再実行 / repair-truncated-screenshot-terminals.ts 系の運用 reconciliation 注記 (§9.6) に委ねられる。 / On data.delete target=page|all_user_data, GdprDeletionService.deletePage() / deleteAllUserData() invokes ScreenshotPersistenceService.deleteScreenshot(), removing the persisted PNG alongside DB rows. Best-effort deletion after DB commit; file failure does not roll back DB. Since PR-SS-B (ADR-0041) removed the TTL cron, an orphaned PNG left by a failed file deletion is not reaped by any time-based TTL; it relies on re-running data.delete / the operational reconciliation note (§9.6).
  2. TTL cron(v0.4.0 PR6 実装、retired in PR-SS-B / ADR-0041) / TTL cron (implemented in v0.4.0 PR6, retired in PR-SS-B / ADR-0041): かつて apps/mcp-server/src/cron/screenshot-cleanup-cron.tsscheduleScreenshotCleanupCron()start-workers.ts 起動時にセットアップし、デフォルト 24 時間ごとに cleanupExpired(olderThanMs=7d, { maxBatchSize=1000 }) を呼び出していた (間隔は SCREENSHOT_CLEANUP_INTERVAL_MS、保持期間は SCREENSHOT_CLEANUP_OLDER_THAN_MS、バッチサイズは SCREENSHOT_CLEANUP_MAX_BATCH_SIZE でオーバーライド可能だった)。PR-SS-B (ADR-0041) で cron module + cleanupExpired orchestrator + これら 4 つの SCREENSHOT_CLEANUP_* env var を構造ごと撤去。現行の削除経路は経路 1 (GDPR data.delete) のみ。 / The screenshot-cleanup-cron.ts module used to wire scheduleScreenshotCleanupCron() into start-workers.ts at startup, invoking cleanupExpired(olderThanMs=7d, { maxBatchSize=1000 }) every 24 hours by default (interval / retention / batch size overridable via the SCREENSHOT_CLEANUP_* env vars). PR-SS-B (ADR-0041) removed the cron module + cleanupExpired orchestrator + those 4 SCREENSHOT_CLEANUP_* env vars together with the structure. The current deletion path is path 1 (GDPR data.delete) only.

削除経路マトリクス / Deletion Path Matrix (v0.4.0 PR7d-3):

経路 / PathPR7b 以前 / Before PR7bPR7c / After PR7cPR7d-1 以降 / After PR7d-1PR7d-2 / After PR7d-2PR7d-3 / After PR7d-3
GDPR data.delete (Art. 17)同期削除(DB 行削除後に best-effort)同期削除(変更なし)同期削除(変更なし) / synchronous (unchanged)変更なし / unchanged変更なし / unchanged
Phase 5 fork orchestrator 完了時cleanupScreenshotAndTmp() で即時削除(deleteScreenshot 呼出)cleanupPhase5TmpDirOnly() が RAW decode tmp dir のみ best-effort 削除cleanupPhase5TempDir() に統合。3段 whitelist 防御(realpath + os.tmpdir() + reftrix-phase5-raw- prefix)を強制 / consolidatedRAW decode 書き込み先を os.tmpdir()/reftrix-phase5-raw-* へ修正変更なし / unchanged
page-analyze-worker Phase 5 後処理cleanupPhase5TempDir(phase5Dir) を呼出(永続化 PNG を削除)cleanupPhase5TempDir(phase5Dir) が残存(retention-over-deletion bug 温存、ADR-0010 で検出)解除。in-memory state.screenshotPngPath の null 化のみ / removed (null-out only)変更なし / unchanged変更なし / unchanged
page-analyze-worker finallycleanupPhase5TempDir(phase5Dir) を呼出cleanupPhase5TempDir(phase5Dir) が残存解除。in-memory null 化のみ / removed変更なし / unchanged変更なし / unchanged
PR6 TTL cron (cleanupExpired, 7d) → retired in PR-SS-B / ADR-0041定常削除(最終回収)定常削除 + PR7b までの即時削除分も担当定常削除(唯一の scheduled deletion path) / sole scheduled deletion path変更なし / unchangedaudit_logs 記録追加 (GDPR Art.30、削除件数 > 0 時のみ)。PR-SS-B (ADR-0041) で本経路を構造ごと撤去 — 現行は GDPR data.delete のみ / audit_logs recording added (GDPR Art.30, only when deletedCount > 0). Removed together with its structure in PR-SS-B (ADR-0041) — the current path is GDPR data.delete only

削除経路変更の理由 / Why paths changed (PR7c → PR7d-1):

PR7c (ADR-0009) は phase-5-fork-orchestrator.ts の即時削除を解除したが、page-analyze-worker.ts の Phase 5 後処理 / finally ブロックに残された cleanupPhase5TempDir(path.dirname(state.screenshotPngPath)) 呼び出しが見落とされていた。v0.4.0 PR1 で state.screenshotPngPath の指す先が永続化パス <REFTRIX_SCREENSHOT_ROOT>/phase5/<webPageId>.png に変わった結果、この呼び出しが永続化ディレクトリ全体を rmSync(recursive:true) で破壊するようになり、Queue-based Backfill (part_visual / section_visual) の visual embedding が再び 0 件になるバグ(retention-over-deletion bug)が発生。PR7d-1 (ADR-0010) でこの 2 箇所を削除し、cleanupPhase5TempDir 自体に realpath + os.tmpdir() + reftrix-phase5-raw- prefix の 3 段 whitelist 防御を追加して、類似の誤用を型レベルでは保証できないが関数レベルで遮断する。

PR7c (ADR-0009) removed the immediate deletion in phase-5-fork-orchestrator.ts but overlooked two cleanupPhase5TempDir(path.dirname(state.screenshotPngPath)) call sites in page-analyze-worker.ts (Phase 5 post-processing and the finally block). After v0.4.0 PR1 repointed state.screenshotPngPath at the persisted path <REFTRIX_SCREENSHOT_ROOT>/phase5/<webPageId>.png, those calls started destroying the persisted directory via rmSync(recursive:true), reintroducing the zero-visual-embedding bug. PR7d-1 (ADR-0010) removes both call sites and hardens cleanupPhase5TempDir itself with a three-stage whitelist defense (realpath + os.tmpdir() containment + reftrix-phase5-raw- prefix), providing function-level protection against similar misuse.

Historical Bug Transparency (PR7a–PR7c period) / 歴史的バグの透明性開示 (PR7a–PR7c 期間):

GDPR Art.17 の「不当な遅延なく削除」観点での透明性として、以下の期間における削除挙動を開示する:

For GDPR Art.17 "without undue delay" transparency, the following deletion behaviour during the noted periods is disclosed:

  • PR7a–PR7b 期間 (v0.4.0 PR1 merge 後〜PR7c 前): page-analyze-worker.ts の Phase 5 後処理 / finally に残存した cleanupPhase5TempDir(path.dirname(state.screenshotPngPath)) が、永続化 screenshot ディレクトリ <REFTRIX_SCREENSHOT_ROOT>/phase5/意図せず即時削除していた。結果的に Queue-based Backfill から visual embedding が生成できず、本来の 7 日間保持ポリシーが不成立だった。GDPR Art.17 の削除権は 逆に過剰に満たされていた(利用者データはむしろ迅速に消えていた)が、Art.5(1)(a) の「公正かつ透明な処理」および Art.32(1)(b) の「システムの完全性」観点では欠陥であった。

  • PR7c 期間 (ADR-0009 merge 後〜PR7d-1 前): phase-5-fork-orchestrator.ts 側の即時削除は解除されたが、page-analyze-worker.ts 側の retention-over-deletion bug は残存。ADR-0009 の意図(PR6 TTL cron + GDPR 2 経路化)と実装の整合が取れていなかった。

  • PR7d-1 以降 (PR-SS-B 前の dated state): 2 箇所の worker cleanup を削除し、永続化 screenshot の削除経路は GDPR data.delete(即時)+ PR6 TTL cron(7 日)の 2 本のみに確実に集約 (この 2 本構成は PR-SS-B / ADR-0041 で TTL cron 撤去により GDPR data.delete 1 本へ縮退)。cleanupPhase5TempDir の 3 段 whitelist 防御により、同種の誤用を関数レベルで遮断。既存の破損レコードscreenshotStoragePath が NOT NULL で embeddingBackfillStatusqueued / in_progress のまま残るページ)は新規修復スクリプト apps/mcp-server/scripts/repair-orphaned-backfill-records.tsfailed + skipped_screenshot_missing 遷移させる。

  • PR7a–PR7b window: the leftover cleanupPhase5TempDir(path.dirname(state.screenshotPngPath)) calls in page-analyze-worker.ts unintentionally deleted the persisted screenshot directory immediately — the intended 7-day retention did not hold. Users' data was erased faster than the policy promised, which over-satisfied the Art.17 erasure right but violated Art.5(1)(a) fair-and-transparent processing and Art.32(1)(b) integrity-of-systems guarantees.

  • PR7c window: the orchestrator-side immediate deletion was removed, but the worker-side retention-over-deletion bug persisted. ADR-0009 intent (PR6 TTL cron + GDPR two-path consolidation) did not match the implementation.

  • After PR7d-1 (the pre-PR-SS-B dated state): the two worker cleanup call sites are removed; persisted-screenshot deletion is truly consolidated into GDPR data.delete (synchronous) and the PR6 TTL cron (7-day) (this two-path setup later shrank to GDPR data.delete alone when PR-SS-B / ADR-0041 removed the TTL cron). cleanupPhase5TempDir's three-stage whitelist defense blocks similar misuse at the function level. Pre-existing corrupted rows (screenshotStoragePath NOT NULL with embeddingBackfillStatus stuck at queued / in_progress) are repaired via the new apps/mcp-server/scripts/repair-orphaned-backfill-records.ts script, transitioning them to failed + skipped_screenshot_missing.

Race Condition / 競合状態:

data.delete(GDPR Art.17)と Backfill の競合は 3 シナリオ(Backfill in_progress / queued / fd open)で発生し得るが、いずれも既存の best-effort ハンドラ(FK ROLLBACK / P2025 swallow / Linux unlink() inode 残存特性)で吸収される。TTL cron との競合シナリオは PR-SS-B (ADR-0041) で TTL cron が撤去されたため消滅した (SCREENSHOT_TTL_GRACE_PERIOD_MS grace window も同時に retire)。詳細は [ADR-0009 §M9 Race Condition Handling](/ 参照 (TTL 側は ADR-0041 で supersede)。

data.delete (GDPR Art.17) can race with Backfill in 3 scenarios (Backfill in_progress / queued / fd open), all absorbed by existing best-effort handlers (FK ROLLBACK / P2025 swallow / Linux unlink() inode persistence). The TTL-cron race scenario was eliminated when PR-SS-B (ADR-0041) removed the TTL cron (the SCREENSHOT_TTL_GRACE_PERIOD_MS grace window was retired at the same time). See [ADR-0009 §M9 Race Condition Handling](/ for details (the TTL portion is superseded by ADR-0041).

運用注記 / Operational Note:

WORKER_MAX_JOBS_BEFORE_RESTART=0(永続 Worker モード、ADR-0009 で公式サポート化)を選択した場合、DINOv2 ONNX セッションが長時間プロセス内に残存するため、data.delete の fd open 中 inflight 時間が延長する可能性がある(通常 1-2 分 → 最大 Worker 寿命まで)。GDPR Art.17 の「不当な遅延なく削除」要件を厳密に維持するには、デフォルト(=1)の使用を推奨。

When WORKER_MAX_JOBS_BEFORE_RESTART=0 (persistent-worker mode, officially supported via ADR-0009) is selected, the DINOv2 ONNX session persists in-process for extended periods, potentially extending data.delete fd-open inflight time (from typical 1-2 min up to the Worker lifetime). To strictly maintain GDPR Art.17's "without undue delay" requirement, the default (=1) is recommended.

セキュリティ措置 / Security Measures:

  • Path Traversal 防御 / Path Traversal defense: 三重チェック — (1) UUID v4/v7 正規表現で webPageId を厳格検証(RFC 4122 準拠、version 4/7、variant 8/9/a/b)、(2) path.resolve() で絶対パス化、(3) startsWith(phase5Dir + path.sep) で root 配下を確認。 / Triple defense — (1) strict UUID v4/v7 regex (RFC 4122 compliant), (2) path.resolve() to absolute, (3) startsWith root-containment check.
  • Symlink 正規化 / Symlink canonicalization: 初回 resolveScreenshotRoot() 呼出時に fs.realpath() で root を正規化・キャッシュ(symlink 経由の escape 防止)。 / First-call fs.realpath() canonicalization of root (defends against symlink escape).
  • サイズ上限 / Size cap: saveScreenshot() は sourceBuffer サイズをチェックし、上限超過時は保存前に拒否(DoS / ディスク枯渇対策)。 / saveScreenshot() checks source buffer size and rejects before write (DoS / disk exhaustion defense).
  • Batch size cap (retired): かつて cleanupExpired() は 1実行あたり最大 1000件まで(絶対上限 100,000件)に制限していた。cleanupExpired は PR-SS-B (ADR-0041) で撤去済。 / cleanupExpired() formerly capped at 1000 files per call (absolute limit 100,000); it was removed in PR-SS-B (ADR-0041).
  • PII truncation: webPageId はログ出力時に truncateId()(先頭8文字 + ...)で切り詰める。 / webPageId is truncated via truncateId() (first 8 chars + ...) in logs.

関連環境変数 / Related Env Vars:

変数 / Variableデフォルト / Default説明 / Description
REFTRIX_SCREENSHOT_ROOT/tmp/reftrix-screenshotsScreenshot 永続化ルートディレクトリ / Root persistence directory
SCREENSHOT_MAX_BYTES52428800 (50MB)saveScreenshot サイズ上限 / saveScreenshot size cap

9.5 Embedding キャッシュ(Layout Embedding ディスクキャッシュ) / Embedding Cache (Layout Embedding Disk Cache)

JP: LayoutEmbeddingService は section text の embedding (768 次元 e5-base ベクトル) を PersistentCache (persistent-cache.ts) でディスクにキャッシュし、同一 boilerplate (cookie banner 等) の再分析時に推論を省略する。本ディスクキャッシュは ADR-0040 (embedding cache temp-leak 根治) で 3 層 bounded retention 化された。

EN: LayoutEmbeddingService caches section-text embeddings (768-dimension e5-base vectors) to disk via PersistentCache (persistent-cache.ts), skipping inference on re-analysis of the same boilerplate (cookie banner, etc.). This disk cache was made 3-layer bounded-retention by ADR-0040 (embedding cache temp-leak root-cause fix).

対象データ / Data in Scope

項目 / Item内容 / Description
ルート / RootREFTRIX_EMBEDDING_CACHE_ROOT(デフォルト /tmp/reftrix-embedding-cacheos.tmpdir() allowlist 外は default reject、§ env vars / ADR-0040 Decision 2-3) / REFTRIX_EMBEDDING_CACHE_ROOT (default /tmp/reftrix-embedding-cache; rejected by default if outside the os.tmpdir() allowlist)
dbPath(worker) / Workerper-pid <root>/<workerType>-<pid>/(例 /tmp/reftrix-embedding-cache/page-12345/) / per-pid <root>/<workerType>-<pid>/
dbPath(MCP server)固定 <root>/mcp-server/(長命プロセス、再起動毎にリセットしない、ADR-0040 Decision 2) / fixed <root>/mcp-server/ (long-lived process, not reset on restart)
ファイル / Filescache.json(本体)+ cache.json.tmp.<pid>.<timestamp>(atomic-rename 用 temp、SSOT prefix CACHE_TEMP_PREFIX) / cache.json (body) + cache.json.tmp.<pid>.<timestamp> (atomic-rename temp, SSOT prefix CACHE_TEMP_PREFIX)
キャッシュ key / Cache keySHA-256(modelName + section text)(one-way hash) / SHA-256(modelName + section text) (one-way hash)
キャッシュ value / Value768 次元 e5-base embedding ベクトル / 768-dimension e5-base embedding vector

PII 評価 / PII Assessment

JP: ディスクキャッシュは raw text を一切含まない — SHA-256 cache key (one-way hash) と 768 次元 embedding ベクトルのみを格納する。元の section text は cache key から逆算不可能 (SHA-256 one-way)。embedding ベクトルの personal-data 該当性は 未確定 であり (EU GDPR Recital 26 / EDPB Opinion 28/2024 / 日本 個人情報保護法 第 2 条)、precautionary に bounded retention を適用する (LCC-L-04)。

EN: The disk cache contains no raw text — only SHA-256 cache keys (one-way hash) and 768-dimension embedding vectors. The original section text cannot be reverse-derived from a cache key (SHA-256 is one-way). The personal-data classification of an embedding vector is undetermined (EU GDPR Recital 26 / EDPB Opinion 28/2024 / Japan APPI Art. 2), so bounded retention is applied precautionarily (LCC-L-04).

保持方針(3 層 bounded retention) / Retention Policy (3-layer bounded)

ADR-0040 Decision 1 に従い、3 つの直交する層で bound する (専用の物理削除 cron は不要):

Per ADR-0040 Decision 1, the cache is bounded by three orthogonal layers (no dedicated physical-deletion cron is required):

層 / Layer機構 / Mechanism
(1) LRU エントリ数 bound / LRU entry boundmaxSize=10000(古い順に evict、論理 bound) / maxSize=10000 (oldest evicted, logical bound)
(2) TTL 論理失効 / TTL logical expiry24h(ttlMs = 24 * 60 * 60 * 1000、read-time に失効判定) / 24h (ttlMs = 24 * 60 * 60 * 1000, expiry evaluated at read time)
(3) 起動時 sweep 物理 bound / Startup sweep起動時に orphan temp + 死 pid の <root>/<workerType>-<pid>/ dir 全体(temp + 本体 cache.json + 空 dir)を物理回収。maxJobsBeforeRestart=1 の worker churn(高頻度再起動 → 高頻度 sweep)と組み合わせて cron なしで物理 footprint を bound(ADR-0040 Decision 1 + Decision 3) / On startup, physically reclaims orphan temps + dead-pid <root>/<workerType>-<pid>/ dirs (temp + body cache.json + empty dir); combined with maxJobsBeforeRestart=1 worker churn this bounds the physical footprint without a cron

MCP server 固定 dbPath の bound 根拠 (LCC-RE2-01): MCP server 本体の固定 <root>/mcp-server/ cache.json は worker のように per-pid rotation せず長命だが、(1) maxSize=10000 LRU + (2) 24h TTL 論理失効 + (3) saveToDisk-level の temp sweep で bound される (worker の per-pid dir 全体回収の対象外であることは、長命 server の cache 永続性を意図的に保つ設計意図、ADR-0040 Decision 2)。

Bound rationale for the MCP server fixed dbPath (LCC-RE2-01): The MCP server's fixed <root>/mcp-server/ cache.json does not per-pid-rotate like a worker and is long-lived, but it is bounded by (1) the maxSize=10000 LRU + (2) the 24h TTL logical expiry + (3) the saveToDisk-level temp sweep. Its exclusion from the worker per-pid dir reclamation is by design, deliberately preserving long-lived server cache persistence (ADR-0040 Decision 2).

data.delete(GDPR Art.17)scope 外注記 / data.delete (GDPR Art.17) Out-of-Scope Note

JP: GDPR Art.17 data.delete(webPageId) の CASCADE 削除はこのディスクキャッシュに 到達しない (cache は web_page_id に紐付かず SHA-256 key されている)。これは erasure の不完全性を意味しない。以下 3 点で erasure 完全性が担保される (LCC-RE-01):

  1. derived ephemeral artifact: cache は元データ (DB の section text / embedding 行) から derive された ephemeral な成果物であり、元データは data.delete の CASCADE で削除済み。
  2. 24h TTL + LRU 自然失効: cache エントリは 24h TTL + maxSize=10000 LRU で自然失効・evict される。
  3. one-way hash: cache key = SHA-256(modelName + section text) は one-way であり、削除後に残存しうる cache エントリから元の section text を逆算できない。

EN: The GDPR Art.17 data.delete(webPageId) CASCADE deletion does not reach this disk cache (the cache is SHA-256-keyed and not linked to web_page_id). This does not imply incomplete erasure; erasure completeness is guaranteed by three points (LCC-RE-01): (1) derived ephemeral artifact — the cache is an ephemeral artifact derived from the source data (DB section-text / embedding rows), and the source data is deleted by the data.delete CASCADE; (2) 24h TTL + LRU natural expiry — cache entries naturally expire / evict via the 24h TTL + maxSize=10000 LRU; (3) one-way hash — the cache key SHA-256(modelName + section text) is one-way, so the original section text cannot be reverse-derived from any cache entry that may persist after deletion.

GDPR Art.33(侵害通知)非該当 / GDPR Art.33 (Breach Notification) Non-applicability

JP: 本 incident (134 GB disk-full、ADR-0040 Context) は GDPR Art.33 (72h 監督機関通知) に 非該当

  • 第一論拠 (local availability incident): 本 incident は local availability incident (worker host のディスク満杯) であり、データの外部流出も無権限第三者への開示も発生していない。GDPR Art.4(12) の "personal data breach"(confidentiality / integrity / availability の breach のうち、本件は外部に対する availability breach ではなく local な disk-full)に該当する confidentiality breach ではない。
  • 補強論拠 (raw text 非含有): leak した temp/cache.json は SHA-256 key + 768 次元ベクトルのみで raw text を含まない。ただし EDPB Guidelines 01/2025 (anonymisation/pseudonymisation) が hash = pseudonymisation ≠ anonymisation を確定したため、「raw text 非含有」は単独の決定論拠ではなく 補強論拠 として位置づける (LCC-RE-02)。

EN: This incident (134 GB disk-full, ADR-0040 Context) is not subject to GDPR Art.33 (72h supervisory-authority notification). Primary basis (local availability incident): the incident is a local availability incident (disk-full on the worker host); no external data exfiltration and no disclosure to an unauthorized third party occurred. It is not a confidentiality breach under GDPR Art.4(12) "personal data breach" (of the confidentiality / integrity / availability breach categories, this is a local disk-full, not an externally-facing availability breach). Supporting basis (no raw text): the leaked temp/cache.json files hold only SHA-256 keys + 768-dimension vectors, no raw text. However, because EDPB Guidelines 01/2025 (anonymisation/pseudonymisation) established that hashing = pseudonymisation ≠ anonymisation, "no raw text" is positioned as a supporting, not a sole-determinative, basis (LCC-RE-02).

セキュリティ措置 / Security Measures

JP: 起動時 sweep は (1) EPERM 区別 liveness (isProcessAlive: EPERM → 生存扱い = 非削除 fail-safe)、(2) Phase 5 同型 3-stage whitelist (null byte reject / realpath / 自 dbPath 配下 + CACHE_TEMP_REGEX basename) で誤削除 / path traversal を構造防御する。per-worker dbPath 分離により、sweep が自 dbPath に閉じ live worker の in-flight temp を unlink 候補に含めない (ADR-0040 Decision 2-3)。

EN: The startup sweep structurally defends against mis-deletion / path traversal via (1) EPERM-aware liveness (isProcessAlive: EPERM → alive = not-deleted, fail-safe) and (2) a Phase 5-style 3-stage whitelist (null-byte reject / realpath / under-own-dbPath + CACHE_TEMP_REGEX basename). Per-worker dbPath separation confines the sweep to the process's own dbPath, so a live worker's in-flight temp is never an unlink candidate (ADR-0040 Decision 2-3).

GDPR 対応 / GDPR Compliance

条項 / Article内容 / Subject対応 / Implementation
Art. 5(1)(e)保管制限 / Storage limitation3 層 bounded retention(LRU maxSize=10000 + 24h TTL + 起動時 sweep)。screenshot 7d / crash-dump 90d との非対称(cron 不在)は per-worker dbPath + churn-driven sweep で解消。 / 3-layer bounded retention; the prior asymmetry vs screenshot 7d / crash-dump 90d (no cron) is resolved by per-worker dbPath + churn-driven sweep
Art. 5(1)(c)データ最小化 / Data minimisationraw text 非含有(SHA-256 key + ベクトルのみ)。 / no raw text (SHA-256 key + vector only)
Art. 17忘れられる権利 / Right to erasure元データ(DB 行)は data.delete CASCADE で削除。cache は derived ephemeral + 24h TTL + one-way hash で erasure 完全性を担保(上記 scope 外注記)。 / source data (DB rows) deleted by data.delete CASCADE; the cache guarantees erasure completeness via derived-ephemeral + 24h TTL + one-way hash (see out-of-scope note above)
Art. 33侵害通知 / Breach notification非該当(第一論拠 = local availability incident、補強論拠 = raw text 非含有、上記参照)。 / Not applicable (primary = local availability incident; supporting = no raw text, see above)

Cross-ref: ADR-0040 (embedding cache temp-leak 根治、Decision 1-5) / apps/mcp-server/src/services/persistent-cache.ts (PersistentCache 3 層 retention + saveToDisk try/finally + close skipFlushOnClose) / apps/mcp-server/src/services/cache-orphan-sweep.ts (sweepOrphanTempFiles / sweepDeadWorkerDirs / EPERM-aware isProcessAlive / 3-stage whitelist) / apps/mcp-server/src/services/cache-temp-const.ts (CACHE_TEMP_PREFIX / CACHE_TEMP_REGEX SSOT) / apps/mcp-server/src/services/layout-embedding.service.ts (resolveEmbeddingCacheRoot / resolvePerWorkerCacheConfig) / ` §"Embedding cache temp-leak fix — L tracked-issues" / EDPB Guidelines 01/2025 (hash = pseudonymisation ≠ anonymisation) / EDPB Opinion 28/2024 (embedding personal-data 該当性) / Plan V2 §2.8 + §5 / IO Plan Decision V2 (APPROVE) + IO Impl Decision V1 (APPROVE).


9.6 Screenshot Retention(ユーザー閲覧用、正典 / canonical) / Screenshot Retention (for user viewing)

JP: フルページ screenshot は WebUI v1 (分析結果ビューア) の恒久表示のため、永続ディレクトリに保存される。本節は screenshot 保持契約の 正典 (canonical) であり、§2 (嗜好プロファイル専用) は本節への cross-ref のみを置く (UB-8 / M-1)。詳細な設計判断・法的根拠は ADR-0041 を参照。

EN: Full-page screenshots are stored in a persistent directory for WebUI v1's (analysis-result viewer) permanent display. This section is the canonical screenshot retention contract; §2 (preference-only) holds only a cross-ref to here (UB-8 / M-1). For detailed design judgments / legal basis, see ADR-0041.

対象データ / Data in Scope

項目 / Item内容 / Description
ルート / RootREFTRIX_SCREENSHOT_ROOT(デフォルト ${XDG_DATA_HOME:-$HOME/.local/share}/reftrix/screenshots、ADR-0041 Decision 1。旧 default /tmp/reftrix-screenshots から変更) / env (default as shown, changed from the old /tmp/reftrix-screenshots)
サブディレクトリ / Subdir<root>/phase5/
ファイル / File<webPageId>.png(webPageId は UUID v4/v7) / <webPageId>.png (UUID v4/v7)
DB カラム / DB columnweb_pages.screenshot_storage_path (TEXT nullable)
内容 / Content公開 Web サイトのフルページ画像(第三者データ主体 = 写り込み人物の顔・氏名を含みうる、PII / 著作物) / full-page image of a public website (may contain third-party data subjects — captured persons' faces/names; PII / copyrighted works)

保持方針(ユーザー明示削除まで、TTL 撤廃) / Retention Policy (until user deletes, TTL removed)

JP: 保持期間 = 「ユーザーが明示削除 (data.delete) するまで」(「無期限 / indefinite」という表現は使わない)。従来の 7d TTL cron は撤廃 された(ADR-0041 Decision 3、PR-SS-A で挙動停止 / PR-SS-B で構造撤去)。保持目的 = 分析結果ビューアでの恒久表示(運用者自身の閲覧)。削除手段 = data.delete 同期削除のみ(GDPR Art.17)。自動的な期限切れ・定期削除は存在しない。

EN: Retention = "until the user explicitly deletes via data.delete" (never "indefinite"). The prior 7d TTL cron is removed (ADR-0041 Decision 3; behaviorally stopped in PR-SS-A / structurally removed in PR-SS-B). Purpose = permanent display in the analysis-result viewer (the operator's own viewing). Deletion path = data.delete synchronous deletion only (GDPR Art.17). There is no automatic expiry / scheduled deletion.

JP: 法的根拠の完全版は ADR-0041 §Legal Basis を正典とする (balancing test 段落含む)。要点:

  • GDPR Art.5(1)(e) (storage limitation) — trigger-event 明示型 (data.delete まで)。Art.13(2)(a) の criteria 型通知 (trigger event = data.delete) を data subject に通知 (M-4)。EDPB 2025 CEF Report は「無期限・過大・不整合な保持期間」を problematic とし、本契約はこれを回避。
  • GDPR Art.6(1)(f) (legitimate interest) — デザイン分析結果閲覧の正当利益。SS は第三者データ主体を含むため balancing test の明示段落が必須 (preference の本人データのみロジック流用は不十分)。根拠文書 = EDPB Guidelines 1/2024 STEP3 (V1.0 draft) + EDPB Opinion 28/2024 §Mitigating measures。緩和措置 = 写り込み人物マスキング検討 (将来) + data.delete による erasure facilitation。
  • APPI 第17/21条 — dogfood でも取得時点で発火しうるが、本人運用 + 利用目的特定で充足、第三者提供なしゆえリスク極小 (L)。OSS/Cloud で第三者閲覧・反復事業性により M 昇格 (PPC FAQ Q1-49)。

EN: The full legal basis is canonical in ADR-0041 §Legal Basis (incl. the balancing-test paragraph). Key points: Art.5(1)(e) (storage limitation) — trigger-event-explicit (until data.delete); the criteria-type Art.13(2)(a) notification (trigger event = data.delete) is given to the data subject (M-4); the EDPB 2025 CEF Report flags "indefinite, excessive or inconsistent retention periods" as problematic, which this contract avoids. Art.6(1)(f) (legitimate interest) — viewing design-analysis results; because screenshots contain third-party data subjects, an explicit balancing-test paragraph is mandatory (the first-party-only preference logic is insufficient); source documents = EDPB Guidelines 1/2024 STEP3 (V1.0 draft) + EDPB Opinion 28/2024 §Mitigating measures; mitigating measures = consideration of masking captured persons (future) + erasure facilitation via data.delete. APPI Art.17/21 — may fire at acquisition even in dogfood, but satisfied by first-party operation + purpose specification, minimal exposure (L) given no third-party provision; escalates to M at OSS/Cloud due to third-party viewing and repetitive business (PPC FAQ Q1-49).

削除手段と backstop (H-1) / Deletion path & backstop (H-1)

JP: data.delete(webPageId) (GDPR Art.17) は web_pages 行の CASCADE 削除に同期して screenshot ファイルを削除する。TTL 撤廃により「TTL cron が最終回収する」backstop が消滅するため、ADR-0041 Decision 5 (H-1 hardening) で GdprDeletionService は factory 未配線時でも自身の Prisma client から default ScreenshotPersistenceService を inline 構築して fallback し、削除成立が DI 配線に依存しないよう構造保証する。standing INV INV-SCREENSHOT-RETENTION-001 (gdpr-delete domain) が factory 未配線での削除成立を real-DB + real-FS で実証する。

EN: data.delete(webPageId) (GDPR Art.17) deletes the screenshot file synchronously with the CASCADE deletion of the web_pages row. Because TTL removal eliminates the "TTL cron reaps" backstop, ADR-0041 Decision 5 (H-1 hardening) makes GdprDeletionService fall back to an inline-constructed default ScreenshotPersistenceService (from its own Prisma client) even when the factory is unwired, structurally guaranteeing that deletion does not depend on DI wiring. The standing INV INV-SCREENSHOT-RETENTION-001 (gdpr-delete domain) proves unwired-factory deletion on real-DB + real-FS.

orphan PNG reconciliation 運用注記 (M-10) / Orphan-PNG reconciliation note (M-10)

JP: D-5 fallback 後も deleteScreenshot 自体が FS エラーで失敗した場合 (warn のみ・DB 行は削除済み)、第三者 SS の orphan PNG が「削除記録済みだが FS 残存」状態で残りうる。TTL backstop 消滅後の回収経路として 再実行可能な reconcile script 等を運用手段とする。EDPB 2025 CEF Report §4.2.6 Issue 6 の勧告 "Verify that erasure has been carried out and be able to demonstrate such erasure" に照らし、回収経路の明記と本透明性記述を必須とする。

EN: Even after the D-5 fallback, if deleteScreenshot itself fails with an FS error (warn only; the DB row is already deleted), a third-party-SS orphan PNG may remain "deletion-recorded but FS-surviving". As the recovery path after the TTL backstop is gone, a re-runnable reconcile script is the operational means. Per the EDPB 2025 CEF Report §4.2.6 Issue 6 recommendation "Verify that erasure has been carried out and be able to demonstrate such erasure", the recovery path and this transparency note are mandatory.

移行履歴 + 51 ページの非回復性 (L-18 transparency) / Migration history + 51 pages unrecoverable

JP: root 変更に伴い、既存 345 ファイル (web_pages 396 中 345 に screenshot_storage_path) は operator 実行の一回性 CLI (migrate-screenshots-to-persistent-root.ts、ADR-0041 Decision 6) で旧 /tmp → 新 root へ移行する。migration は confirm 実行ごと (件数>0) に screenshot_storage_migrated audit_logs を 1 行 emit (GDPR Art.30)。SS 消失済み 51 ページは復元不能 — 元ファイルは /tmp 消失済みであり、回復手段は当該 URL の page.analyze 再実行のみ (本 PR scope 外)。viewer は「screenshot なし」placeholder を表示 (webui 側要件)。

EN: With the root change, the existing 345 files (345 of 396 web_pages have screenshot_storage_path) are migrated from the old /tmp to the new root by an operator-run one-shot CLI (migrate-screenshots-to-persistent-root.ts, ADR-0041 Decision 6); the migration emits one screenshot_storage_migrated audit_logs row per non-zero confirm run (GDPR Art.30). The 51 pages with lost screenshots are unrecoverable — the source files are already gone from /tmp, and the only recovery is re-running page.analyze for those URLs (out of scope for this PR); the viewer shows a "no screenshot" placeholder (a webui-side requirement).

audit action 整理 (M-3、TTL retired) / Audit action update (M-3, TTL retired)

JP: 旧 screenshot_ttl_cleanup audit action は retired (emit 停止) されるが、過去に記録された歴史的 audit_logs 行は GDPR Art.30 記録として保持 される (emit 停止は Art.30 違反ではない — Art.30 は過去の処理活動の記録保持を要求し、将来の emit 停止を禁じない)。新 action screenshot_storage_migrated (migration CLI、365 日保持、truncateAuditTargetId PII minimisation、actor は audit-actions SSOT 定数) を登録する。

EN: The prior screenshot_ttl_cleanup audit action is retired (no longer emitted), but the historical audit_logs rows already recorded are retained as GDPR Art.30 records (ceasing emission is not an Art.30 violation — Art.30 requires retaining records of past processing activities and does not forbid stopping future emission). The new action screenshot_storage_migrated (migration CLI, 365-day retention, truncateAuditTargetId PII minimisation, actor = audit-actions SSOT constant) is registered.

配布形態別信号 (能動 fail-loud、M-11) / Per-distribution signals (active fail-loud, M-11)

JP: dogfood GREEN (本人運用、充足) / OSS YELLOW (弁護士確認 + 免責同梱を推奨) / Cloud RED (弁護士確認必須、Mobbin 等競合 ToS の DMCA + indemnification 構造精査 + EDPB scraping guidelines 確認を含む)。能動 fail-loud 機構: .ossfilter H-2 ルール近傍の警告コメント + release-readiness チェック項目に「screenshot を OSS 同梱する場合 ADR-0041 §配布形態別信号を参照」を追加 (将来の OSS 同梱判断時に能動発火)。詳細は ADR-0041 §Legal Basis 参照。

EN: dogfood GREEN (first-party, satisfied) / OSS YELLOW (attorney review + disclaimer bundle recommended) / Cloud RED (attorney review mandatory, incl. scrutiny of competitor ToS DMCA + indemnification structures and confirmation of the EDPB scraping guidelines). Active fail-loud mechanism: a warning comment near the .ossfilter H-2 rules + a release-readiness checklist item "if bundling screenshots into OSS, see ADR-0041 §per-distribution signals" (actively fires at a future OSS-bundling decision). See ADR-0041 §Legal Basis for detail.

GDPR 対応 / GDPR Compliance

条項 / Article内容 / Subject対応 / Implementation
Art.5(1)(e)保管制限 / Storage limitationtrigger-event 明示型 (data.delete まで、無期限回避)。criteria 型通知 (M-4)。 / trigger-event-explicit (until data.delete, avoiding indefinite); criteria-type notification (M-4)
Art.6(1)(f)正当利益 / Legitimate interestbalancing test 明示 (第三者データ主体、EDPB Guidelines 1/2024 + Opinion 28/2024)、緩和措置 (masking 検討 + erasure facilitation)。ADR-0041 §Legal Basis 正典。 / explicit balancing test, ADR-0041 canonical
Art.13/14透明性通知 / Transparency notice保持目的・trigger event (data.delete)・削除手段を通知 (M-4)。 / notify purpose / trigger event / deletion path (M-4)
Art.17忘れられる権利 / Right to erasuredata.delete 同期削除 + H-1 fallback (DI 配線非依存) + orphan reconcile 経路。 / data.delete sync deletion + H-1 fallback + orphan reconcile path
Art.30処理活動記録 / Recordsscreenshot_storage_migrated emit (365d)。screenshot_ttl_cleanup 歴史的行は記録として保持。 / screenshot_storage_migrated emit; historical screenshot_ttl_cleanup rows retained

WebUI v1 W2 rework — viewer 表示用途の residual transparency + ROPA (保持不変、ADR-0042 Amendment 3) / WebUI v1 W2 rework — residual transparency + ROPA for viewer display (retention unchanged)

JP: WebUI v1 W2 rework (ADR-0042 Amendment 3) は、既に §9.6 で永続化済の フルスクリーンショットを read-only viewer に表示する新 read surface (GET /internal/pages/:webPageId/screenshot + webui Option B same-origin proxy) を追加した。これは 新規の保持 tier / 取得 / 保存先を一切導入しない — 表示するのは §9.6 の保持契約下に既に存在する PNG であり、本節の保持期間・法的根拠・削除手段 (= data.delete まで) は 不変である。

  • residual transparency (GDPR Art.5(1)(a) / LCC-W2RW-V1-IMGPII-01 binding L + LCC-W2RW-V1-ROPA-02): フルページ screenshot は不可分に第三者データ主体 (写り込んだ人物の顔・avatar 等) を含みうる。serve される画像は 公開された第三者ページのレンダリングソースであり、抽出 PII フィールド (htmlSnippet / attributes / css_classes) という別 sink (markup-redaction SSOT INV-WEBUI-HIGHPII-NEVER-IN-RESPONSE-001 が対象) とは区別される。per-section image crop は ADR-0042 Amendment 3 §A3.3 (decision 019ecfe4、座標空間不整合) で descope され per-section image sink が消滅したため、残る suppression 手段は whole-page 抑止のみだが、これは CWE-697 over-broad で core requirement (フルスクリーンショット表示) を guts するため 採らない (LCC binding severity = L、whole-page getHighPiiSectionIds→404 gate は mandated されない)。hasScreenshot boolean が既に存在を開示済 + localhost single-user dogfood ゆえ exposure は極小。この透明性記述自体が GDPR Art.5(1)(a) fair-and-transparent processing 要件を満たす緩和措置である。
  • ROPA viewer-display purpose (no new ROPA entry): 本 viewer 表示は 既存 §9.6 screenshot-retention ROPA エントリに「viewer-display (localhost dogfood)」purpose を追記するものであり、新規 ROPA エントリは作らない (保持期間・法的根拠・削除経路は本 §9.6 が canonical のまま不変)。purpose = 分析結果ビューアでの恒久表示 (運用者自身の閲覧) に「永続済 screenshot の localhost viewer 表示」を追加。
  • data.delete leg unchanged (LCC-W2RW-V1-DELETE-04): data.delete は依然 serve される screenshot を削除する (ADR-0041 leg 不変)。getScreenshotPath は削除後 null → endpoint 404 (INV-WEBUI-SCREENSHOT-002 に subsume)。INV-SCREENSHOT-RETENTION-001 (gdpr-delete standing) は不変 — viewer 追加による削除契約への regression なし (Impl negative-confirm)。
  • 配布形態別信号 (不変): 本 viewer は §9.6 既述の配布形態別信号 (dogfood GREEN / OSS YELLOW / Cloud RED) を不変に継承する。webui は .ossfilter / rsync allowlist で OSS sync 非対象 (localhost 127.0.0.1 dogfood)。

EN: The WebUI v1 W2 rework (ADR-0042 Amendment 3) added a new read surface (GET /internal/pages/:webPageId/screenshot + the webui Option B same-origin proxy) that displays the full screenshot already persisted under §9.6 in a read-only viewer. It introduces no new retention tier / acquisition / storage location — it displays a PNG that already exists under the §9.6 retention contract, and this section's retention period / legal basis / deletion path (= until data.delete) are unchanged.

  • residual transparency (GDPR Art.5(1)(a) / LCC-W2RW-V1-IMGPII-01 binding L + LCC-W2RW-V1-ROPA-02): a full-page screenshot indivisibly can include third-party data subjects (captured persons' faces / avatars, etc.). The served image is a render of a public third-party page source, distinct from the extracted-PII-field sink (htmlSnippet / attributes / css_classes, the target of the markup-redaction SSOT INV-WEBUI-HIGHPII-NEVER-IN-RESPONSE-001). Because per-section image crops were descoped in ADR-0042 Amendment 3 §A3.3 (decision 019ecfe4, coordinate-space mismatch) and the per-section image sink vanished, the only remaining suppression means is whole-page suppression, which is CWE-697 over-broad and would gut the core requirement (full-screenshot display), so it is NOT adopted (LCC binding severity = L; no whole-page getHighPiiSectionIds→404 gate is mandated). With the hasScreenshot boolean already disclosing existence + localhost single-user dogfood, exposure is minimal. This transparency note itself is the mitigating measure satisfying the GDPR Art.5(1)(a) fair-and-transparent-processing requirement.
  • ROPA viewer-display purpose (no new ROPA entry): this viewer display appends a "viewer-display (localhost dogfood)" purpose to the EXISTING §9.6 screenshot-retention ROPA entry and creates no new ROPA entry (the retention period / legal basis / deletion path remain canonical in this §9.6, unchanged); the purpose "permanent display in the analysis-result viewer (the operator's own viewing)" gains "localhost viewer display of the already-persisted screenshot".
  • data.delete leg unchanged (LCC-W2RW-V1-DELETE-04): data.delete still removes the served screenshot (ADR-0041 leg unchanged); getScreenshotPath returns null → endpoint 404 post-deletion (subsumed by INV-WEBUI-SCREENSHOT-002); the INV-SCREENSHOT-RETENTION-001 (gdpr-delete standing) is unchanged — no regression to the deletion contract from adding the viewer (Impl negative-confirm).
  • Per-distribution signals (unchanged): this viewer inherits the §9.6 per-distribution signals unchanged (dogfood GREEN / OSS YELLOW / Cloud RED); webui is out of OSS sync scope via .ossfilter / rsync allowlist (localhost 127.0.0.1 dogfood).

Cross-ref: ADR-0041 (screenshot 永続保存 + TTL 撤廃 + Legal Basis 正典) / ADR-0009 §TTL (Bug 2 portion、partial superseded) / ADR-0010 §deletion-path matrix (TTL leg partial superseded) / ADR-0042 Amendment 3 (W2 rework full-screenshot serve + per-section crop descope + image-sink scope = screenshot-serve-only + delete-leg unchanged) / apps/mcp-server/src/services/screenshot-persistence.service.ts (root SSOT + getScreenshotPath + validateScreenshotPath) / apps/mcp-server/src/api/internal/page-detail.service.ts (getScreenshotStream resolver chain) / apps/mcp-server/src/services/gdpr-deletion.service.ts (H-1 fallback) / apps/mcp-server/scripts/migrate-screenshots-to-persistent-root.ts (migration CLI) / INV-SCREENSHOT-RETENTION-001 (gdpr-delete) / INV-WEBUI-SCREENSHOT-001..008 (webui-readonly negative-confirm domain) / EDPB Guidelines 1/2024 (V1.0 draft) + Opinion 28/2024 + 2025 CEF Report + PPC FAQ Q1-49 (retrieved 2026-06-13) / Plan v1 §6 (M-1〜M-6) + §10 (a)-(l) / IO Impl Decision APPROVE (019ebdd1-209a-7699-b49c-3402ea6ae0fe) / W2 rework Finding Registry V1 (§9 IO Impl Decision = CONDITIONAL documentation-only unblock; LCC-W2RW-V1-IMGPII-01 / ROPA-02 / DELETE-04 binding L) / crop-descopedecision 019ecfe4` (main agent, fact-based, verified)。


9.7 Crop Retention(section/part viewable crop、ADR-0044) / Crop Retention (section/part viewable crop)

JP: WebUI v1 W6 Issue A PR-3a (crop 永続化、[ADR-0044](../../ は、Phase 5 が embedding 生成時に in-memory で作る per-section / per-part の viewable PNG crop を 224-downscale 破棄前に永続化する新 visual-PII surface を導入した。crop の保持契約は §9.6 (screenshot retention) と完全 parity であり (ADR-0041 §Amendment Crop Retention Parity)、保持期間・法的根拠・削除経路を継承する。詳細な設計判断は [ADR-0044](../../ を参照。

EN: WebUI v1 W6 Issue A PR-3a (crop persistence, [ADR-0044](../../ introduced a new visual-PII surface that persists the per-section / per-part viewable PNG crops Phase 5 generates in-memory at embedding time, just before the 224-downscale discard. The crop retention contract is in full parity with §9.6 (screenshot retention) (ADR-0041 §Amendment Crop Retention Parity), inheriting its retention period / legal basis / deletion path. For detailed design judgments, see [ADR-0044](../../

対象データ / Data in Scope

項目 / Item内容 / Description
ルート / Root<root>/crops/(root = screenshot SSOT と同じ realpath 済 resolveScreenshotRoot から derive、REFTRIX_SCREENSHOT_ROOT 配下の sibling) / derived from the same realpath'd resolveScreenshotRoot as the screenshot SSOT (a sibling under REFTRIX_SCREENSHOT_ROOT)
サブディレクトリ / Subdir<root>/crops/<webPageId>/(per-page 決定論 dir、section/part id を dir level に持たない) / per-page deterministic dir, holds no section/part id at the dir level
ファイル / File<entityId>.png(entityId = section_pattern_id または component_part_id、UUID) / <entityId>.png (entityId = section_pattern_id or component_part_id, UUID)
DB カラム / DB columnsection_embeddings.crop_storage_path + component_part_embeddings.crop_storage_path (TEXT nullable、additive)
内容 / Content公開 Web サイトの section/part viewable PNG crop(第三者データ主体を含みうるが high-PII = avatar は構造的に非生成、Art.5(1)(c)) / section/part viewable PNG crop of a public website (may contain third-party data subjects, but high-PII = avatars are structurally non-generated, Art.5(1)(c))

保持方針(§9.6 parity、TTL cron 復活なし) / Retention Policy (§9.6 parity, no TTL cron revival)

JP: 保持期間 = 「ユーザーが明示削除 (data.delete) するまで」(§9.6 / ADR-0041 と完全 parity、「無期限」表現は使わない)。screenshot の TTL cron を crop に 再導入しない(ADR-0041 Decision 3 の TTL 構造撤去を crop でも維持)。保持目的 = 分析結果ビューアの section→part 視覚ドリル表示(運用者自身の閲覧)。削除手段 = data.delete 同期削除のみ(GDPR Art.17、crop dir cascade は W6 Issue A PR-3b で single+batch 両 path に配線完了、下記「削除経路 (cascade) — PR-3b 配線完了」参照)。自動的な期限切れ・定期削除は存在しない。

EN: Retention = "until the user explicitly deletes via data.delete" (full parity with §9.6 / ADR-0041, never "indefinite"). The screenshot TTL cron is NOT re-introduced for crops (ADR-0041 Decision 3's TTL-structure removal is maintained for crops too). Purpose = the section→part visual-drill display in the analysis-result viewer (the operator's own viewing). Deletion path = data.delete synchronous deletion only (GDPR Art.17; the crop-dir cascade is now wired in W6 Issue A PR-3b on both the single and batch paths, see "Deletion path (cascade) — wired in PR-3b" below). There is no automatic expiry / scheduled deletion.

削除経路 (cascade) — PR-3b 配線完了 / Deletion path (cascade) — wired in PR-3b

JP: W6 Issue A PR-3b (Finding Registry webui-v1-w6-issue-a-pr3b-finding-registry-v0.md F-03 / F-12、IO Plan Decision V0 = CONDITIONAL 019ef9b2-3c92) で crop dir GDPR cascade を data.delete の single + batch 両経路に配線完了した(PR-3a では未配線だった)。

  • single path (gdpr-deletion.service.ts deletePage:576): deleteScreenshotBestEffort の直後に deleteCropDirBestEffort(pageId) を配線。<root>/crops/<pageId>/ を一括 unlink。
  • batch path (deleteAllUserData:890): batch screenshot 削除ループの後に、要求された全 pageIdsdeletedPageIds ではなく)に対し deleteCropDirBestEffort(pageId) を配線。single-only 配線だと batch 削除ページの crop が永久 orphan = Art.17 違反になるため。
  • path-traversal 防御: cascade は buildSafeCropDir(webPageId)(内部 UUID_REGEX 検証 → screenshot SSOT 共有 buildSafePathWithinRoot core、弱い第2 resolver なし)が返す validated dir のみ fs.rm する。malformed id(../sibling / null-byte)は throw して fs.rm に到達しない。
  • best-effort + 冪等: fs.rm(force:true, recursive:true) は ENOENT 冪等ゆえ crop 未生成 / 既削除ページでも throw しない。失敗は logger.warn(truncateId + sanitizeErrorMessage、全環境で出力、isDevelopment() ガード禁止)のみで DB 削除を巻き戻さない(deleteScreenshotBestEffort parity)。
  • orphan reconcile = 冪等 re-run: data.delete 後に deleteCropDirBestEffort 自体が FS error で失敗した場合(warn のみ・DB 行は削除済)、第三者 visual-PII の orphan crop が「削除記録済だが FS 残存」状態で残りうる。回収経路 = data.delete の冪等 re-runfs.rm force:true recursive 冪等で残存 crop を回収)。専用 reconcile 運用 script の新規作成は将来 PR(現 dogfood/single-user 段階では冪等 re-run で adequate)。EDPB 2025 CEF Report on the right to erasure (Art.17, Feb 2026) — "controllers need to verify that erasure has been carried out and be able to demonstrate such erasure"(recurring main challenges の 1 つ)整合(source edpb.europa.eu/system/files/2026-02/edpb_cef-report_2025_right-to-erasure_en.pdf、retrieved 2026-06-24)。
  • standing 検証: INV-CROP-RETENTION-001(gdpr-delete standing、5+1 assertions: single + batch factory-unwired unlink / orphan reconcile 冪等 re-run / 3s SLA 絶対 3000ms / 0-file negative / negative traversal throw)が CI 継続検証。

EN: W6 Issue A PR-3b (Finding Registry webui-v1-w6-issue-a-pr3b-finding-registry-v0.md F-03 / F-12, IO Plan Decision V0 = CONDITIONAL 019ef9b2-3c92) wired the crop-dir GDPR cascade on both the single and batch paths of data.delete (it was unwired in PR-3a). Single path (gdpr-deletion.service.ts deletePage:576): deleteCropDirBestEffort(pageId) immediately after deleteScreenshotBestEffort, bulk-unlinking <root>/crops/<pageId>/. Batch path (deleteAllUserData:890): after the batch screenshot-deletion loop, deleteCropDirBestEffort(pageId) over the requested pageIds (not deletedPageIds), because single-only wiring would leave batch-deleted pages' crops as permanent orphans (Art.17 violation). Path-traversal defense: the cascade fs.rms only the validated dir returned by buildSafeCropDir(webPageId) (internal UUID_REGEX → the shared buildSafePathWithinRoot core, no weak 2nd resolver); a malformed id (../sibling / null-byte) throws before fs.rm. Best-effort + idempotent: fs.rm(force:true, recursive:true) is ENOENT-idempotent (no throw for un-generated / already-deleted pages); failure is a logger.warn-only (truncateId + sanitizeErrorMessage, all environments, no isDevelopment() guard) with no DB rollback (deleteScreenshotBestEffort parity). Orphan reconcile = idempotent re-run: if deleteCropDirBestEffort itself fails with an FS error after data.delete (warn only, DB row already deleted), an orphan crop of third-party visual-PII may remain "deletion-recorded but FS-surviving"; the recovery path is an idempotent re-run of data.delete (the recursive fs.rm force:true reaps residual crops); a dedicated operational reconcile script is a future PR (idempotent re-run is adequate at the current dogfood/single-user stage). This satisfies the EDPB 2025 CEF Report on the right to erasure (Art.17, Feb 2026) — "controllers need to verify that erasure has been carried out and be able to demonstrate such erasure" (one of the recurring main challenges; source edpb.europa.eu/system/files/2026-02/edpb_cef-report_2025_right-to-erasure_en.pdf, retrieved 2026-06-24). Standing verification: INV-CROP-RETENTION-001 (gdpr-delete standing, 5+1 assertions: single + batch factory-unwired unlink / orphan reconcile idempotent re-run / absolute-3000ms 3s SLA / 0-file negative / negative-traversal throw) verifies it continuously in CI.

JP: 法的根拠の完全版は [ADR-0044](../../ §Legal Basis(§9.6 / ADR-0041 §Legal Basis を継承)を正典とする。要点:

  • GDPR Art.5(1)(e) (storage limitation) — trigger-event 明示型 (data.delete まで、§9.6 parity)。
  • GDPR Art.6(1)(f) (legitimate interest) — section/part 視覚プレビュー閲覧の正当利益。crop は第三者データ主体を含みうるため balancing test を §9.6 / ADR-0041 から継承。STEP 2 緩和措置 = high-PII (avatar) crop の構造的非生成 (fail-closed write-sink、本 PR で既に実装) + data.delete erasure facilitation + 将来のマスキング検討。
  • Art.9 over-claim 回避 (LCC-PR3A-02): 保存 crop PNG を special-category biometric と過剰主張しない — EDPB Guidelines 05/2022 により顔画像が Art.9 biometric に該当するのは一意識別目的の技術処理時のみであり、PNG 保存 + DINOv2 汎用 visual embedding は識別処理ではない。Art.6(1)(f) を法的根拠とする。
  • APPI 第17/21条 — §9.6 parity (dogfood L、OSS/Cloud で M 昇格)。

EN: The full legal basis is canonical in [ADR-0044](../../ §Legal Basis (inheriting §9.6 / ADR-0041 §Legal Basis). Key points: Art.5(1)(e) (storage limitation) — trigger-event-explicit (until data.delete, §9.6 parity). Art.6(1)(f) (legitimate interest) — viewing section/part visual previews; because crops can contain third-party data subjects, the balancing test is inherited from §9.6 / ADR-0041, and STEP 2's mitigating measure = the structural non-generation of high-PII (avatar) crops (the fail-closed write-sink, already implemented in this PR) + erasure facilitation via data.delete + future masking consideration. Art.9 over-claim avoidance (LCC-PR3A-02): we do NOT over-claim that the persisted crop PNG is special-category biometric — per EDPB Guidelines 05/2022 a facial image qualifies as Art.9 biometric only under technical processing for unique identification, and PNG storage + a DINOv2 generic visual embedding is not identification processing; it is grounded in Art.6(1)(f). APPI Art.17/21 — §9.6 parity (dogfood L, escalating to M at OSS/Cloud).

第三者データ主体 + high-PII fail-closed (LCC-PR3A-03 / L-1) / Third-party data subjects + high-PII fail-closed

JP: crop は 公開された第三者ページのレンダリングソースであり、第三者データ主体 (写り込み人物の顔・avatar) を含みうる。ただし screenshot (W2 §9.6、不可分ゆえ whole-page 抑止しかなく un-redactable residual L) とは異なり、crop は per-entity (section/part 単位) で生成されるため、high-PII entity の crop を構造的に非生成 (fail-closed) にできる — high-PII = avatar (Issue A 223 件、全 part_type=avatar) の crop は一切ディスクに書かれない (生成後 unlink ではない、ADR-0044 §Decision 2)。section sink の gate = queryHighPiiPendingSectionPatternIds predicate (pii_risk_level='high' value-equal literal、PR-C4 lineage)、part sink の gate = piiRiskLevel:{not:"high"} query。これは Art.5(1)(c) data-minimisation の積極的実装である。crop の image-sink redaction scope は ADR-0042 Amendment 11 §A11.2 で W2 の whole-page un-redactable residual L とは scope が異なる (per-entity fail-closed gate) と明記。

EN: A crop is a render of a public third-party page source and can contain third-party data subjects (captured persons' faces / avatars). However, unlike a screenshot (W2 §9.6, indivisible — so only whole-page suppression exists, an un-redactable residual L), a crop is generated per-entity (per section/part), so the crop of a high-PII entity can be structurally non-generated (fail-closed) — the crop of a high-PII = avatar (Issue A 223, all part_type=avatar) is never written to disk (not generate-then-unlink, ADR-0044 §Decision 2). The section sink's gate = the queryHighPiiPendingSectionPatternIds predicate (a pii_risk_level='high' value-equal literal, PR-C4 lineage); the part sink's gate = the piiRiskLevel:{not:"high"} query. This is an active implementation of Art.5(1)(c) data-minimisation. ADR-0042 Amendment 11 §A11.2 records that the crop's image-sink redaction scope differs from the W2 whole-page un-redactable residual L (a per-entity fail-closed gate).

ROPA — design-analysis crop 生成 (LCC L-4) / ROPA — design-analysis crop generation

JP: 処理活動記録 (ROPA、GDPR Art.30) に design-analysis crop 生成の 1 行を追加:

項目 / Item内容 / Description
処理活動 / Processing activitydesign-analysis crop generation — section/part viewable PNG crop の生成・永続化 (WebUI viewer 表示用) / generation + persistence of section/part viewable PNG crops
purpose / 目的分析結果ビューアの section→part 視覚ドリル表示 (運用者自身の閲覧) / section→part visual-drill display in the analysis-result viewer (operator's own viewing)
法的根拠 / Legal basisGDPR Art.6(1)(f) (legitimate interest、balancing test = ADR-0044 §Legal Basis) / Art.6(1)(f)
データ主体 / Data subjects第三者データ主体 (写り込み人物)。high-PII = avatar は構造的に非生成 (fail-closed) / third-party data subjects; high-PII = avatars structurally non-generated
保持期間 / Retentiondata.delete まで (trigger-event 明示型、Art.5(1)(e)、§9.6 parity) / until data.delete (trigger-event-explicit)
削除経路 / Deletion pathdata.delete 同期削除 (Art.17、crop dir cascade は PR-3b で single+batch 両 path 配線完了) / data.delete synchronous deletion (crop-dir cascade wired in PR-3b on both single + batch paths)
緩和措置 / Mitigating measureshigh-PII crop 非生成 (Art.5(1)(c)) + erasure facilitation + 将来マスキング検討 / high-PII non-generation + erasure facilitation + future masking

EN: A single ROPA (GDPR Art.30) row is added for design-analysis crop generation (see the table above). The processing activity is the generation + persistence of section/part viewable PNG crops for the WebUI viewer display; legal basis Art.6(1)(f) (the balancing test in ADR-0044 §Legal Basis); data subjects are third-party data subjects with high-PII (avatars) structurally non-generated; retention until data.delete (Art.5(1)(e), §9.6 parity); deletion via data.delete synchronous deletion (the crop-dir cascade is wired in PR-3b on both the single and batch paths); mitigating measures = high-PII non-generation (Art.5(1)(c)) + erasure facilitation + future masking.

配布形態別信号 + storage growth (§9.6 / ADR-0041 parity) / Per-distribution signals + storage growth

JP: crop は §9.6 の配布形態別信号 (dogfood GREEN / OSS YELLOW / Cloud RED) を不変に継承する。crop root も repo 外 + .ossfilter 二重防御により第三者画像の OSS 混入が構造的に不可能 (H-2 parity)。storage は TTL 撤廃により単調増加するが、crop は per-entity PNG で screenshot より小さく、概算 storage 増分 = 数百MB〜1GB (実機規模依存、PR-4 実機 DoD で実測)。dogfood 単一運用では accepted-risk (ADR-0041 §item (k) と同 class)。OSS/Cloud 配布時は総容量 quota / df admission guard を別 PR で検討 (ADR-0041 §Future Conditions parity)。PR-3a では CROP_PERSISTENCE_ENABLED default OFF ゆえ crop file が一切生成されなかったが、PR-3b W3 で default OFF→ON に flip 済(W1-W4 cascade GREEN を gate に flip、operative resolver isCropPersistenceEnabled() = !== "false"、additive opt-out)。よって flip 後の新規 page.analyze から crop file が生成され storage 単調増加が始まるCROP_PERSISTENCE_ENABLED="false" で operator opt-out 可)。flip 後も既存ページの retroactive crop backfill は PR-4(新規 page.analyze のみ crop を付ける)。

EN: Crops inherit unchanged the §9.6 per-distribution signals (dogfood GREEN / OSS YELLOW / Cloud RED); the crop root is also outside the repo + .ossfilter defense-in-depth, making third-party-image OSS leakage structurally impossible (H-2 parity). Storage grows monotonically with TTL removal, but crops are per-entity PNGs smaller than a screenshot, with a rough storage increment of ~hundreds of MB–1 GB (real-machine-scale-dependent, measured at the PR-4 DoD); dogfood single-user is an accepted-risk (same class as ADR-0041 §item (k)); OSS/Cloud will consider a total-capacity quota / df admission guard in a future PR (ADR-0041 §Future Conditions parity). PR-3a generated no crop file (CROP_PERSISTENCE_ENABLED default OFF), but PR-3b W3 flipped the default OFF→ON (gated on W1-W4 cascade GREEN; operative resolver isCropPersistenceEnabled() = !== "false", additive opt-out), so crop files are now generated from new page.analyze runs post-flip and storage growth begins (operators can opt out with CROP_PERSISTENCE_ENABLED="false"). Even post-flip, retroactive crop backfill of existing pages is PR-4 (only new page.analyze runs attach crops).

Cross-ref: [ADR-0044](../../ (Crop Persistence, root design) / [ADR-0041](../../ §Amendment Crop Retention Parity + §9.6 screenshot parity / [ADR-0042](../../ Amendment 11 (crop image-PII redaction scope = per-entity fail-closed gate, distinct from W2 whole-page residual) / apps/mcp-server/src/services/part/crop-persistence.helper.ts (buildSafeCropPath / saveCropFromBuffer / isCropPersistenceEnabled / resolveCropRoot) / apps/mcp-server/src/workers/phases/phase-5-embedding.ts (persistSectionCrop / persistPartCrop / queryHighPiiPendingSectionPatternIds:2488) / apps/mcp-server/src/services/screenshot-persistence.service.ts (validatePathWithinRoot / buildSafePathWithinRoot shared core) / packages/database/prisma/migrations/20260624000000_add_crop_storage_path/migration.sql / INV-CROP-PII-EXCLUDED-001 (gdpr-delete + large-page) / INV-CROP-PATH-TRAVERSAL-001 / INV-CROP-COVERAGE-PARITY-001 (large-page) / INV-PART-SKIP-REASON-NO-STALE-001 (large-page) / INV-CROP-RETENTION-001 (gdpr-delete, PR-3b) / EDPB Guidelines 1/2024 + Opinion 28/2024 + Guidelines 05/2022 (Art.9 FRT) + 2025 CEF Report §4.2.6 + PPC FAQ Q1-49 (retrieved 2026-06-13, <24h current in LCC impl audit 019ef619-2f6c) / IO Plan Decision V0 = CONDITIONAL 019ef5da-0b03 / IO Impl Decision V1 = APPROVE 019ef624-c89d / Finding Registry ` (LCC-PR3A-01/02/03/04 / LCC-IMPL-PR3A-L-01)。


10. クロールデータ保持(HTML/CSS) / Crawl Data Retention (HTML/CSS)

対象データ / Data in Scope

テーブル / Tableカラム / Columns内容 / Description
web_pageshtml, screenshotクロール時に取得したHTML(DOMPurifyサニタイズ済み)、スクリーンショット画像 / Crawled HTML (DOMPurify-sanitized), screenshot images
section_patternshtmlSnippet, cssSnippet, externalCssContent, externalCssMeta, layout_info jsonbセクション単位のHTMLスニペット、ページレベルCSS(インライン+styleタグ+外部CSS実内容)、外部CSSメタデータ、layout_info jsonb(type / confidence / position / sectionSelector) / Per-section HTML snippets, page-level CSS, external CSS metadata, layout_info jsonb (type / confidence / position / sectionSelector)

保持方針 / Retention Policy

クロールデータは再分析時に clean-slate パターン(deleteMany + create)で上書きされます。手動削除はWebPageレコードの削除により CASCADE で自動実行されます。自動的な期限切れはありません。

Crawl data is overwritten on re-analysis using the clean-slate pattern (deleteMany + create). Manual deletion is performed automatically via CASCADE when a WebPage record is deleted. There is no automatic expiration.

セキュリティ措置 / Security Measures

  • HTMLサニタイズ / HTML Sanitization: すべてのクロール済みHTMLはDOMPurify 3.3.xでサニタイズ済み(<script>, javascript: URL, イベントハンドラ除去) / All crawled HTML is sanitized with DOMPurify 3.3.x (removes <script>, javascript: URLs, event handlers)
  • CSSデータ / CSS Data: ページレベルCSS(cssSnippet, externalCssContent)はセクション単位に配布してsection_patternsに保存。デザイン分析(レイアウト検索、コード生成)用途で保持 / Page-level CSS is distributed to sections and stored in section_patterns. Retained for design analysis (layout search, code generation)
  • SSRF対策 / SSRF Prevention: 外部CSSフェッチ時はSSRFバリデーション適用済み / SSRF validation applied during external CSS fetching

layout_info.sectionSelector(W6 Issue A PR-2 / F-L-09、GDPR Art.4(1) / Art.30)

JP: section_patterns.layout_info(jsonb)に W6 Issue A PR-2 で追加した additive sibling key sectionSelector。値は安定 DOM selector(tag#id / tag.class:nth-of-type(k) / tag の段階的劣化形)で、bbox-resolve / section-fallback の経路が live container を再 query して DOM-ancestry containment を判定するために persist される(layout_info.position の隣に並ぶ既存 field と同列)。

  • GDPR scope — personal data 非該当 / NOT personal data (Art.4(1)): sectionSelectorCSS 構造 path(tag 名 / id / class / nth-of-type 序数)のみを格納する 構造 DOM メタデータ であり、識別された/識別可能な自然人に relating する情報を含まない(GDPR Art.4(1) の personal data 定義に該当しない)。htmlSnippet / attributes / css_classes のような抽出 PII フィールド sink(markup-redaction SSOT INV-WEBUI-HIGHPII-NEVER-IN-RESPONSE-001 の対象)とは別物。LCC 計画監査(PASS)が GDPR scope 境界を確認済(PII sink ではない)。
  • 保持・削除(CASCADE / Art.17 subsumed)/ Retention & deletion: 独立した保持期間を持たない。親 section_patternsweb_pages 行と共に CASCADE 削除され、GDPR Art.17 data.delete(行削除)に subsumed(追加 erasure ステップ不要)。再分析時は §9 / §10 の clean-slate パターン(deleteMany + create)で上書きされる。新 retention tier を一切導入しない
  • storage / migration: jsonb の additive sibling key ゆえ migration 不要。schema-enum-sync 非該当(Prisma JSON、enum ではない)。既存 reader は unknown key を ignore。
  • internal-only(F-L-11 forward): PR-2 時点では user-facing response に surface しない(internal-only)。PR-3/PR-4 で user-facing response へ露出する場合は既存 high-PII redaction class を適用する(§"§9.6.1 / W2 rework" の image-PII discipline と整合、GDPR Art.4(1) / Art.5(1)(c))— forward 制約として下記 §"Forward note" に記載。

EN: sectionSelector is an additive sibling key added to section_patterns.layout_info (jsonb) in W6 Issue A PR-2. Its value is a stable DOM selector (the graceful-degradation forms tag#id / tag.class:nth-of-type(k) / tag), persisted so the bbox-resolve / section-fallback paths can re-query the live container to decide DOM-ancestry containment (it sits alongside the existing layout_info.position field).

  • GDPR scope — NOT personal data (Art.4(1)): sectionSelector stores only a CSS structural path (tag name / id / class / nth-of-type ordinal) — i.e. structural DOM metadata — and contains no information relating to an identified or identifiable natural person (it does not meet the GDPR Art.4(1) personal-data definition). It is distinct from the extracted-PII-field sinks (htmlSnippet / attributes / css_classes, the target of the markup-redaction SSOT INV-WEBUI-HIGHPII-NEVER-IN-RESPONSE-001). The LCC plan audit (PASS) confirmed this GDPR scope boundary (it is not a PII sink).
  • Retention & deletion (CASCADE / Art.17 subsumed): it has no independent retention horizon. It is CASCADE-deleted together with the parent section_patternsweb_pages row and is subsumed by GDPR Art.17 data.delete (row deletion; no additional erasure step required). On re-analysis it is overwritten via the §9 / §10 clean-slate pattern (deleteMany + create). It introduces no new retention tier.
  • storage / migration: as a jsonb additive sibling key it requires no migration; it is out of schema-enum-sync scope (Prisma JSON, not an enum); existing readers ignore the unknown key.
  • internal-only (F-L-11 forward): as of PR-2 it is NOT surfaced to user-facing responses (internal-only). If a future PR-3/PR-4 exposes it to a user-facing response, the existing high-PII redaction class must be applied (consistent with the §9.6.1 / W2 rework image-PII discipline, GDPR Art.4(1) / Art.5(1)(c)) — recorded as a forward constraint in the Forward note below.

Forward note (F-L-11 / DI-3, PR-3/PR-4): PR-3/PR-4 が sectionSelector を user-facing response(例: section/part inspect response、WebUI internal read API surface)へ露出する設計を採る場合、その plan 立案時に 既存 high-PII section redaction class を sectionSelector 経路にも適用することを明記すること(GDPR Art.4(1) personal-data 定義 / Art.5(1)(c) data-minimisation)。PR-2 では surface しない(internal-only、bbox-resolve / section-fallback の内部消費のみ)ゆえ forward 制約として残す。/ If PR-3/PR-4 designs a surface that exposes sectionSelector to a user-facing response (e.g. a section/part inspect response, a WebUI internal read API surface), that plan MUST state that the existing high-PII section redaction class is applied to the sectionSelector path too (GDPR Art.4(1) / Art.5(1)(c)). PR-2 does not surface it (internal-only, consumed solely by the bbox-resolve / section-fallback paths), so this is carried as a forward constraint.

Cross-ref: W6 Issue A PR-2 Finding Registry F-L-09 / F-L-11 ( / plan-v1 §11.3 ( / LCC plan-audit PASS anchor 019ef0a5-759c (impl-phase LCC PASS) / apps/mcp-server/src/services/worker-db-save.service.ts (saveSectionPatterns layoutInfo.sectionSelector persist seam) / apps/mcp-server/src/services/part/part-bbox-playwright.service.ts (bbox-resolve consumer) / INV-WEBUI-HIGHPII-NEVER-IN-RESPONSE-001 (markup-redaction SSOT, distinct sink) / §"§9.6.1 / W2 rework" (image-PII discipline)。


11. 監査ログデータ保持 / Audit Log Data Retention

対象データ / Data in Scope

テーブル / Tableカラム / Columns内容 / Description
audit_logsid, timestamp, action, actor, target_type, target_id, details, ip_address, resultGDPR Art.30 処理活動記録。すべてのデータ操作(削除、エクスポート、分析等)の監査証跡。 / GDPR Art.30 records of processing activities. Audit trail for all data operations (deletion, export, analysis, etc.).

PII評価 / PII Assessment

  • PII リスク: 低 / Low
  • target_idtruncateId() 適用済み(先頭8文字 + ...) / target_id has truncateId() applied (first 8 chars + ...)
  • ip_address はオプショナル(記録される場合は IPv4/IPv6 形式) / ip_address is optional (IPv4/IPv6 format when recorded)
  • details JSON はサニタイズ済み(シークレット情報不含) / details JSON is sanitized (no secrets)

保持方針 / Retention Policy

  • 保持期間 / Retention Period: 365日(1年) / 365 days (1 year)
  • 設計 / Design: Append-only(通常の UPDATE/DELETE 不可、cleanup のみ例外) / Append-only (no regular UPDATE/DELETE, cleanup only exception)
  • 自動期限切れ / Auto-expiry: 365日超過レコードの定期クリーンアップ推奨 / Periodic cleanup of records older than 365 days recommended
  • 手動削除 / Manual deletion: 監査ログは法的証跡のため、通常の削除要求対象外 / Audit logs are exempt from regular deletion requests as legal evidence
条項 / Article内容 / Subject対応 / Implementation
Art. 30処理活動の記録 / Records of processing activitiesすべてのデータ処理操作を監査ログとして記録。 / All data processing operations recorded as audit logs.
Art. 5(1)(e)保存制限の原則 / Storage limitation365日保持後にクリーンアップ推奨。法的義務がある期間は保持。 / Cleanup recommended after 365 days. Retained while legal obligations exist.

Worker lifecycle audit actions (v0.4.0 PR-D-8) / Worker ライフサイクル監査アクション

PR-D-8 で WorkerSupervisor の scope が EmbeddingBackfillWorker へ拡張されたことに伴い、Worker ライフサイクルの完全な監査証跡を確立。GDPR Art.30 (処理活動の記録義務) を 100% satisfied とし、APPI 第23条 (安全管理措置) の技術的安全管理措置 scope を per-type Worker 監視に拡張。すべての action は actor="system:worker-supervisor", 365日保持, targetIdtruncateId() で PII-truncate (PII-safe), details JSON は raw payload 不含 (CWE-209 + SEC-V11-01 PII minimisation 保全)。

PR-D-8 expanded WorkerSupervisor scope to EmbeddingBackfillWorker, establishing a complete Worker-lifecycle audit trail. GDPR Art.30 (processing-records obligation) is 100% satisfied, and APPI Article 23 (technical safeguarding measures) is extended to per-type Worker supervision. All actions use actor="system:worker-supervisor", 365-day retention, targetId truncated via truncateId() (PII-safe), and details JSON contains no raw payload (CWE-209 + SEC-V11-01 PII minimisation preserved).

actionactortargetTyperetentionresultdetails (PII-safe)
worker_supervisor_restartsystem:worker-supervisorworker365日 / 365 dayssuccess / failureworkerType, restartReason, jobsProcessed, exitCode, pid, respawnCount
worker_type_spoofing_detectedsystem:worker-supervisorworker365日 / 365 daysdeniedpid, expectedWorkerType, receivedWorkerType
worker_lock_ttl_fallbacksystem:worker-supervisorworker365日 / 365 dayssuccessworkerType, retryCount, fallbackReason
worker_ipc_spoofing_detectedsystem:worker-supervisorworker365日 / 365 daysdeniedpid, reason ('unknown-workerType' | 'schema-invalid')

PII-safe 設計詳細 / PII-safe design details:

Backfill auto-start audit actions (backfill-worker-autostart, ADR-0011 Amendment 7) / Backfill 自動起動監査アクション

backfill-worker-autostart (deferred-spawn closure、ADR-0011 Amendment 7 §A7.4/§A7.6/§A7.7) で、secondary embedding-backfill worker の deferred-spawn bounded retry に 2 個の新 audit_logs.action を追加。large-page overflow embeddings が永久 queued/in_progress に滞留し terminal 状態 (completed/failed/skipped_*) に到達しない構造的 stall (MEMORY.md #162) を fail-loud に可観測化する。両 action は actor="system:worker-supervisor" (SSOT 定数 AUDIT_ACTOR_WORKER_SUPERVISOR)、365日保持、details は PII-free numeric/enum のみ (raw error message / URL / user identifier 不含、CWE-209 + GDPR Art.5(1)(c) data-minimisation 維持)、targetIdtruncateAuditTargetId() 適用済 (CWE-209)。SSOT action 定数は apps/mcp-server/src/audit/audit-actions.tsINV-AUDIT-EMIT-SSOT-IMPORT-001 AST sweep で production bare literal 0 を保証。

backfill-worker-autostart (deferred-spawn closure, ADR-0011 Amendment 7 §A7.4/§A7.6/§A7.7) adds 2 new audit_logs.action entries to the secondary embedding-backfill worker's deferred-spawn bounded retry, making fail-loud-observable the structural stall (MEMORY.md #162) where large-page overflow embeddings stay perpetually queued/in_progress and never reach a terminal state (completed/failed/skipped_*). Both actions use actor="system:worker-supervisor" (SSOT constant AUDIT_ACTOR_WORKER_SUPERVISOR), 365-day retention, details of PII-free numeric/enum only (no raw error messages, URLs, or user identifiers; CWE-209 + GDPR Art.5(1)(c) data-minimisation preserved), and targetId truncated via truncateAuditTargetId() (CWE-209). The SSOT action constants live in apps/mcp-server/src/audit/audit-actions.ts; the INV-AUDIT-EMIT-SSOT-IMPORT-001 AST sweep guarantees zero production bare literals.

actionactortargetTyperetentionresultdetails (PII-safe)
backfill_secondary_spawn_timeoutsystem:worker-supervisorweb_page365日 / 365 daysfailureattemptCount, elapsedMs, finalProbeStatus, terminalizedCount
vision_probe_unavailablesystem:worker-supervisorworker365日 / 365 daysfailureattemptCount, probeFailedStreak

設計詳細 / Design details:

  • backfill_secondary_spawn_timeout (§A7.4/§A7.7): deferred-spawn bounded retry が SECONDARY_SPAWN_RETRY_TIMEOUT_MS (= VISION_UNLOAD_FINAL_TIMEOUT_MS 10min) に達し worker が依然不在のとき emit する fail-loud marker。worker-spawn-level retry は webPageId を持たない (ADR-0011 §A7.4 SEC-REAUDIT-01) ため、emit 後に web_pagesfallback-on-absence scanembeddingBackfillStartedAt < now - 10min の stranded overflow row を terminal へ CAS-guard updateManyfrom-status 2分岐 (ADR-0007 Amendment 2 §A2.2.1 / ADR-0011 §A7.4 / Plan v3 §V2.1 ruling (a)-narrowed): terminal write は from-status で 2 分岐し、recovery handler の挙動差を反映する — (1) queued-origin (worker-absence で stranded した、検証済み rescue 可能 population) → failed_with_known_reason + reason supervisor_restart_orphan (recovery-IN; 当該 handler は無条件 re-enqueue で completed 経路に乗せる)、(2) in_progress-origin (真の vision-unload timeout で stranded) → bare failed + reason vision_unload_timeout を温存 (recovery-OUT; SEC-REAUDIT-02 契約不変)。両分岐は from-status で相互排他 (1 行は queued XOR in_progress) なので 2 つの updateMany が同一行に触れることはなく、observability 順序は固定 (queued → in_progress)。terminalizedCount はその scan が terminal 化した行数の合計 (queued + in_progress)。targetId は scan が複数行を terminal 化するため null (truncateAuditTargetId(null))。finalProbeStatus"vision_residual" | "probe_failed" の enum。本 scan は actor system:worker-supervisor集約 action であり、Section C rescue の per-row backfill_rescue_queued_gave_up (actor system:backfill-reconciliation-cron、§11.9.6.quater 参照) とは reason supervisor_restart_orphan を共有しつつ action / actor が異なる別 RoPA エントリである。
  • vision_probe_unavailable (§A7.6): Ollama /api/ps probe が 3-strike (SECONDARY_SPAWN_RETRY_PROBE_FAILED_STRIKE_LIMIT = 3) 連続失敗に達し retry が block (停止) されたとき emit。vision_probe_failed (単発失敗) と異なり、probe が persistently undecidable なため retry loop を terminal stop し unbounded retry を防止 (CWE-770)。Amendment 2 §A2.4:550 の fail-closed → degrade-but-warn 契約に合流。

Design details (EN):

  • backfill_secondary_spawn_timeout (§A7.4/§A7.7): a fail-loud marker emitted when the deferred-spawn bounded retry reaches SECONDARY_SPAWN_RETRY_TIMEOUT_MS (= VISION_UNLOAD_FINAL_TIMEOUT_MS 10min) while the worker is still absent. Because the worker-spawn-level retry holds no webPageId (ADR-0011 §A7.4 SEC-REAUDIT-01), after emit it runs a fallback-on-absence scan that CAS-guard updateMany-transitions stranded overflow rows (embeddingBackfillStartedAt < now - 10min) to terminal. from-status 2-branch (ADR-0007 Amendment 2 §A2.2.1 / ADR-0011 §A7.4 / Plan v3 §V2.1 ruling (a)-narrowed): the terminal write is split by from-status to reflect the differing recovery-handler behaviour — (1) queued-origin (stranded by worker-absence, the verified-rescuable population) → failed_with_known_reason + reason supervisor_restart_orphan (recovery-IN; its handler re-enqueues unconditionally, driving the completed path), and (2) in_progress-origin (stranded by a genuine vision-unload timeout) → UNCHANGED bare failed + reason vision_unload_timeout (recovery-OUT; SEC-REAUDIT-02 contract preserved). The two branches are mutually exclusive by from-status (a row is queued XOR in_progress), so the two updateMany calls never touch the same row, and observability order is fixed (queued → in_progress). terminalizedCount is the total number of rows that scan terminalized (queued + in_progress); targetId is null (truncateAuditTargetId(null)) because the scan terminalizes multiple rows; finalProbeStatus is the enum "vision_residual" | "probe_failed". This scan is the aggregate action under actor system:worker-supervisor; it shares the reason supervisor_restart_orphan with Section C rescue's per-row backfill_rescue_queued_gave_up (actor system:backfill-reconciliation-cron, see §11.9.6.quater) but is a distinct RoPA entry differing in action / actor.
  • vision_probe_unavailable (§A7.6): emitted when the Ollama /api/ps probe reaches the 3-strike consecutive-failure limit (SECONDARY_SPAWN_RETRY_PROBE_FAILED_STRIKE_LIMIT = 3) and the retry is blocked (stopped). Unlike vision_probe_failed (a single probe failure), this terminally stops the retry loop because the probe is persistently undecidable, preventing an unbounded retry (CWE-770). It converges with the Amendment 2 §A2.4:550 fail-closed → degrade-but-warn contract.

Standing regression coverage (backfill auto-start):

  • INV-BACKFILL-SECONDARY-SPAWN-RETRY-011 (tests/regression/standing/worker-lifecycle/inv-backfill-secondary-spawn-retry-011.test.ts) — secondary spawn retry + 3-strike block vision_probe_unavailable emit
  • INV-BACKFILL-SECONDARY-SPAWN-TIMEOUT-TERMINAL-013 (tests/regression/standing/worker-lifecycle/inv-backfill-secondary-spawn-timeout-terminal-013.test.ts) — 10min timeout backfill_secondary_spawn_timeout emit + scan-based terminal transition (large-page cross-binding)
  • INV-AUDIT-EMIT-SSOT-IMPORT-001 (audit SSOT import AST sweep, production bare literal 0)
  • Cross-ref: ADR-0011 Amendment 7 §A7.4 (fallback-on-absence scan) / §A7.6 (3-strike block) / §A7.7 (audit action SSOT + targetType)

PII-safe 設計詳細 / PII-safe design details:

  • targetIdworkerType (PII-free)。
  • details は raw IPC payload を含まない (CWE-209 Information Exposure 防御)。
  • result='denied' actions (worker_type_spoofing_detected / worker_ipc_spoofing_detected) は SIGTERM + 60s respawn suppress lifecycle と紐付き、攻撃面の即時遮断を audit trail で可観測化。
  • Cross-ref: ADR-0011 Amendment 1 (PR-D-8) §A.2 + §A.3 + Audit logs 4 new actions テーブル。

PII-safe design details (EN):

  • targetId is workerType (PII-free).
  • details excludes raw IPC payload (CWE-209 Information Exposure defense).
  • result='denied' actions (worker_type_spoofing_detected / worker_ipc_spoofing_detected) are wired to the SIGTERM + 60s respawn suppress lifecycle, making attack-surface containment observable via audit trail.
  • Cross-ref: ADR-0011 Amendment 1 (PR-D-8) §A.2 + §A.3 + the "Audit logs 4 new actions" table.

Standing regression coverage:

  • INV-WORKER-LOCK-003 (apps/mcp-server/tests/regression/standing/worker-lifecycle/inv-worker-lock-003-embedding-backfill-supervisor.test.ts cases #1-#14 including v12 targeted re-fix MF-V12-02 cases #13/#14 for worker_ipc_spoofing_detected emit)
  • INV-DATA-DELETE-002 (audit_logs cross-ref, GDPR Art.30 coverage)
  • INV-SCHEMA-ENUM-004 (inv-schema-enum-004-worker-type.test.ts, WorkerType SSOT exhaustive)

§11.7 DDL / permission change runbook (v0.4.0 PR-D-9-patch Batch D-1a, INFRA-RLS-ADMIN-GRANT-001)

PR-D-9-patch Batch D-1a (INFRA-RLS-ADMIN-GRANT-001, M severity, deadline 2026-05-25) により、reftrix_admin role の canonical privilege scope (ALL PRIVILEGES on tables + sequences + default privileges) を Prisma migration SSOT として永続化。Pre-existing infra drift (PR7d-1 d98fece4 以降) を解消し、PR-D-9-patch Phase 4 RLS Tier 2 test failure (permission denied for table users SQLSTATE 42501) の root cause を恒久対応。本 §11.7 は DDL / permission change の運用ランブックを規定する。

PR-D-9-patch Batch D-1a (INFRA-RLS-ADMIN-GRANT-001, M severity, deadline 2026-05-25) persists the canonical privilege scope of the reftrix_admin role (ALL PRIVILEGES on tables + sequences + default privileges) as a Prisma migration SSOT, resolving the pre-existing infra drift since PR7d-1 d98fece4 and permanently fixing the PR-D-9-patch Phase 4 RLS Tier 2 test failure (permission denied for table users SQLSTATE 42501) root cause. This §11.7 codifies the operational runbook for DDL / permission changes.

Migration SSOT / Migration SSOT:

  • packages/database/prisma/migrations/20260427120000_restore_admin_role_grants/migration.sql
  • packages/database/scripts/create-rls-roles.sql:101-108 (initialization-time role bootstrap SSOT)
  • 両 SSOT は同一の privilege set (ALL PRIVILEGES on tables + sequences + default privileges) を指す契約。/ Both SSOTs describe the identical privilege set as a contract.

Manual psql template (DBA emergency repair) / 手動 psql テンプレート (DBA 緊急修復用):

-- Run as a superuser (e.g. postgres) when the migration cannot be applied via
-- `prisma migrate deploy` (e.g. broken Prisma metadata, schema rollback in
-- production). Idempotent at PostgreSQL privilege catalog level.
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO reftrix_admin;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO reftrix_admin;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO reftrix_admin;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO reftrix_admin;

Rollback maintenance window note (FIND-PLAN-SEC-D1a-03 L) / ロールバック時のメンテナンスウィンドウ注意事項:

このマイグレーションを production で rollback (例: 過去 migration への手動 reset) した場合、reftrix_admin の table-level privileges が失われる可能性がある (BYPASSRLS は維持されるが、SQL-standard table privileges は別管理)。Rollback 時は メンテナンスウィンドウ内 で上記 manual psql template を即座に再適用し、Phase 4 RLS Tier 2 regression を防止すること。

If this migration is rolled back in production (e.g. manual reset to a prior migration), reftrix_admin may lose its table-level privileges (BYPASSRLS persists but SQL-standard table privileges are managed separately). On rollback, re-apply the manual psql template within the maintenance window to prevent Phase 4 RLS Tier 2 regression.

audit_logs SSOT entry / audit_logs SSOT エントリ:

fieldvalue
actionadmin_role_grant
actorsystem:migration
target_typerole
target_idreftrix_admin
details{ migration, finding: "INFRA-RLS-ADMIN-GRANT-001", tables: "ALL TABLES IN SCHEMA public", sequences: "ALL SEQUENCES IN SCHEMA public", privileges: "ALL PRIVILEGES + DEFAULT PRIVILEGES", rationale, severity: "M", deadline: "2026-05-25" } (PII-free, no operator name)
resultsuccess
retention365日 / 365 days (§11 共通方針継承 / inherits §11 common policy)
idempotencyWHERE NOT EXISTS guard (TPA-RLS-05 M) — re-apply で重複 INSERT を防止 / prevents duplicate INSERT on re-apply

PII-safe 設計詳細 / PII-safe design details (FIND-PLAN-LCC-D1A-01 M):

  • target_id は role 名 (reftrix_admin) で固定。operator name / connection string / sensitive metadata は含まない。
  • details JSON は migration ファイル名 / finding ID / privilege 列挙 / rationale / severity / deadline のみで、認証情報・接続文字列・操作者個人識別子を一切持たない (CWE-209 Information Exposure 防御 + GDPR Art.5(1)(c) data minimisation 維持)。
  • target_id is fixed to the role name (reftrix_admin); no operator names, connection strings, or sensitive metadata.
  • details JSON contains only the migration filename, finding ID, privilege enumeration, rationale, severity, and deadline — no credentials, connection strings, or operator identifiers (CWE-209 Information Exposure defense + GDPR Art.5(1)(c) data minimisation preserved).

法的根拠 / Legal basis (FIND-PLAN-LCC-D1A-03 L):

RegulationArticleApplicability
GDPRArt.32(1)(b) integrity of processingDBA-level privilege scope の SSOT 永続化は state-of-the-art technical measure であり、"ongoing confidentiality, integrity, availability and resilience" を保証する
GDPRArt.30 records of processing activitiesaudit_logs.admin_role_grant エントリにより 365日保持の processing activity record を確立
AGPL-3.0§5 (a) modification notice + §5 (b) preservation of legal noticesMigration ファイル + standing test の SPDX header 維持により AGPL §5 を遵守
個人情報保護法 (APPI) / Act on Protection of Personal Information第23条 (安全管理措置) / Article 23 (Security control measures)技術的安全管理措置として DBA role の privilege scope を SSOT 化。BYPASSRLS による全 table アクセスは "システム管理者の安全管理措置" として明示的に文書化される

M1 → M2 transition criteria (TPA-RLS-03 L) / M1 → M2 移行基準:

  • Currently (M1): apps/mcp-server/tests/regression/standing/schema-enum-sync/inv-rls-admin-grant-005.test.ts に配置 (PR-D-9-patch Batch D-1a, schema-enum-sync 拡張)。CI overhead は 0 (既存 schema-enum-sync job に追加された 1 test file のみ)。

  • Promotion to M2 (deferred to ADR-0019 + tracked issue, deadline 2026-Q3): 専用 5th-domain infra-grants/ への promotion を以下の基準で検討する:

    1. 別個の infra-permission invariant が 2 件以上追加された場合 (例: reftrix_app privilege scope の SSOT 化、role rotation policy の invariant 化)
    2. CI runtime overhead が schema-enum-sync 全体の 30% を超えた場合
    3. ADR-0019 で infra-grants/ 5th-domain の正式採用が approved された場合
  • Promotion 時の作業 / Promotion work: (i) test file を tests/regression/standing/infra-grants/ へ migrate、(ii) pnpm test:regression:standing runner に新 domain を登録、(iii) CLAUDE.md § Standing Regression Suite の "4 domains" naming を "5 domains" に更新、(iv) .github/workflows/regression-standing.yml の matrix に追加。

  • Currently (M1): Located at apps/mcp-server/tests/regression/standing/schema-enum-sync/inv-rls-admin-grant-005.test.ts (PR-D-9-patch Batch D-1a, schema-enum-sync extension). CI overhead is 0 (single test file added to the existing schema-enum-sync job).

  • Promotion to M2 (deferred to ADR-0019 + tracked issue, deadline 2026-Q3): Promotion to a dedicated 5th domain infra-grants/ is to be considered when: (1) two or more additional infra-permission invariants are added (e.g. SSOT-ifying reftrix_app privilege scope, invariant-ifying role rotation policy); (2) CI runtime overhead exceeds 30% of the overall schema-enum-sync domain; (3) ADR-0019 formally approves the infra-grants/ 5th domain.

  • Promotion work: (i) migrate the test file to tests/regression/standing/infra-grants/, (ii) register the new domain in pnpm test:regression:standing, (iii) update CLAUDE.md § Standing Regression Suite "4 domains" → "5 domains", (iv) add the new domain to .github/workflows/regression-standing.yml matrix.

Standing regression coverage (M1) / 常設 regression カバレッジ (M1):

  • INV-RLS-ADMIN-GRANT-005 (apps/mcp-server/tests/regression/standing/schema-enum-sync/inv-rls-admin-grant-005.test.ts, 4 cases + 1 skip-marker case) — reftrix_admin canonical privilege scope (TRUNCATE/REFERENCES/TRIGGER inclusive)、ALL-tables coverage、default privileges contract、audit_logs SSOT entry idempotency。

Cross-ref / クロスリファレンス:

  • Plan 1 IO §13.16 Plan Decision (CONDITIONAL, targeted re-audit TPA+SEC+LCC, 採用された domain placement / migration timestamp / idempotency guard)
  • SEC-INFRA-RLS-001 (M, SEC sign-off conditional GRANTED) + FIND-PLAN-SEC-D1a-01/03/04
  • TDA-INFRA-RLS-001 (M, infra-migration-drift) + TPA-RLS-02/03/05
  • LCC-OPINION-RLS-01 (M, GDPR Art.32(1)(b) + AGPL §5 + APPI Art.23) + FIND-PLAN-LCC-D1A-01/03/04
  • ADR-0011 (Worker dual-run lock infra runbook framework) — 本 §11.7 は ADR-0011 の infra runbook framework を継承する DDL / permission change runbook
  • packages/database/scripts/create-rls-roles.sql:101-108 (initialization-time role bootstrap SSOT)
  • packages/database/prisma/migrations/20260427120000_restore_admin_role_grants/migration.sql (Prisma migration SSOT)

§11.8 motion category SIGABRT recovery runbook (v0.4.0 PR-D-9-patch Batch D-1b, INFRA-EMBEDDING-MOTION-SIGABRT-001)

JP: PR-D-9-patch Batch D-1b (INFRA-EMBEDDING-MOTION-SIGABRT-001, M severity, deadline 2026-05-25) により、embedding-backfill Worker が motion category 処理中の planned restart で SIGABRT を起こす race condition を 3 階層 (Fix-1 + Fix-2 + Fix-3) で恒久対応した。本 §11.8 は SIGABRT 発生時の検出 + 復旧 + audit trail の運用ランブックを規定する。**Fix-1/2/3 の本旨は「race の再現」ではなく「fix-correctness invariant の永続化」**であり、4GB CI runner 環境では実 race は再現困難である (TPA-MOTION-06 L)。standing regression test は invariant violation を CI で fail させる契約検証として配置される。

EN: PR-D-9-patch Batch D-1b (INFRA-EMBEDDING-MOTION-SIGABRT-001, M severity, deadline 2026-05-25) permanently resolves a SIGABRT race condition that occurred when the embedding-backfill Worker entered planned restart while a motion-category ONNX inference was still in flight. The remediation is layered (Fix-1 + Fix-2 + Fix-3). This §11.8 codifies the operational runbook for SIGABRT detection, recovery, and audit trail. The intent of Fix-1/2/3 is invariant persistence, not race reproduction — the actual race is not reliably reproducible in a 4GB CI runner (TPA-MOTION-06 L); the standing regression test asserts the fix-correctness invariants as CI-failing contracts.

Detection / 検出:

-- SIGABRT 発生検出 (per worker type, 24h window)
-- audit_logs.action='worker_sigabrt_detected' は Fix-3 で導入された新 action。
-- v0.4.0 PR-D-9-patch Batch D-1b 以降、SIGABRT 検出時に rate-limited (1/min)
-- で emit され、`details->>'consecutiveCount'` で N 連続発生数を保持する。
SELECT
  target_id AS worker_type,
  COUNT(*) AS sigabrt_24h,
  MAX((details->>'consecutiveCount')::int) AS max_consecutive,
  BOOL_OR((details->>'suppressionTriggered')::boolean) AS suppressed_24h
FROM audit_logs
WHERE action = 'worker_sigabrt_detected'
  AND created_at >= NOW() - INTERVAL '24 hours'
GROUP BY target_id;

Recovery procedure / 復旧手順:

  1. Standing regression verification: pnpm --filter @reftrixmcp/mcp-server test:regression:standing tests/regression/standing/worker-lifecycle/inv-worker-lock-003-motion-sigabrt.test.ts で Fix-1/2/3 invariants が破綻していないか確認 / Verify Fix-1/2/3 invariants haven't regressed.
  2. Orphaned record repair: apps/mcp-server/src/scripts/repair-orphaned-backfill-records.ts --dry-run で SIGABRT で失敗した job の embeddingBackfillStatus を identify、--confirm で repair / Identify and repair orphaned embeddingBackfillStatus rows.
  3. Re-enqueue: 復旧 candidate を addEmbeddingBackfillJobWithGuard() 経由で再投入 / Re-enqueue recovery candidates via the guard helper.
  4. 24h escalation: SIGABRT detection が 24h で 3 回以上 / suppression triggered の場合は pipeline-engineer + capture-embedding-engineer に escalate (P0 incident) / If detection ≥3/24h or suppression triggered, escalate to pipeline-engineer + capture-embedding-engineer (P0).

Audit trail (GDPR Art. 30):

  • audit_logs.action='worker_sigabrt_detected' (新 action, 365d retention 継承) — Fix-3 rate-limited emit (1/min per worker type, FIND-PLAN-SEC-D1b-03 L DoS-via-log-flood defense) / New audit_logs action with 365d retention; Fix-3 rate-limited emit.
  • audit_logs.action='worker_supervisor_restart' (既存) — restart sequence は従来通り (planned / unexpected_exit / crash_max_attempts) / Existing action, restart sequence unchanged.
  • 全 entry は actor='system:worker-supervisor'target_id=workerType (PII-free)、details.finding='INFRA-EMBEDDING-MOTION-SIGABRT-001' を含む / All entries carry actor='system:worker-supervisor', target_id=workerType (PII-free), and details.finding.

Test scope clarification (TPA-MOTION-06 L) / テストスコープ明示:

本 §11.8 が参照する inv-worker-lock-003-motion-sigabrt.test.tsfix-correctness invariant (close → dispose ordering / pre-fork dispose contract / SIGABRT detection + suppress contract) を CI fail-fast で検証する。実際の SIGABRT race の再現には:

  • ONNX Runtime の native pthread COW inheritance を引き起こす十分な inference workload (motion 150+ patterns)
  • 4GB を超える RSS budget (parent + child の同時 inference state)
  • Worker Thread + child fork の planned restart taking 同期化

が必要だが、いずれも CI 4GB runner では制約超過。将来的に専用 simulator を設計する場合は ADR で formalize する (Phase 2 follow-up tracked, deadline TBD).

The standing test verifies the fix-correctness invariants (close → dispose ordering, pre-fork dispose contract, SIGABRT detection + suppress contract) as CI-failing contracts. Reproducing the actual SIGABRT race requires sufficient inference workload, >4GB RSS budget, and synchronized Worker Thread + fork restart timing — all infeasible on the 4GB CI runner. A future dedicated simulator may be formalized in an ADR (Phase 2 follow-up, deadline TBD).

Production env hygiene EMBEDDING_BACKFILL_FORK_ENABLED=true (FIND-PLAN-SEC-D1b-06 L + FIND-IMPL-SEC-D1b-01 L):

JP: production deployment は 明示的に env file (.env.production / orchestration secret manager) で EMBEDDING_BACKFILL_FORK_ENABLED=true を設定することrunForkOrFallback の default fallback path (env var unset または != "true") は in-process fallback のみを使用し、Fix-2 (pre-fork dispose) を bypass する。Fix-1 (close → dispose ordering) は env-var とは独立して常に保護を提供するが、Fix-2 による COW-window minimisation には fork mode が active であることが必須。EMBEDDING_BACKFILL_FORK_ENABLED=false は SIGABRT race の debug 用にローカル環境限定で使用 (e.g., reproducing legacy in-process behavior to triangulate the race) であり、production 用途では bypassed Fix-2 により parent OOM risk + COW window が再現する。production SSOT は EMBEDDING_BACKFILL_FORK_ENABLED=true (env-var explicit set required).

EN: Production deployments MUST explicitly set EMBEDDING_BACKFILL_FORK_ENABLED=true in the env file (.env.production / orchestration secret manager). The default fallback path of runForkOrFallback (env var unset or != "true") bypasses Fix-2's pre-fork dispose and uses in-process fallback only. Fix-1 (close → dispose ordering) protects regardless of env-var state, but Fix-2's COW-window minimisation requires fork mode to be active. EMBEDDING_BACKFILL_FORK_ENABLED=false is reserved for local debug only (e.g., reproducing legacy in-process behavior to triangulate the race); using it in production reintroduces parent OOM risk + COW windows by bypassing Fix-2. Production SSOT is EMBEDDING_BACKFILL_FORK_ENABLED=true (explicit env-var set required).

Footnote (FIND-IMPL-LCC-D1b-02 L): EMBEDDING_BACKFILL_FORK_ENABLED (this section) is distinct from PHASE5_FORK_ENABLED (The former controls embedding-backfill worker process fork (Worker Thread + ONNX ↔ parent process boundary, Fix-2 enablement); the latter controls Phase 5 (Embedding)child_process.forkin the page-analyze pipeline (Phase 5 visual / text embedding child process). They are **orthogonal flags with separate scopes**, not aliases. /EMBEDDING_BACKFILL_FORK_ENABLED(本セクション) はPHASE5_FORK_ENABLED ( とは別の env-var である。前者は embedding-backfill worker の process fork (Worker Thread + ONNX と parent process 境界、Fix-2 有効化) を制御し、後者は page-analyze pipeline の Phase 5 (Embedding) child_process.fork (Phase 5 visual / text embedding 子プロセス) を制御する。両者は独立 scope の orthogonal flag であり、alias ではない。

GDPR Art. 5(1)(e) storage-limitation note (FIND-PLAN-LCC-D1B-04 L):

SIGABRT 失敗 job の re-enqueue は personal data の保管期間を延長しない。再投入は embeddingBackfillStatus の遷移と Queue 再投入のみを行い、新規 embedding は通常の Queue-based Backfill Worker が既存 retention 規約 (screenshot 保持 = data.delete まで、PR-SS-B / ADR-0041 で 7d TTL cron 撤去済) に従って生成する (§5 と同じ semantic)。SIGABRT 自体は personal data ではなく worker-internal な race condition の signal である。

Re-enqueueing SIGABRT-failed jobs does not extend personal data retention. Re-enqueue only transitions embeddingBackfillStatus and pushes back to the Queue; new embeddings are generated by the regular Queue-based Backfill Worker under the existing retention policy (screenshot retention = until data.delete; the 7d TTL cron was removed in PR-SS-B / ADR-0041) (same semantic as §5). SIGABRT itself is a worker-internal race signal, not personal data.

CRA Article 14 disclaimer / CRA Article 14 免責 (FIND-IMPL-LCC-D1b-01 M, deadline 2026-05-25):

JP: 本 §11.8 が扱う SIGABRT race condition は operational reliability blocker であり、attacker-exploitable vulnerability ではない。EU CRA (Cyber Resilience Act, Regulation (EU) 2024/2847) Article 14 (Reporting obligations of manufacturers) の "actively exploited vulnerability" / "severe incident" 報告対象には該当せず、GDPR Article 33 (72-hour breach notification) の trigger も発動しない。本 disclaimer は ADR-0019 §"CRA Article 14 disclaimer" (lines 196-209) と平行構造で 4 軸の根拠を runbook 側に再掲する:

  • External attacker surface 不在 / No external attacker surface: SIGABRT は internal worker lifecycle (planned restart × motion last category processing × COW-inherited native pthread) に起因。external network input、user-controlled URL、untrusted upload 等の attack vector は経路に介在しない。
  • Privilege escalation 不能 / No privilege escalation: SIGABRT で worker child process が abort されても、process-level boundary を超える escalation 経路は存在しない。WorkerSupervisor respawn は host OS 内で同一 user privilege のまま fork する (host-level capability 増加なし)。
  • Information disclosure 不能 / No information disclosure: production deployment では ulimit -c 0 により core dump が抑制され、仮に core dump が writable な場合でも audit_logs.details の PII は truncateTargetId で minimisation 済み (§11.8 audit trail 規約)。CWE-209 / CWE-200 の latent risk は sanitizeErrorMessage SSOT 経由でも遮蔽される。
  • DoS amplification 不能 / No DoS amplification: WorkerSupervisor の maxRestartAttempts (default 10) で respawn 発生数が上限化されており、Fix-3 が landing 済の現状では 2 連続 SIGABRT で fail-closed 停止 + audit_logs エスカレーション entry が emit される (defense-in-depth 完成)。attacker-induced DoS 増幅経路は構造的に不能。

したがって本 §11.8 が記述する Fix-1/2/3 の運用は operational reliability ADR として PR-D-9-patch INFRA-EMBEDDING-MOTION-SIGABRT-001 finding (M severity, deadline 2026-05-25) を closure するものであり、CRA Article 14 vulnerability disclosure 義務 / GDPR Art.33 72-hour breach notification gate のいずれも trigger しない。詳細な architectural rationale は ADR-0019:196-211 参照。

EN: The SIGABRT race condition addressed in this §11.8 is an operational reliability blocker, not an attacker-exploitable vulnerability. It is not a reportable item under EU CRA Article 14 (Cyber Resilience Act, Regulation (EU) 2024/2847; "actively exploited vulnerability" / "severe incident") and does not trigger the GDPR Article 33 72-hour breach notification gate. This disclaimer mirrors ADR-0019 §"CRA Article 14 disclaimer" (lines 196-209) and re-states the 4-axis rationale at the runbook layer:

  • No external attacker surface: SIGABRT originates from internal worker lifecycle (planned restart × motion last-category processing × COW-inherited native pthread). No external network input, user-controlled URL, or untrusted upload is in the path.
  • No privilege escalation: A SIGABRT-induced worker child abort does not cross process boundaries. WorkerSupervisor respawn forks within the same host OS user privilege (no host-level capability gain).
  • No information disclosure: Production deployments suppress core dumps via ulimit -c 0; even if a core dump were writable, audit_logs.details PII is already minimised via truncateTargetId (per the §11.8 audit-trail contract). Latent CWE-209 / CWE-200 risks are additionally shielded by the sanitizeErrorMessage SSOT.
  • No DoS amplification: WorkerSupervisor's maxRestartAttempts (default 10) bounds respawn count; with Fix-3 landed, two consecutive SIGABRTs fail-closed with an audit_logs escalation entry (defense-in-depth complete). No attacker-induced DoS amplification path exists structurally.

The Fix-1/2/3 operations described in this §11.8 therefore close the PR-D-9-patch INFRA-EMBEDDING-MOTION-SIGABRT-001 finding (M severity, deadline 2026-05-25) as an operational reliability ADR, triggering neither CRA Article 14 vulnerability disclosure nor the GDPR Art.33 72-hour breach notification gate. See ADR-0019:196-211 for the full architectural rationale.

Cross-ref / クロスリファレンス:

  • INFRA-EMBEDDING-MOTION-SIGABRT-001 (M, deadline 2026-05-25) — 本 §11.8 の root finding / Root finding of this §11.8
  • IO §13.16 Plan Decision (CONDITIONAL, full re-audit, Fix-1 Option A + Fix-2 + Fix-3 same-PR landing approved) / Plan Decision summary
  • ADR-0019 "Embedding Worker Close-Before-Dispose Ordering" (Fix-1 architectural rationale) / Fix-1 ADR
  • TPA-MOTION-02 (M, observability metric pre_fork_dispose_duration_ms) — Fix-2 [EmbeddingBackfill] pre-fork dispose slow warn line (>500ms threshold, future SLO source) / Future SLO source
  • TPA-MOTION-03 (M, same-PR landing of Fix-3 telemetry-verified) / Fix-3 telemetry contract
  • TPA-MOTION-06 (L, "test verifies fix-correctness invariant, not race reproduction") / Test scope clarification rationale
  • TDA-D-1b-01 (H, block-equivalent mandatory landing of tests/workers/embedding-backfill-worker.test.ts close/dispose ordering test) / Existing test fix mandate
  • TDA-D-1b-02 (M, simulator design pending Phase 2 ADR) / Future simulator design tracking
  • FIND-PLAN-SEC-D1b-03 (L, audit emit budget rate limit defending CWE-770 DoS-via-log-flood) / Rate limit semantic
  • FIND-PLAN-SEC-D1b-04 (L, fixture genericisation — example-large-motion-page.test placeholder) / Fixture sanitisation
  • FIND-PLAN-SEC-D1b-05 (L, GPU CUDA leak coverage process_exit_during_active_inference case) / SEC-additive coverage
  • FIND-PLAN-SEC-D1b-06 (L, EMBEDDING_BACKFILL_FORK_ENABLED=false debug-only documentation) / Debug-flag scope
  • FIND-PLAN-LCC-D1B-01 (M, third-party URL avoidance) / Fixture sanitisation
  • FIND-PLAN-LCC-D1B-03 (L, synthetic motion fixture builder, no real production URLs) / Fixture builder
  • FIND-PLAN-LCC-D1B-04 (L, GDPR Art. 5(1)(e) re-enqueue does not extend retention) / Storage-limitation invariant
  • apps/mcp-server/src/workers/embedding-backfill-worker.ts:954- (Fix-1 close/dispose path)
  • apps/mcp-server/src/queues/embedding-backfill-processors.ts:264- (Fix-2 pre-fork dispose)
  • apps/mcp-server/src/services/worker-supervisor.service.ts:~1039- (Fix-3 SIGABRT detection + suppress)
  • apps/mcp-server/tests/regression/standing/worker-lifecycle/inv-worker-lock-003-motion-sigabrt.test.ts (INV-006 standing regression)

§11.9 Worker respawn lock nonce contract runbook (v0.4.0 PR-E-1, INV-WORKER-RESPAWN-LOCK-NONCE-007)

JP: PR-E-1 (Severity H, current-release-cycle deadline) により、WorkerSupervisor 内の lock nonce contract mismatch (spawnWorker:687 の per-spawn randomUUID() と acquire/extend/release path の per-supervisor bootToken の不一致) を Option A (lockNonce を bootToken に統一) で恒久対応した。本 §11.9 は zombie worker 検知 / Redis lock 強制解除 / manual restart の運用ランブックを規定する。

EN: PR-E-1 (Severity H, current-release-cycle deadline) permanently resolves a lock-nonce contract mismatch in WorkerSupervisor (per-spawn randomUUID() at spawnWorker:687 diverging from the per-supervisor bootToken in the acquire/extend/release path) via Option A (unify lockNonce on bootToken). This §11.9 codifies the operational runbook for zombie-worker detection, forced Redis-lock release, and manual restart.

§11.9.1 Detection / 検出 (D1 + D2 + D3 SOP)

3 つの検出経路を SOP として規定 / 3 detection paths to be codified as SOP:

IDDetection criterion / 検出基準Query template / Query template
D1audit_logs: planned restart 後 60s 以内に新 spawn の result='success' entry が無い / SIGABRT after planned restart with no subsequent worker_supervisor_restart result='success' for new spawn within 60ssql\nSELECT * FROM audit_logs\nWHERE action='worker_supervisor_restart'\n AND details->>'signal'='SIGABRT'\n AND details->>'restartReason'='planned'\n AND created_at > NOW() - INTERVAL '60 seconds'\nORDER BY created_at DESC;\n
D2Redis: GET reftrix:worker:active:embedding-backfill returns non-null but no active child process on host / lock 値あるが child PID 不在Redis CLI: GET reftrix:worker:active:embedding-backfill + ps -p <expected_pid>
D3BullMQ: bull:embedding-backfill:active list contains job with jobLock TTL=-2 / Active list に jobLock 削除済 job 存在Redis CLI: LRANGE bull:embedding-backfill:active 0 -1 + TTL bull:embedding-backfill:<jobId>:lock

§11.9.2 Recovery procedure (manual) / 復旧手順 (手動)

  1. Verify zombie state: D1 + D2 + D3 すべて true を確認 / Verify D1 + D2 + D3 all true.
  2. Force-release Redis lock: DEL reftrix:worker:active:embedding-backfill (only if no active child process on host) / Force-release only if no active child.
  3. Manual restart: pnpm worker:start:embedding-backfill (with REFTRIX_ALLOW_MANUAL_WORKER=true) / Manual restart with opt-out flag.
  4. Verify recovery: audit_logs を tail で監視し worker_supervisor_restart result='success' entry を確認 + worker_zombie_recovered (新 action) emit を確認 / Tail audit_logs for both worker_supervisor_restart result='success' and the new worker_zombie_recovered emit.

§11.9.3 Audit trail (GDPR Art. 30) / 監査記録 (FIND-PLAN-LCC-PRE1-04 M 確定書換)

PR-E-1 LCC-04 確定 contract:

  • action: worker_zombie_recovered (採用確定 / adopted)
  • retention: 365 days (PR-D-9 audit_logs default 継承、GDPR Art.30 audit trail 整合 / inherits default; aligns with GDPR Art.30)
  • actor: operator:<email> (manual recovery 経路のみ、autospawn は将来別 action として扱う / manual path only; autospawn deferred to a future action)
  • target_id: raw workerType (page / embedding-backfill 固定 enum、entropy なく PII 該当性ゼロ、可読性維持のため raw 採用、truncateTargetId 不要 / fixed enum, zero entropy, no PII applicability; raw adopted; truncateTargetId not needed)
  • details schema: {recovery_method: 'force_release_redis_lock', original_signal: 'SIGABRT', original_pid: <pid>, redis_lock_key: 'reftrix:worker:active:<workerType>'}
  • compliance tags: [EU GDPR Art.30][JP APPI 第23条]

SSOT: apps/mcp-server/src/services/audit-log.service.ts AUDIT_LOG_CONSTANTS.WORKER_ZOMBIE_RECOVERED_ACTION / WORKER_ZOMBIE_RECOVERED_ACTOR_PREFIX / WORKER_ZOMBIE_RECOVERY_METHODS.

-- Recovery emit verification (last 24h)
SELECT
  target_id AS worker_type,
  details->>'recovery_method' AS method,
  details->>'original_signal' AS signal,
  created_at
FROM audit_logs
WHERE action = 'worker_zombie_recovered'
  AND created_at >= NOW() - INTERVAL '24 hours'
ORDER BY created_at DESC;

§11.9.4 SLO / 運用 SLO

SLO IDMetric / 指標Target / 目標
§11.9-1Zombie state 検出 → operator 通知 latency (audit_logs query 経由) / Detection-to-notification latency≤ 5 min
§11.9-2通知 → manual restart 完了 latency (operator 手順) / Notification-to-restart latency≤ 15 min
§11.9-3(Future) 自動 zombie detection + auto-recovery 導入 / Auto detection + recovery (separate PR)Future PR (TBD)
§11.9-4Stale embedding-backfill state on web_pages 残存許容上限 (zombie state recovery 完了までの max latency) — GDPR Art.5(1)(d) accuracy invariant≤ 24 hours

§11.9-4 derivation / 24h 上限導出 (FIND-PLAN-LCC-PRE1-02 M): PR-D-9 stripe.com 568s baseline = 7-categories 完走 latency × safety margin ×150 で 24h 上限を導出。web_pages.embedding_backfill_status='pending' row が 24h 超 zombie 状態で残存する場合は GDPR Art.5(1)(d) accuracy violation の reportable trigger となり、operator manual recovery + audit_logs.worker_zombie_recovered emit が義務化される。

The 24h cap derives from the PR-D-9 stripe.com baseline of 568 s for 7-category completion × 150 safety margin. Rows stale beyond 24 h trigger a reportable GDPR Art.5(1)(d) accuracy violation, mandating operator manual recovery + audit_logs.worker_zombie_recovered emit.

-- Stale state detection (>= 24h)
SELECT
  id AS web_page_id,
  url,
  embedding_backfill_status,
  embedding_backfill_started_at,
  NOW() - embedding_backfill_started_at AS staleness
FROM web_pages
WHERE embedding_backfill_status = 'pending'
  AND embedding_backfill_started_at < NOW() - INTERVAL '24 hours'
ORDER BY embedding_backfill_started_at ASC;

§11.9.5 GDPR Art.33 + EU CRA Article 14 disclaimer (4-axis, mirrors ADR-0019:196-211 + DATA_RETENTION §11.8) / 4 軸 disclaimer (FIND-PLAN-LCC-PRE1-03 M)

JP: 本 §11.9 が扱う lock nonce contract violation は operational reliability blocker であり、attacker-exploitable vulnerability ではない。EU CRA (Regulation (EU) 2024/2847) Article 14 (Reporting obligations of manufacturers) の "actively exploited vulnerability" / "severe incident" 報告対象には該当せず、GDPR Article 33 (72-hour breach notification) の trigger も発動しない。本 disclaimer は ADR-0019 §"CRA Article 14 disclaimer" (lines 196-209) + DATA_RETENTION §11.8:831-834 / 840-843 と平行構造で 4 軸の根拠を runbook 側に再掲する:

  • External attacker surface 不在 / No external attacker surface: lock nonce mismatch は internal control plane (process-internal coordination) の implementation drift に起因し、外部 untrusted input 経由で attacker が contract violation を発火させる path は構造的に不能。Redis lock 自体は ADR-0011 §A "Not a distributed mutex" 設計通り host-internal coordination 専用。
  • Privilege escalation 不能 / No privilege escalation: zombie worker 状態でも process boundary を超える escalation は不能。WorkerSupervisor respawn は同一 host OS user privilege 内で fork する (host-level capability 増加なし)。force-release manual recovery 経路は operator:<email> actor と pre-flight WorkerActiveLockService.probeExistingLock で多重防御。
  • Information disclosure 不能 / No information disclosure: Lock value (= bootToken) は process-internal ephemeral UUID、Redis log 経由でも no PII / no secret 流出。INV-007 全 nonce equality assertion は §5.0 SEC-03 nonce-name-free assertion form (boolean form expect(actual === expected).toBe(true)) を採用し test failure path での raw value dump も構造的に防止 (Wave 5 LCC-endorsed canonical CWE-209 PII protection pattern と整合)。
  • DoS amplification 不能 / No DoS amplification: 1 worker zombie 化が他 worker / queue / DB を degrade させる連鎖はない (operable amplification factor 1.0)、内部完結型 fault。NF-6 clearInterval 修正により lock heartbeat の永久 refresh 経路 (CWE-770) も両 crashed-entry 経路で構造的に遮断済み。

したがって本 §11.9 が記述する PR-E-1 Option A + NF-6 の運用は operational reliability ADR として PR-E-1 lock nonce contract violation finding (H severity, current-release-cycle deadline) を closure するものであり、CRA Article 14 vulnerability disclosure 義務 / GDPR Art.33 72-hour breach notification gate のいずれも trigger しない。詳細な architectural rationale は ADR-0011 Amendment 4 §A4.5 + ADR-0019:196-211 参照。

EN: The lock-nonce contract violation addressed in this §11.9 is an operational reliability blocker, not an attacker-exploitable vulnerability. It is not a reportable item under EU CRA Article 14 (Regulation (EU) 2024/2847; "actively exploited vulnerability" / "severe incident") and does not trigger the GDPR Article 33 72-hour breach notification gate. This disclaimer mirrors ADR-0019 §"CRA Article 14 disclaimer" (lines 196-209) and DATA_RETENTION §11.8:831-834 / 840-843 with parallel 4-axis structure:

  • No external attacker surface: The lock-nonce mismatch originates from internal control-plane implementation drift; no external untrusted input path can trigger the violation. Redis lock itself, per ADR-0011 §A "Not a distributed mutex", is host-internal coordination only.
  • No privilege escalation: A zombie-worker state cannot cross process boundaries. WorkerSupervisor respawn forks within the same host OS user privilege (no host-level capability gain). The force-release manual-recovery path is double-guarded by an operator:<email> actor and a pre-flight WorkerActiveLockService.probeExistingLock.
  • No information disclosure: Lock value (= bootToken) is a process-internal ephemeral UUID; no PII / secret can exfiltrate via Redis logs. All INV-007 nonce equality assertions adopt the §5.0 SEC-03 nonce-name-free assertion form (boolean form expect(actual === expected).toBe(true)), preventing raw-value dumps in test-failure paths (aligned with the Wave-5 LCC-endorsed canonical CWE-209 PII-protection pattern).
  • No DoS amplification: A single-worker zombie does not cascade to other workers / queues / DB; the operable amplification factor is 1.0; the fault is self-contained. The NF-6 clearInterval fix structurally severs the CWE-770 heartbeat-refresh path at both crashed-entry sites.

The PR-E-1 Option A + NF-6 operations described in this §11.9 therefore close the PR-E-1 lock-nonce contract-violation finding (H severity, current-release-cycle deadline) as an operational reliability ADR, triggering neither CRA Article 14 vulnerability disclosure nor the GDPR Art.33 72-hour breach notification gate. See ADR-0011 Amendment 4 §A4.5 + ADR-0019:196-211 for full architectural rationale.

§11.9.6 GDPR Art. 5(1)(e) storage-limitation note + Stale state Art.5(1)(d) accuracy invariant

JP: zombie state 中の web_pages.embedding_backfill_status='pending' row は GDPR Art.5(1)(d) accuracy invariant の対象。§11.9-4 24h SLO 超過時は accuracy violation reportable trigger となり、operator manual recovery + audit_logs.worker_zombie_recovered emit が義務化。Stripe.com 568s baseline = PR-D-9 で観測した 7-categories 完走 latency、safety margin × 150 で 24h 上限導出。zombie state からの recovery は personal data の保管期間を延長しない: re-enqueue は embeddingBackfillStatus の遷移と Queue 再投入のみ、新規 embedding は通常の Queue-based Backfill Worker が既存 retention 規約 (screenshot 保持 = data.delete まで、PR-SS-B / ADR-0041 で 7d TTL cron 撤去済) に従って生成 (§5 と同じ semantic、§11.8 の retention contract と並行)。lock nonce 自体は personal data ではなく process-internal ephemeral UUID。

EN: Stale web_pages.embedding_backfill_status='pending' rows during zombie state fall under the GDPR Art.5(1)(d) accuracy invariant. SLO §11.9-4 violation (>24 h) triggers a reportable accuracy violation, mandating operator manual recovery + audit_logs.worker_zombie_recovered emit. The 24 h cap derives from the PR-D-9 stripe.com baseline of 568 s for 7-category completion × 150 safety margin. Recovery from zombie state does not extend personal-data retention: re-enqueue only transitions embeddingBackfillStatus and pushes back to the Queue; new embeddings are generated by the regular Queue-based Backfill Worker under the existing retention policy (screenshot retention = until data.delete; the 7d TTL cron was removed in PR-SS-B / ADR-0041) (same semantic as §5, parallel with the §11.8 retention contract). The lock nonce itself is not personal data — it is a process-internal ephemeral UUID.

§11.9.6.bis Backfill terminal-state reconciliation orthogonality (Item 2 / CO-30 closure, 2026-04-28) / Backfill 終端状態 reconciliation の orthogonality

Purpose / 目的: 24h Stale state SLO (zombie worker recovery) と 5min cron cadence (backfill reconciliation polling) は orthogonal な SLO であり、混同してはならない。Item 2 PR (CO-30 closure) で BackfillReconciliationCron の polling cadence default を 1h → 5min に短縮したが、worst-tail reconciliation lag は arithmetically max(staleThresholdMs, intervalMs) = 1h+5min であり、従来主張された "12x SLO improvement" は math fallacy だった。本 sub-section は cadence と staleThresholdMs の役割分離 を明文化し、Plan v0.2 §4.4.2 directive に従って TPA-05 L finding を closure する。

The 24h zombie state SLO and the 5min cron cadence are orthogonal SLOs and must not be conflated. Item 2 (CO-30 closure) reduced the default BackfillReconciliationCron polling cadence from 1h to 5min, but the worst-tail reconciliation lag is arithmetically max(staleThresholdMs, intervalMs) = 1h+5min; the "12x SLO improvement" framing in Plan v0.1 was a math fallacy. This sub-section codifies the role separation between cadence and staleThresholdMs per Plan v0.2 §4.4.2 directive, closing TPA-05 L.

Reconciliation upper bound formula / 上限計算式:

Concern / 関心Mechanism / 仕組Default value / デフォルト値
Cron polling cadenceBACKFILL_RECONCILIATION_INTERVAL_MS (backfill-reconciliation-cron.ts:DEFAULT_INTERVAL_MS)5 min (Item 2)
Stale row age filterstaleThresholdMs in fetchStaleInProgressPages1 h (immutable per IO Plan Decision option (b))
Effective reconciliation upper boundmax(staleThresholdMs, intervalMs)max(1h, 5min) = 1h + 5min worst-tail
Zombie worker recovery (lock heartbeat 完全停止)WorkerActiveLockService 60s TTL + 30s heartbeat (ADR-0011)24 h SLO (orthogonal — §11.9-4)

Cadence semantics / cadence の意味: cron polling frequency。stale row 検出 latency に直接寄与。Item 2 で 12x 高頻度化 (288 cron tick/day 相当) されたが、tick 1 回あたりは inFlight skip-tick backpressure (backfill-reconciliation-cron.ts:runOnce) で前回完了前なら skip するため、spike 時の queue piling は構造的に防止。

cron polling frequency. Directly governs stale-row detection latency. Item 2 raised cadence 12x (≈288 ticks/day); each tick is gated by the inFlight skip-tick backpressure (backfill-reconciliation-cron.ts:runOnce) so a previous in-flight sweep prevents tick piling during spikes.

staleThresholdMs semantics / staleThresholdMs の意味: worker-side late-arrive race avoidance window。embedding-backfill worker per-job UPDATE が cron reconcile より遅れて到着しても CAS 競合しないための保守的境界。1h より短くすると embedding-backfill-worker.ts:661 UPDATE と reconcileInProgressRows updateMany CAS が race するリスクが上昇する (CO-30-FOLLOWUP scope の IPC notification path 整備が前提)。

worker-side late-arrive race avoidance window. A conservative boundary so the embedding-backfill worker's per-job UPDATE does not race the cron reconcile via the CAS UPDATE. Shortening below 1h raises the risk that embedding-backfill-worker.ts:661 UPDATE collides with the reconcileInProgressRows updateMany CAS (CO-30-FOLLOWUP IPC notification path is the prerequisite for safe reduction).

True 12x SLO improvement deferred to CO-30-FOLLOWUP M 2026-Q4 / 真の 12x SLO 改善は CO-30-FOLLOWUP に deferred: cadence + staleThresholdMs 両方を 5min に short にすることで真の 12x 改善が実現可能だが、worker-side late-arrive race risk + Option B IPC 補強の combined PR 化が必要 (Plan v0.2 §3.4 CO-30-FOLLOWUP carryover、deadline 2026-Q4)。

True 12x SLO improvement (cadence + threshold both 5min) requires the CO-30-FOLLOWUP combined PR (Option B IPC notification path + staleThresholdMs reduction), tracked at deadline 2026-Q4 per Plan v0.2 §3.4.

Cross-ref: Plan v0.2 §3.4 + §4.4.2 (TPA-05 L); PR-E-1 finding registry §1.3 CO-30 (closed by Item 2) + CO-30-FOLLOWUP (NEW 2026-Q4); Item 2 finding registry §1.3.1 (CO-30 closure mechanism) + §1.3.2 (CO-30-FOLLOWUP self-registration); ADR-0008 §6 (Skip Recovery / 7d TTL); ADR-0011 (Worker Dual-run Lock); backfill-reconciliation-cron.ts:DEFAULT_INTERVAL_MS (5min); backfill-reconciliation.service.ts:DEFAULT_STALE_THRESHOLD_MS (1h, immutable).

§11.9.6.ter backfill_reconcile_in_progress_failed audit action (Plan v2 PR-B, FIND-IMPL-V2-L5 / TDA-IMPL-V2-02, 2026-05-23) / backfill_reconcile_in_progress_failed 監査アクション

Purpose / 目的: Plan v2 PR-B (part visual terminal-skip F1–F4) で reconcileInProgressRows (backfill-reconciliation.service.ts) に追加された terminal in_progress → failed_with_known_reason reconciliation 遷移の GDPR Art.30 (処理活動の記録義務) audit emit を retention contract に記録する。既存 §11.9.6.bis (reconciliation cron orthogonality) と並行し、本 §11.9.6.ter は 新 audit action の retention / PII 分類 を扱う。なお当初 (Plan v2 PR-B) は残余ありの stale 行を plain failed に pin していたが、後続の motion backfill / terminal-status 修正 (defect B fix) で failed_with_known_reason + failure_reason='supervisor_restart_orphan' + failed_at 遷移に統一され、recovery service の auto_recoverable scan window に乗るようになった (詳細は backfill-reconciliation.service.tsreconcileInProgressRows JSDoc / INV-BACKFILL-RECONCILE-METADATA-010 参照)。

This subsection records, within the retention contract, the GDPR Art.30 (processing-records obligation) audit emit for the terminal in_progress → failed_with_known_reason reconciliation transition added to reconcileInProgressRows (backfill-reconciliation.service.ts) in Plan v2 PR-B (part visual terminal-skip F1–F4). It runs parallel to §11.9.6.bis (reconciliation cron orthogonality); this §11.9.6.ter covers the retention / PII classification of the new audit action. Note: while Plan v2 PR-B originally pinned a remaining stale row to plain failed, the subsequent motion-backfill / terminal-status fix (defect B fix) unified the transition to failed_with_known_reason + failure_reason='supervisor_restart_orphan' + failed_at, routing the row into the recovery service's auto_recoverable scan window (see the reconcileInProgressRows JSDoc in backfill-reconciliation.service.ts / INV-BACKFILL-RECONCILE-METADATA-010).

Audit action contract / 監査アクション契約 (SSOT-imported per ` §"Worker actor naming SSOT"):

fieldvalue
actionbackfill_reconcile_in_progress_failed (SSOT: AUDIT_ACTION_BACKFILL_RECONCILE_IN_PROGRESS_FAILED)
actorsystem:backfill-reconciliation-cron (SSOT: AUDIT_ACTOR_BACKFILL_RECONCILIATION_CRON)
target_typeweb_page
target_idtruncateAuditTargetId(row.id) — CWE-209 PII minimisation (8-char prefix + ...)
details{ remainingStatus, failureReason, newStatus, stalenessMs } (PII-free, numeric/enum only — no raw error message, no URL, no user identifier)
resultfailure
retention365日 / 365 days (§11 共通方針継承 / inherits §11 common policy)
failure-modeaudit emit 失敗は non-fatal (logger.warn + sanitizeErrorMessage、reconciliation を abort しない) / non-fatal (logger.warn + sanitizeErrorMessage, never aborts)

PII 評価 (非PII 再確認) / PII Assessment (non-PII reconfirmation):

  • JP: details.remainingStatuscomputeRemainingStatusWithPrisma の finalStatus enum 値 ("in_progress" 等)、details.stalenessMs は経過 ms の数値であり、自然人を識別可能な情報を含まない (GDPR Recital 26 / Art.4(1) personal data 定義に非該当)。defect B fix で追加された 2 field の非PII 再確認: details.failureReason は SSOT EmbeddingBackfillFailureReason enum 値 (本経路では "supervisor_restart_orphan"classifyFailureReasonPolicy で auto_recoverable バケット) であり、details.newStatusEmbeddingBackfillStatus enum 値 (本経路では "failed_with_known_reason") である。いずれも固定の enum literal であって自然人を識別する情報経路を持たず、GDPR Recital 26 / Art.4(1) personal data 定義に非該当 (process / job lifecycle のオペレーション分類値のみ)。target_idtruncateAuditTargetId で truncate 済 (CWE-209 information exposure 防御、§11 共通 PII minimisation 契約継承)。本 action は web_pages row のライフサイクルに従属し独立 retention horizon を持たない。GDPR Art.17 data.delete 同期削除において、関連 audit_logs row は §11 audit log retention policy に従う (削除権処理は §3.2 / §6.3 CASCADE 契約に従う)。
  • EN: details.remainingStatus is a computeRemainingStatusWithPrisma finalStatus enum value (e.g. "in_progress"); details.stalenessMs is a numeric elapsed-ms value — neither identifies a natural person (does not meet the GDPR Recital 26 / Art.4(1) personal-data definition). Non-PII reconfirmation of the 2 fields added by the defect B fix: details.failureReason is an SSOT EmbeddingBackfillFailureReason enum value ("supervisor_restart_orphan" on this path, an auto_recoverable bucket under classifyFailureReasonPolicy), and details.newStatus is an EmbeddingBackfillStatus enum value ("failed_with_known_reason" on this path). Both are fixed enum literals with no linkage path to any natural person, so they do not meet the GDPR Recital 26 / Art.4(1) personal-data definition (operational process / job-lifecycle classification values only). target_id is truncated via truncateAuditTargetId (CWE-209 information-exposure defense, inheriting the §11 common PII minimisation contract). This action follows the web_pages row lifecycle with no independent retention horizon; under GDPR Art.17 data.delete synchronous erasure, the related audit_logs rows follow the §11 audit-log retention policy (erasure handling follows the §3.2 / §6.3 CASCADE contract).

Gap closed / closure した gap: 既存 skipped_* TTL/retry-cap 経路は skip_recovery_expired / skip_recovery_capped を emit していたが、in_progress-origin の terminal failed_with_known_reason 遷移には audit emit が無かった。本 action はその gap を closure し、reconciliation cron の全 terminal 遷移経路を GDPR Art.30 で可観測化する。Legacy actor note: 既存 expireSkippedRowOverTTL / pinSkippedRowOverRetryCap は bare "backfill-reconciliation-cron" actor (legacy, system: prefix 無し) を emit するが、本 §11.9.6.ter action は SSOT-bound system:backfill-reconciliation-cron 形式を使う (将来の legacy actor SSOT 統合の対象)。

The pre-existing skipped_* TTL/retry-cap path emitted skip_recovery_expired / skip_recovery_capped, but the in_progress-origin terminal failed_with_known_reason transition had no audit emit. This action closes that gap, making all terminal-transition paths of the reconciliation cron observable under GDPR Art.30. Legacy actor note: the pre-existing expireSkippedRowOverTTL / pinSkippedRowOverRetryCap emit a bare "backfill-reconciliation-cron" actor (legacy, no system: prefix), while this §11.9.6.ter action uses the SSOT-bound system:backfill-reconciliation-cron form (a candidate for future legacy-actor SSOT consolidation).

Cross-ref: ADR-0018 Amendment 7 §7.9 (reconcile in_progress→failed_with_known_reason audit emit design) / apps/mcp-server/src/services/backfill-reconciliation.service.ts (reconcileInProgressRows emit) / apps/mcp-server/src/audit/audit-actions.ts (AUDIT_ACTION_BACKFILL_RECONCILE_IN_PROGRESS_FAILED + AUDIT_ACTOR_BACKFILL_RECONCILIATION_CRON SSOT) / §"Worker actor naming SSOT" (T1-wins ruling) / DATA_RETENTION §11.9.6.bis (reconciliation orthogonality) / INV-BACKFILL-RECONCILE-METADATA-010 (apps/mcp-server/tests/regression/standing/large-page/inv-backfill-reconcile-metadata-010.test.ts, defect B terminal-status metadata invariant) / IO Impl Decision anchor 019e5259-28fb(Plan v2 PR-B original) +019e6a93-bcb0` (defect B terminal-status fix, CONDITIONAL).

§11.9.6.quater backfill_rescue_queued / backfill_rescue_queued_gave_up audit actions (Plan v3 Section C, FIND-IMPL-LCC-05 / NEW-LCC-L-01, 2026-05-31) / backfill_rescue_queued / backfill_rescue_queued_gave_up 監査アクション

Purpose / 目的: Plan v3 Section C (worker-present queued-stuck rescue scan、reconcileQueuedRows in backfill-reconciliation.service.ts) で追加された 2 個の新 audit_logs.action の GDPR Art.30 (処理活動の記録義務) retention / PII 分類を retention contract に記録する。BullMQ job が失われ (worker 中断 / Redis flush / job-retention 失効) queued に固着した large-page overflow row を救済 (rescue P2 re-enqueue) または救済断念 (rescue P3 give-up) する遷移を可観測化する。§11.9.6.ter (backfill_reconcile_in_progress_failedin_progress-origin terminal) と並行し、本 §11.9.6.quater は queued-origin の rescue / give-up 2 action を扱う。

This subsection records, within the retention contract, the GDPR Art.30 (processing-records obligation) retention / PII classification of the 2 new audit_logs.action entries added in Plan v3 Section C (the worker-present queued-stuck rescue scan, reconcileQueuedRows in backfill-reconciliation.service.ts). They make observable the transition that rescues (rescue P2 re-enqueue) or gives up (rescue P3 give-up) a large-page overflow row stuck in queued after its BullMQ job was lost (worker interruption / Redis flush / job-retention expiry). It runs parallel to §11.9.6.ter (backfill_reconcile_in_progress_failed, the in_progress-origin terminal); this §11.9.6.quater covers the 2 queued-origin rescue / give-up actions.

actionactortargetTyperetentionresultdetails (PII-safe)
backfill_rescue_queuedsystem:backfill-reconciliation-cronweb_page365日 / 365 daysfailureretryCountAfter, stalenessMs, enqueuedCategories
backfill_rescue_queued_gave_upsystem:backfill-reconciliation-cronweb_page365日 / 365 daysfailurefailureReason, originStatus, retryCountAtCap

設計詳細 / Design details:

  • backfill_rescue_queued (rescue P2、ADR-0007 Amendment 2 §A2.1 / Plan v3 §2 判断7 remedy (a)): BullMQ job を失った queued-stuck page を re-arm (queuedqueuedstartedAt=now()retryCount+1) し 7 backfill カテゴリ全てを completed 方向へ re-enqueue したとき emit。result: "failure" は「現に失敗している」のではなく「stuck-queued インシデントから recovery 中である」ことを表す observability 上の選択 (FIND-PLAN-L-02 / TDA-IMPL-L-02、JSDoc に rationale 記載)。
  • backfill_rescue_queued_gave_up (rescue P3、ADR-0007 Amendment 2 §A2.1 / §A2.5): queued-stuck page が共有 retry budget (embeddingBackfillRetryCount >= SKIP_RECOVERY_RETRY_CAP = 5) を使い切り、CAS queuedfailed_with_known_reason (reason supervisor_restart_orphan) で give-up したとき per-row emit。action 名自体が origin (queued-origin give-up) を固定するため、in_progress-origin の backfill_reconcile_in_progress_failed (§11.9.6.ter) とは RoPA エントリが明確に分離する (remedy (a): clean RoPA separation、details.originStatus discriminator で in_progress-origin action を再利用するより IO が選好)。details.originStatus は常に "queued"。本 per-row action は §A7.4 give-up scan の集約 action backfill_secondary_spawn_timeout (actor system:worker-supervisor) と reason supervisor_restart_orphan を共有するが、別 action / 別 actor / per-row vs 集約という別 RoPA エントリである (§11.9.6.bis の reconciliation orthogonality に整合)。

Design details (EN):

  • backfill_rescue_queued (rescue P2, ADR-0007 Amendment 2 §A2.1 / Plan v3 §2 判断7 remedy (a)): emitted when a queued-stuck page whose BullMQ job was lost is re-armed (queuedqueued, startedAt=now(), retryCount+1) and all 7 backfill categories are re-enqueued toward completed. result: "failure" is an observability choice reflecting that the row is being recovered from a stuck-queued incident, not failing now (FIND-PLAN-L-02 / TDA-IMPL-L-02, rationale documented in the JSDoc).
  • backfill_rescue_queued_gave_up (rescue P3, ADR-0007 Amendment 2 §A2.1 / §A2.5): emitted per-row when a queued-stuck page has exhausted the shared retry budget (embeddingBackfillRetryCount >= SKIP_RECOVERY_RETRY_CAP = 5) and is given up via CAS queuedfailed_with_known_reason (reason supervisor_restart_orphan). The action name itself fixes the origin (queued-origin give-up), so the RoPA entry is cleanly separated from the in_progress-origin backfill_reconcile_in_progress_failed (§11.9.6.ter) — remedy (a): clean RoPA separation, an IO preference over reusing the in_progress-origin action with a details.originStatus discriminator. details.originStatus is always "queued". This per-row action shares the reason supervisor_restart_orphan with the §A7.4 give-up scan's aggregate action backfill_secondary_spawn_timeout (actor system:worker-supervisor), but is a distinct RoPA entry differing in action / actor / per-row vs aggregate (consistent with the §11.9.6.bis reconciliation orthogonality).

PII-safe 分類 / PII-safe classification:

  • JP: 両 action の details は固定 enum literal と数値のみで構成され、自然人を識別可能な情報を含まない (GDPR Recital 26 / Art.4(1) personal data 定義に非該当)。backfill_rescue_queued.detailsretryCountAfter (number: +1 後の embeddingBackfillRetryCount)、stalenessMs (number: embeddingBackfillStartedAt からの経過 ms)、enqueuedCategories (number: 再 enqueue 成功カテゴリ数)。backfill_rescue_queued_gave_up.detailsfailureReason (string: 常に "supervisor_restart_orphan" enum)、originStatus (string: 常に "queued")、retryCountAtCap (number: give-up 時の embeddingBackfillRetryCount ≥ 5)。いずれも raw error message / URL / user identifier を含まず process / job lifecycle のオペレーション分類値のみ。target_idtruncateAuditTargetId で truncate 済 (CWE-209 information exposure 防御、§11 共通 PII minimisation 契約継承)。両 action は web_pages row のライフサイクルに従属し独立 retention horizon を持たない。GDPR Art.17 data.delete 同期削除において、関連 audit_logs row は §11 audit log retention policy に従う (削除権処理は §3.2 / §6.3 CASCADE 契約に従う)。両 action は INV-AUDIT-EMIT-SSOT-IMPORT-001 対象で production bare literal 0 を保証し、emit 経路は INV-PAGE-QUEUE-001 / INV-014 (large-page) で test-covered。
  • EN: Both actions' details consist solely of fixed enum literals and numeric values, containing no information that identifies a natural person (does not meet the GDPR Recital 26 / Art.4(1) personal-data definition). backfill_rescue_queued.detailsretryCountAfter (number: embeddingBackfillRetryCount after +1), stalenessMs (number: elapsed ms since embeddingBackfillStartedAt), enqueuedCategories (number: categories successfully re-enqueued). backfill_rescue_queued_gave_up.detailsfailureReason (string: always the "supervisor_restart_orphan" enum), originStatus (string: always "queued"), retryCountAtCap (number: embeddingBackfillRetryCount at give-up, ≥ 5). None contain raw error messages, URLs, or user identifiers — only operational process / job-lifecycle classification values. target_id is truncated via truncateAuditTargetId (CWE-209 information-exposure defense, inheriting the §11 common PII minimisation contract). Both actions follow the web_pages row lifecycle with no independent retention horizon; under GDPR Art.17 data.delete synchronous erasure, the related audit_logs rows follow the §11 audit-log retention policy (erasure handling follows the §3.2 / §6.3 CASCADE contract). Both actions are subject to INV-AUDIT-EMIT-SSOT-IMPORT-001 (zero production bare literals), and their emit paths are test-covered by INV-PAGE-QUEUE-001 / INV-014 (large-page).

Cross-ref: ADR-0007 Amendment 2 §A2.1 / §A2.5 (queued rescue + give-up lifecycle transitions, queued give-up action separation) / apps/mcp-server/src/services/backfill-reconciliation.service.ts (reconcileQueuedRows rescue P2 / P3 emit) / apps/mcp-server/src/audit/audit-actions.ts (AUDIT_ACTION_BACKFILL_RESCUE_QUEUED + AUDIT_ACTION_BACKFILL_RESCUE_QUEUED_GAVE_UP + AUDIT_ACTOR_BACKFILL_RECONCILIATION_CRON SSOT) / §"Worker actor naming SSOT" (T1-wins ruling) / DATA_RETENTION §11.9.6.bis (reconciliation orthogonality) / §11.9.6.ter (in_progress-origin terminal) / DATA_RETENTION §11.9.5 give-up scan from-status 2-branch (aggregate backfill_secondary_spawn_timeout, shared reason supervisor_restart_orphan) / INV-PAGE-QUEUE-001 / INV-BACKFILL-QUEUED-RESCUE-014 (apps/mcp-server/tests/regression/standing/large-page/inv-backfill-queued-rescue-014.test.ts, queued-stuck rescue 10 contracts) / IO Impl Decision anchor 019e7e6d-0da2` (Plan v3 Phase 2 V1 APPROVE) + FIND-IMPL-LCC-05 / NEW-LCC-L-01.

§11.9.7 Cross-references / 相互参照

  • PR-E-1 Plan v1.1: ` (775 LoC) / Plan v1.1 must-implement contract
  • PR-E-1 Finding Registry: ` §1 (CONDITIONAL, IO Plan Decision v1) + §1.8 (APPROVE, IO Plan Decision v2)
  • ADR-0011 Amendment 4 "Lock Nonce Contract Clarification" / Amendment 4 architectural rationale
  • ADR-0019 §"CRA Article 14 disclaimer" lines 196-209 / 4-axis disclaimer source
  • DATA_RETENTION §11.7 (DDL / permission change runbook) / Audit-trail structural precedent
  • DATA_RETENTION §11.8 (motion category SIGABRT recovery runbook) / SIGABRT detection SQL precedent + parallel CRA Article 14 disclaimer
  • INV-WORKER-RESPAWN-LOCK-NONCE-007 standing test (8 cases) / apps/mcp-server/tests/regression/standing/worker-lifecycle/inv-worker-respawn-lock-nonce-007.test.ts
  • audit-log.service.ts SSOT: AUDIT_LOG_CONSTANTS.WORKER_ZOMBIE_RECOVERED_ACTION / WORKER_ZOMBIE_RECOVERED_ACTOR_PREFIX / WORKER_ZOMBIE_RECOVERY_METHODS
  • worker-supervisor.service.ts:687 (Option A landing) + worker-supervisor-helpers.ts clearLockHeartbeatTimer helper (NF-6 CWE-770 landing)
  • CO-22 closure (PR-D-9-patch §13.17.8 mirror) / INV-007 6 cases addition で本 PR landing 時に CLOSED (PR-E-1)
  • CO-PRDD9-01 / OBS-7 closure / Plan §6.1 + §6.1.1 + Phase 2 commit gate wc -l ≤ 1500 step landing で本 PR landing 時に closure
  • CO-25 / CO-26 NEW carryovers / Plan v1.1 Appendix C + Registry §1.3 で tracked (deadline 2026-Q4 / 2026-Q3)

§11.10 FL-2 cleanup cron + worker_pid non-PII classification + Art.17 vs Art.5(1)(c) responsibility boundary (v0.5.0 Plan v3 Track T4, FIND-IMPL-T4-LCC-M-01)

Scope / 適用範囲: 本サブセクションは Plan v3 Track T4 (Worker Pre-Return Pause failure-path race) で導入される (a) web_pages.worker_pid INT NULLABLE カラム、(b) apps/mcp-server/scripts/cleanup-orphaned-web-pages.ts FL-2 cleanup cron (30d retention)、(c) failed_with_known_reason 列挙経路 (worker_restart_during_inflight_phase_<N>) の3要素について、GDPR / APPI / CCPA 3管轄での法的位置付けを記録する。LCC primary co-auditor sign-off contract per FIND-PLAN-V3-T4-LCC-M-01 + LCC-M-03 (deadline 2026-08-31, T4 deadline aligned)、Wave 5-equivalent bilingual JP/EN landing。

This subsection records the legal classification across GDPR / APPI / CCPA jurisdictions of the three FL-2 elements introduced in Plan v3 Track T4: (a) web_pages.worker_pid INT NULLABLE column, (b) the apps/mcp-server/scripts/cleanup-orphaned-web-pages.ts FL-2 cleanup cron with 30d retention, (c) the failed_with_known_reason enumerated path (worker_restart_during_inflight_phase_<N>). LCC primary co-auditor sign-off contract per FIND-PLAN-V3-T4-LCC-M-01 + LCC-M-03 (deadline 2026-08-31, T4 deadline aligned), Wave 5-equivalent bilingual JP/EN landing.

§11.10.1 worker_pid カラム non-PII 分類 / worker_pid Column Non-PII Classification

JP: web_pages.worker_pid INT NULLABLE は Worker プロセスの Linux PID (process identifier) を保持する server-side ephemeral operational metadata であり、3管轄すべてで non-PII (個人情報に該当しない) と分類される:

  • [EU] GDPR Art.4(1) "personal data" 定義 (一次法令 / primary regulatory authority): "any information relating to an identified or identifiable natural person". PID はプロセス識別子であって、いかなる自然人 (natural person) とも関連しない。本判定は Art.4(1) の regulation 本文が定める "relating to a natural person" 構成要件の 直接的な不充足 に基づく (operational system metadata は data subject と "relating to" 関係を持たない)。EDPB Guidelines 4/2019 §3 (Article 29 Working Party endorsed guidance、補完的解釈) は同読み (operational system metadata が Art.4(1) scope 外であること) を確認する補強根拠として参照する。
  • [JP] 個人情報の保護に関する法律 第2条第1項 "個人情報" 定義: 「生存する個人に関する情報であって、特定の個人を識別することができるもの」。PID は OS 内のプロセス識別子であり、生存する個人を識別するものではない。
  • [US] CCPA/CPRA Cal. Civ. Code §1798.140(o)(1) "personal information" 定義: "information that identifies, relates to, describes, is reasonably capable of being associated with, or could reasonably be linked, directly or indirectly, with a particular consumer or household". PID は particular consumer / household に reasonably linked される情報経路を持たない。

特性 / Characteristics: PID は Worker spawn 時に populate、planned exit 時に clear、parent web_pages row の lifetime 内でのみ persist。maxJobsBeforeRestart=1 既定 (CLAUDE.md Worker 既定 + ADR-0009 / ADR-0011) により実効寿命 ≤ 約1時間。SSOT cross-ref: Prisma schema コメント (packages/database/prisma/schema.prisma worker_pid field doc-comment) に同等の non-PII classification statement を併記。先行する DATA_RETENTION §11 worker_supervisor_restart audit_logs entry の pid field (details JSON) が同じ non-PII semantic で運用されており、本分類は repo 内一貫性を保つ。

EN: web_pages.worker_pid INT NULLABLE holds the Linux PID (process identifier) of a Worker process — server-side ephemeral operational metadata. Classified as non-PII (not personal information) under all three jurisdictions:

  • [EU] GDPR Art.4(1) "personal data" definition (primary regulatory authority): "any information relating to an identified or identifiable natural person". A PID is a process identifier and does not relate to any natural person. The classification rests on the direct failure of the Art.4(1) "relating to a natural person" constituent element in the regulation text itself (operational system metadata holds no "relating to" relationship to any data subject). EDPB Guidelines 4/2019 §3 (Article 29 Working Party endorsed guidance, supplementary interpretive layer) is cited as confirmatory support of the same reading (operational system metadata falls outside Art.4(1) scope).
  • [JP] APPI 第2条第1項 "personal information" definition: "information about a living individual that can identify a specific individual". A PID is an OS-internal process identifier and does not identify any living individual.
  • [US] CCPA/CPRA Cal. Civ. Code §1798.140(o)(1) "personal information" definition: "information that identifies, relates to, describes, is reasonably capable of being associated with, or could reasonably be linked, directly or indirectly, with a particular consumer or household". A PID has no reasonable linkage path to any particular consumer or household.

Characteristics: PID is populated on Worker spawn, cleared on planned exit, persisted only as long as the parent web_pages row exists. With the default maxJobsBeforeRestart=1 (CLAUDE.md Worker defaults + ADR-0009 / ADR-0011), effective lifetime ≤ ~1 hour. SSOT cross-ref: an equivalent non-PII classification statement is mirrored in the Prisma schema comment (packages/database/prisma/schema.prisma worker_pid field doc-comment). The precedent worker_supervisor_restart audit_logs entry's pid field (details JSON) under DATA_RETENTION §11 has been operating under the same non-PII semantic, and this classification preserves repo-internal consistency.

§11.10.2 Art.17 vs Art.5(1)(c) 責務境界 / Art.17 vs Art.5(1)(c) Responsibility Boundary

JP: GDPR Art.17 (Right to Erasure / 削除権) と Art.5(1)(c) (Data Minimisation / データ最小化) は 直交する責務 (orthogonal responsibilities) であり、互いに代替・重複しない。Plan v3 Track T4 で導入される FL-2 cleanup cron は Art.5(1)(c)/(e) 経路、data.delete MCP tool は Art.17 経路を担当し、両者は次の責務境界で運用される:

責務 / Responsibility経路 / PathSLA / 契約対象範囲 / Scope
Art.17 Right to Erasure (synchronous)data.delete MCP tool + 11-table cascade (INV-DATA-DELETE-002 standing regression)"without undue delay" (ICO 1-month guidance) — synchronous deletionData subject 由来の erasure request 全般。worker_restart_during_inflight_phase_<N> 状態の row も含めて、status filter に依存せず即時削除。web_pages.failed_with_known_reason カラムも 11-table cascade に含まれる (synchronous deletion path で handled)。
Art.5(1)(c)/(e) Data Minimisation (operational cleanup)cleanup-orphaned-web-pages.ts FL-2 cron (30d retention, LCC-H-01 sign-off)30d retention boundary、cron tick 単位で実施 (operational SLA、not user-facing)Data subject erasure request が無い residual orphan rows のみ。analysis_phase_status='failed' AND analysis_error LIKE 'worker_restart_during_inflight_phase_%' の operational debugging artifact が対象。worker_job_lifecycle テーブル (operational state) は本契約外 (no PII subject linkage、documented exclusion rationale per ADR-0009 Amendment 2 §A2.4)。

両経路は overlap・interference しない: data.delete は status filter に関係なく data subject 要求に対し即時削除を実施、FL-2 cron は orphan-event filter にマッチして <RETENTION_DAYS> を超過した行のみ削除する。Art.17 が cron による操作を待つことは無く (SLA 整合不能)、FL-2 cron が Art.17 の primary path となることは無い (synchronous delivery 要件を満たさない)。

EN: GDPR Art.17 (Right to Erasure) and Art.5(1)(c) (Data Minimisation) are orthogonal responsibilities — neither substitutes nor duplicates the other. The FL-2 cleanup cron introduced in Plan v3 Track T4 covers the Art.5(1)(c)/(e) path; the data.delete MCP tool covers the Art.17 path. Their responsibility boundary:

ResponsibilityPathSLAScope
Art.17 Right to Erasure (synchronous)data.delete MCP tool + 11-table cascade (INV-DATA-DELETE-002 standing regression)"without undue delay" (ICO 1-month guidance) — synchronous deletionAll data-subject-originated erasure requests. Rows in worker_restart_during_inflight_phase_<N> state are deleted immediately regardless of status filter. The web_pages.failed_with_known_reason column is included in the 11-table cascade (handled by synchronous deletion path).
Art.5(1)(c)/(e) Data Minimisation (operational cleanup)cleanup-orphaned-web-pages.ts FL-2 cron (30d retention, LCC-H-01 sign-off)30d retention boundary; executed per cron tick (operational SLA)Only residual orphan rows where no data-subject erasure request was made. Targets operational debugging artifacts matching analysis_phase_status='failed' AND analysis_error LIKE 'worker_restart_during_inflight_phase_%'. The worker_job_lifecycle table (operational state) is out of scope (no PII subject linkage; documented exclusion rationale per ADR-0009 Amendment 2 §A2.4).

The two paths do not overlap or interfere: data.delete performs synchronous deletion regardless of status filter for any data-subject request; the FL-2 cron only deletes rows matching the orphan-event filter that have aged beyond <RETENTION_DAYS>. Art.17 never waits for cron-based operations (SLA mismatch); the FL-2 cron is never the primary Art.17 path (does not satisfy the synchronous-delivery requirement).

§11.10.3 30d retention contract + Art.30 audit_logs cross-reference / 30日保持契約 + Art.30 監査ログ相互参照

JP: FL-2 cleanup cron (cleanup-orphaned-web-pages.ts) は RETENTION_DAYS=30web_pages.analysis_completed_at < NOW() - INTERVAL '30 days' filter で適用し、orphan rows を削除する。retention 値の選定根拠 (LCC-H-01 確定値): operator が orphan event を 30d 遡って forensic 調査でき、weekly P0 incident retro / monthly orphan-rate KPI review (design §A2.8 Monitoring) に十分な期間でありながら、GDPR Art.5(1)(c) "necessary" 境界内に収まる。7d alternative は monthly retro を切り詰める、90d alternative は forensic を minimisation に優越させる解釈となり EDPB Guidelines 4/2019 §3.6 storage-limitation 正当化が必要。

Art.30 record-keeping invariant (LCC-H-02): FL-2 cron が削除する各 row に対し、削除前に 1 件の audit_logs.action='worker_restart_during_inflight_phase' entry が target_id=truncateTargetId(webPageId) 形式で発火している (atomicity invariant: cron が削除する行数 = 該当 audit_logs entry 数、no skipped emit)。本 audit entry は 既存 §11 audit_logs 365d retention 契約 (GDPR Art.30(1) records of processing activities) に従って 365d 保持される。FL-2 cron が web_pages row を 30d で削除しても audit_logs entry は 365d 残存し、Art.30 record-keeping obligation は永続的に充足される。365d audit_logs retention は 30d FL-2 retention と 独立した不変条件 であり、cron retention 変更が audit_logs retention に影響しない設計。

failed_with_known_reason enum disclosure (LCC-M-02): phase number <N> (∈ {0, 1, 2.5, 4, 5, 7.5}) の disclosure は GDPR Art.13/14 transparency 義務 (processing purposes 開示) と整合し、CWE-209 information exposure の redaction 要件を引き起こさない operational diagnostic identifier。phase number は CLAUDE.md / ADR-0007 / ADR-0009 で publicly documented されており、内部 architecture の security-sensitive disclosure には該当しない。詳細な legal reasoning は ADR-0009 Amendment 2 §A2.4 / §A2.6 参照。

EN: The FL-2 cleanup cron (cleanup-orphaned-web-pages.ts) applies RETENTION_DAYS=30 via filter web_pages.analysis_completed_at < NOW() - INTERVAL '30 days' to delete orphan rows. Retention rationale (LCC-H-01 confirmed value): operators can forensically investigate orphan events 30 days back, sufficient for weekly P0 incident retros and monthly orphan-rate KPI reviews (design §A2.8 Monitoring), while staying within the GDPR Art.5(1)(c) "necessary" bound. The 7d alternative truncates monthly retros; the 90d alternative tilts forensic over minimisation, requiring EDPB Guidelines 4/2019 §3.6 storage-limitation justification.

Art.30 record-keeping invariant (LCC-H-02): For every row the FL-2 cron deletes, exactly one prior audit_logs.action='worker_restart_during_inflight_phase' entry is emitted in the target_id=truncateTargetId(webPageId) form (atomicity invariant: cron-deleted row count = matching audit_logs entry count, no skipped emit). This audit entry is retained for 365 days under the existing §11 audit_logs 365d retention contract (GDPR Art.30(1) records of processing activities). Even though the FL-2 cron deletes web_pages rows at 30d, the audit_logs entries persist for 365d, so the Art.30 record-keeping obligation is durably satisfied. The 365d audit_logs retention is an independent invariant from the 30d FL-2 retention; cron retention changes do not affect audit_logs retention by design.

failed_with_known_reason enum disclosure (LCC-M-02): Disclosure of the phase number <N> (∈ {0, 1, 2.5, 4, 5, 7.5}) is compatible with the GDPR Art.13/14 transparency obligation (disclosure of processing purposes) and does not trigger CWE-209 information-exposure redaction requirements as it is an operational diagnostic identifier. Phase numbers are publicly documented in CLAUDE.md / ADR-0007 / ADR-0009 and therefore do not constitute security-sensitive disclosure of internal architecture. See ADR-0009 Amendment 2 §A2.4 / §A2.6 for full legal reasoning.

§11.10.4 EU CRA Article 14 + GDPR Art.33 4-axis disclaimer / EU CRA Article 14 + GDPR Art.33 4 軸 disclaimer

JP: T4 closure deadline 2026-08-31 は EU CRA (Regulation (EU) 2024/2847) Article 14 (Reporting obligations of manufacturers) の effective date 2026-09-11 より 11日早く着地する。Worker SIGKILL / OOM / segfault によって発生する Pre-Return Pause failure-path race は operational reliability events であり、CRA Article 14 の "actively exploited vulnerability" / "severe incident" reporting trigger には該当せず、かつ GDPR Article 33 (72-hour breach notification) gate も発動しない。本 disclaimer は ADR-0019 §"CRA Article 14 disclaimer" lines 196-211 + DATA_RETENTION §11.8 §11.9.5 の 4 軸 disclaimer pattern を継承する:

  • External attacker surface 不在 / No external attacker surface: Worker SIGKILL / OOM / segfault は kernel-level operational fault に起因し、外部 untrusted input 経路から attacker が race condition を発火させる構造は不能。
  • Privilege escalation 不能 / No privilege escalation: race outcome は zombie state / orphan row のいずれも process boundary 内に閉じ、host-level capability 増加を招かない。FL-2 cleanup cron は同一 host OS user privilege 内で実行。
  • Information disclosure 不能 / No information disclosure: phase number <N> は publicly documented operational identifier であり、worker_pid は non-PII operational metadata。audit_logs.target_idtruncateTargetId SSOT 経路で 8 文字 truncation 適用 (Wave 5-endorsed canonical CWE-209 PII protection pattern と整合)。
  • DoS amplification 不能 / No DoS amplification: Pre-Return Pause failure-path race は単一 Worker 単位の operational fault であり、他 Worker / Queue / DB の degrade に連鎖しない (operable amplification factor 1.0)、self-contained fault。

したがって Plan v3 Track T4 は operational reliability ADR (ADR-0009 Amendment 2) として PR-Bα-3 absorbed scope (Worker Pre-Return Pause failure-path race) を closure するものであり、CRA Article 14 vulnerability disclosure 義務 / GDPR Art.33 72-hour breach notification gate のいずれも trigger しない。

EN: The T4 closure deadline 2026-08-31 lands 11 days before the EU CRA (Regulation (EU) 2024/2847) Article 14 (Reporting obligations of manufacturers) effective date 2026-09-11. The Pre-Return Pause failure-path race triggered by Worker SIGKILL / OOM / segfault is an operational reliability eventnot a CRA Article 14 "actively exploited vulnerability" / "severe incident" reporting trigger, and not a GDPR Article 33 72-hour breach notification trigger. This disclaimer inherits the 4-axis pattern from ADR-0019 §"CRA Article 14 disclaimer" lines 196-211 + DATA_RETENTION §11.8 §11.9.5:

  • No external attacker surface: Worker SIGKILL / OOM / segfault originates from kernel-level operational faults; no external untrusted input path can trigger the race condition.
  • No privilege escalation: Race outcomes (zombie state / orphan row) remain within process boundaries; no host-level capability gain. The FL-2 cleanup cron runs within the same host OS user privilege.
  • No information disclosure: Phase number <N> is a publicly documented operational identifier; worker_pid is non-PII operational metadata. audit_logs.target_id is truncated to 8 characters via the truncateTargetId SSOT path (aligned with the Wave 5-endorsed canonical CWE-209 PII-protection pattern).
  • No DoS amplification: The Pre-Return Pause failure-path race is a single-Worker operational fault and does not cascade to other Workers / Queues / DB (operable amplification factor 1.0); self-contained fault.

Therefore, Plan v3 Track T4 closes the PR-Bα-3 absorbed scope (Worker Pre-Return Pause failure-path race) as an operational reliability ADR (ADR-0009 Amendment 2), triggering neither the CRA Article 14 vulnerability disclosure obligation nor the GDPR Art.33 72-hour breach notification gate.

§11.10.5 LCC sign-off marker / LCC sign-off マーカー

Field / 項目Value / 値
LCC originator authority / 発行 originatorlegal-compliance-counsel (Plan v3 Track T4 face audit owner)
Sign-off date / sign-off 日付2026-05-04
Source LCC face audit anchor / 監査 anchor019df6b5-f36c-77dc-88a6-436fd16a7898
IO Decision V0 anchor / IO 判定 anchor019df6e2-9304-70d9-bd85-1751807f6d99 §3 Finding Registry (FIND-IMPL-T4-LCC-M-01 entry)
Wave 5 contract equivalence / 契約 同等性bilingual JP/EN landing required (Wave 5 LCC-endorsed contract per ` §"Canonical CWE-209 PII Protection Pattern (LCC-endorsed)")
Cross-ref / 相互参照(a) FIND-IMPL-T4-LCC-M-01 (this section); (b) FIND-PLAN-V3-T4-LCC-M-03 (Art.17 vs Art.5(1)(c) boundary, Phase 3 docs landing); (c) FIND-PLAN-V3-T4-LCC-H-01 (30d retention, LCC sign-off); (d) FIND-PLAN-V3-T4-LCC-H-02 (Art.30 audit invariant)
Authority preservation / 発行元 sign-offLCC blocker の独断降格禁止 (` IO authority boundary)。本 §11.10 landing は LCC originator authority によって signed-off ずみ、IO は本 sign-off の整合性 (5-check evidence) のみ verify する。

Cross-ref / 相互参照:

  • ADR-0019:196-211 (CRA Article 14 disclaimer source)
  • DATA_RETENTION §11 (audit_logs 365d retention contract)
  • DATA_RETENTION §11.8 §11.9.5 (4-axis disclaimer pattern precedent)
  • ADR-0009 Amendment 2 §A2.4 / §A2.5 / §A2.6 (Forces + Alternatives recording these classifications)
  • Prisma schema worker_pid doc-comment SSOT (packages/database/prisma/schema.prisma)
  • INV-DATA-DELETE-002 standing regression (gdpr-delete domain, 11-table cascade enforcement)
  • apps/mcp-server/src/services/audit-log.service.ts AUDIT_LOG_CONSTANTS.TARGET_ID_TRUNCATE_LENGTH SSOT (Wave 5 canonical CWE-209 truncation pattern)

§11.11 worker_job_lifecycle table privacy classification + 30d retention + Art.17 cascade contract (v0.5.0 Plan v3 Track T4 Phase 2 Z-a Wave 3, FIND-IMPL-T4-LCC-V1-M-01)

Scope / 適用範囲: 本サブセクションは Plan v3 Track T4 Phase 2 Z-a Wave 1 で導入される NEW Prisma model WorkerJobLifecycle (packages/database/prisma/schema.prisma:1975-1994、テーブル名 worker_job_lifecycle) について、3管轄 (GDPR / APPI / CCPA) での privacy classification、30d FL-2 cleanup cron 経路、GDPR Art.17 同期削除との cascade 契約を記録する。NEW table は recordWorkerSpawn / recordWorkerRelease で active row writes (apps/mcp-server/src/workers/page-analyze-worker.ts:1490 + :2641) されるため、Art.30 records of processing activities completeness を維持する目的で Wave 3 として独立 sub-section 化する (§11.10 は web_pages.worker_pid カラム単独の non-PII classification、本 §11.11 は同 PID + spawn time + nonce を結合 join key として保持する観測テーブル全体を対象とする補完関係)。LCC originator authority sign-off contract per FIND-IMPL-T4-LCC-V1-M-01 (deadline 2026-05-06 T+1d)、Wave 5-equivalent bilingual JP/EN landing。

This subsection records the privacy classification across GDPR / APPI / CCPA jurisdictions, the 30d FL-2 cleanup cron path, and the GDPR Art.17 cascade contract for the NEW Prisma model WorkerJobLifecycle (packages/database/prisma/schema.prisma:1975-1994, table worker_job_lifecycle) introduced in Plan v3 Track T4 Phase 2 Z-a Wave 1. The NEW table receives active row writes via recordWorkerSpawn / recordWorkerRelease (apps/mcp-server/src/workers/page-analyze-worker.ts:1490 + :2641); a dedicated sub-section is required as Wave 3 to maintain Art.30 records of processing activities completeness. (§11.10 covers the web_pages.worker_pid column standalone non-PII classification; §11.11 covers the entire observability table that retains the same PID + spawn time + nonce as a composite join key — complementary relationship.) LCC originator authority sign-off contract per FIND-IMPL-T4-LCC-V1-M-01 (deadline 2026-05-06 T+1d), Wave 5-equivalent bilingual JP/EN landing.

§11.11.1 利用目的 / Purpose

JP: worker_job_lifecycle テーブルは Plan v3 Track T4 Sub-C (cross-PID-reuse defense) の 観測 (observability) 目的専用の supplementary table であり、以下 2 つの operational 不変条件のみに利用される:

  • Sub-C orphan backfill atomicity invariant: Worker SIGKILL / OOM / segfault 時に supervisor backfill mechanism が web_pages row を failed_with_known_reason='worker_restart_during_inflight_phase_<N>' 状態へ確実に classify するため、spawn / release / restart / dispose イベントを観測する。
  • Cross-PID-reuse defense join key: Linux kernel が PID 番号を reuse する場合 (long-running host で発生)、worker_pid 単独では真のオーナー Worker を一意に識別できない。worker_pid + worker_spawn_time の同時等値 join key により、PID-reuse false positive を構造的に排除する (INV-WORKER-PID-IDENTITY-005 Sub-C 契約)。nonce field は ADR-0011 lock semantic (nonce-based UUID lock) との observability link であり、lock authority 自体は本テーブルに依存しない。

本テーブルは web_pages parent row 経由の operational lifecycle observation のみを目的とし、user-facing data subject ID、user content、user behavior 等とは構造的に紐付かない (§11.11.2 で詳述)。

EN: The worker_job_lifecycle table is a supplementary observability-only table for Plan v3 Track T4 Sub-C (cross-PID-reuse defense), serving exactly two operational invariants:

  • Sub-C orphan backfill atomicity invariant: Observes spawn / release / restart / dispose events so the supervisor backfill mechanism can reliably classify web_pages rows into the failed_with_known_reason='worker_restart_during_inflight_phase_<N>' terminal state on Worker SIGKILL / OOM / segfault.
  • Cross-PID-reuse defense join key: When the Linux kernel reuses PID numbers (common on long-running hosts), worker_pid alone cannot uniquely identify the true owning Worker. The composite worker_pid + worker_spawn_time equality join key structurally rejects PID-reuse false positives (INV-WORKER-PID-IDENTITY-005 Sub-C contract). The nonce field provides an observability link to the ADR-0011 lock semantic (nonce-based UUID lock); lock authority itself does NOT depend on this table.

The table targets operational lifecycle observation only via the web_pages parent row and is structurally unlinked to user-facing data subject IDs, user content, or user behavior (detailed in §11.11.2).

§11.11.2 3管轄 privacy classification / Privacy Classification per Jurisdiction

JP: worker_job_lifecycle テーブル全体および全カラム (worker_pid, worker_spawn_time, nonce, event_type, event_at, worker_type, web_page_id) は 3管轄すべてで non-PII (個人情報に該当しない) と分類される:

  • [EU] GDPR Art.4(1) "personal data" 定義: "any information relating to an identified or identifiable natural person". worker_pid (Linux process identifier) + worker_spawn_time (process boot timestamp) + nonce (server-generated UUID) はいずれも自然人 (natural person) と関連を持たない server-side ephemeral process identifiers。EDPB Guidelines 4/2019 §3 が確認する通り、data subject に紐付かない operational system metadata は Art.4(1) scope 外 (§11.10.1 の worker_pid 単独 classification と同一根拠を table-wide に適用)。web_page_id は parent web_pages row への FK であって、web_pages row 自体が user-facing personal content を持たない場合 (developer test analysis 等) は完全に non-PII; user-originated content を含む場合でも worker_job_lifecycle row 自体は process metadata のみで personal data の derivative ではない。
  • [JP] 個人情報の保護に関する法律 第2条第1項 "個人情報" 定義: 「生存する個人に関する情報であって、特定の個人を識別することができるもの」。process identifier + nonce + spawn timestamp は OS / server-side runtime に閉じた識別子であり、生存する個人を識別しない。第26条 (個人情報取扱事業者の義務) における "個人情報データベース" 該当性も否定される (本テーブル単独で個人検索可能性なし)。
  • [US] CCPA/CPRA Cal. Civ. Code §1798.140(o)(1) "personal information" 定義: "information that identifies, relates to, describes, is reasonably capable of being associated with, or could reasonably be linked, directly or indirectly, with a particular consumer or household". process identifiers は particular consumer / household に reasonably linked される情報経路を持たない (§1798.140(o)(2) "publicly available information" exception への該当判定すら不要 — そもそも consumer-linkable category に該当しない)。

特性 / Characteristics: 各 row は Worker spawn 時に populate、planned exit / restart / dispose イベント時に追加 row emit。maxJobsBeforeRestart=1 既定 (CLAUDE.md Worker 既定 + ADR-0009 / ADR-0011) により単一 Worker process の lifetime ≤ 約1時間。SSOT cross-ref: Prisma schema doc-comment (packages/database/prisma/schema.prisma WorkerJobLifecycle model header lines 1955-1973) に同等の non-PII classification 言明 + Art.17 Cascade + 30d retention LCC H-01 sign-off marker を併記済。先行 §11.10 worker_pid カラム classification + DATA_RETENTION §11 worker_supervisor_restart audit_logs entry の pid field (details JSON) と完全に同一の non-PII semantic で運用、repo 内一貫性を保つ。

EN: The entire worker_job_lifecycle table and all columns (worker_pid, worker_spawn_time, nonce, event_type, event_at, worker_type, web_page_id) are classified as non-PII (not personal information) under all three jurisdictions:

  • [EU] GDPR Art.4(1) "personal data" definition: "any information relating to an identified or identifiable natural person". worker_pid (Linux process identifier) + worker_spawn_time (process boot timestamp) + nonce (server-generated UUID) are server-side ephemeral process identifiers, none of which relate to a natural person. EDPB Guidelines 4/2019 §3 confirms that operational system metadata not linked to a data subject falls outside Art.4(1) scope (the table-wide application reuses the same rationale as §11.10.1's worker_pid standalone classification). The web_page_id is an FK to the parent web_pages row; when the web_pages row carries no user-facing personal content (e.g., developer test analyses), the worker_job_lifecycle row is entirely non-PII; even when the web_pages parent contains user-originated content, the worker_job_lifecycle row itself holds only process metadata and is not a derivative of personal data.
  • [JP] APPI 第2条第1項 "personal information" definition: "information about a living individual that can identify a specific individual". A process identifier + nonce + spawn timestamp are identifiers confined to the OS / server-side runtime and do not identify any living individual. The table also fails the 第26条 "personal information database" test (no by-individual searchability via this table alone).
  • [US] CCPA/CPRA Cal. Civ. Code §1798.140(o)(1) "personal information" definition: "information that identifies, relates to, describes, is reasonably capable of being associated with, or could reasonably be linked, directly or indirectly, with a particular consumer or household". Process identifiers have no reasonable linkage path to any particular consumer or household (no need to even invoke the §1798.140(o)(2) "publicly available information" exception — the data does not fall within any consumer-linkable category to begin with).

Characteristics: A row is populated at Worker spawn; additional rows are emitted on planned exit / restart / dispose events. Under the default maxJobsBeforeRestart=1 (CLAUDE.md Worker defaults + ADR-0009 / ADR-0011), a single Worker process lifetime is ≤ ~1 hour. SSOT cross-ref: the Prisma schema doc-comment (packages/database/prisma/schema.prisma WorkerJobLifecycle model header, lines 1955-1973) co-records the equivalent non-PII classification statement + Art.17 Cascade + 30d retention LCC H-01 sign-off marker. The semantic is identical to the precedent §11.10 worker_pid column classification and the DATA_RETENTION §11 worker_supervisor_restart audit_logs entry's pid field (details JSON), preserving repo-internal consistency.

§11.11.3 30d retention + FL-2 cleanup cron contract / 30日保持 + FL-2 cleanup cron 契約

JP: worker_job_lifecycle テーブルは apps/mcp-server/scripts/cleanup-orphaned-web-pages.ts FL-2 cleanup cron が 30日保持期間 (LCC H-01 IO-accepted retention) で operational pruning を実施する (詳細実装: findEligibleRows() lines 87-129 + deleteEligibleRows() lines 132-165)。pruning eligibility criteria (両条件 AND):

  1. event_at < now() - INTERVAL '<retentionDays> days' (default 30d、getOrphanRetentionDays() SSOT 経由で env-tunable)
  2. paired web_pages.failed_with_known_reason IS NOT NULL OR analysis_phase_status = 'completed' (terminally classified rows のみ pruning 対象、active in-flight row は構造的保護)

pruning 実行は --confirm flag 必須 (default --dry-run)、accidental production run を防止。retention 値選定根拠 (LCC H-01 確定値): operator が orphan event を 30d 遡って forensic 調査でき、weekly P0 incident retro / monthly orphan-rate KPI review に十分な期間でありながら、GDPR Art.5(1)(c) "necessary" 境界内に収まる (§11.10.3 と同一根拠)。

Art.30 record-keeping invariant (LCC H-02 inheritance): FL-2 cron は run 1 回ごとに summary audit_logs.action='worker_job_lifecycle_cleanup_run' entry 1 件system:cleanup-orphaned-web-pages-cron actor + targetType='worker_job_lifecycle' + targetId=null (per-row PII truncation 不要、テーブル単位 summary record) + details={deleted_count, retention_days, run_at} 形式で発火する (lines 149-161)。本 audit entry は 既存 §11 audit_logs 365d retention 契約 (GDPR Art.30(1) records of processing activities) に従って 365d 保持。FL-2 cron が worker_job_lifecycle row を 30d で削除しても audit_logs summary は 365d 残存し、Art.30 record-keeping obligation は永続的に充足。worker_job_lifecycle の 30d retention と audit_logs の 365d retention は 独立した不変条件 (cron retention 変更が audit_logs retention に影響しない設計)。per-row audit emit は不要 (§11.10.3 で defined paired audit emit が supervisor backfill 時点で既に発火、本 cron は redundant observability cleanup のみ)。

EN: The worker_job_lifecycle table is operationally pruned by the apps/mcp-server/scripts/cleanup-orphaned-web-pages.ts FL-2 cleanup cron at 30d retention (LCC H-01 IO-accepted retention) (implementation: findEligibleRows() lines 87-129 + deleteEligibleRows() lines 132-165). Pruning eligibility criteria (both AND):

  1. event_at < now() - INTERVAL '<retentionDays> days' (default 30d, env-tunable via the getOrphanRetentionDays() SSOT)
  2. paired web_pages.failed_with_known_reason IS NOT NULL OR analysis_phase_status = 'completed' (only terminally classified rows are eligible; active in-flight rows are structurally protected)

Pruning requires the --confirm flag (default is --dry-run), preventing accidental production runs. The retention rationale (LCC H-01 confirmed value): operators can forensically investigate orphan events 30 days back, sufficient for weekly P0 incident retros and monthly orphan-rate KPI reviews, while staying within the GDPR Art.5(1)(c) "necessary" bound (same rationale as §11.10.3).

Art.30 record-keeping invariant (LCC H-02 inheritance): The FL-2 cron emits exactly one summary audit_logs.action='worker_job_lifecycle_cleanup_run' entry per run with system:cleanup-orphaned-web-pages-cron as actor + targetType='worker_job_lifecycle' + targetId=null (per-row PII truncation unnecessary; this is a table-level summary) + details={deleted_count, retention_days, run_at} (lines 149-161). This audit entry is retained for 365 days under the existing §11 audit_logs 365d retention contract (GDPR Art.30(1) records of processing activities). Even though the FL-2 cron deletes worker_job_lifecycle rows at 30d, the audit_logs summary persists for 365d, durably satisfying the Art.30 record-keeping obligation. The 30d worker_job_lifecycle retention and the 365d audit_logs retention are independent invariants by design (cron retention changes do not affect audit_logs retention). Per-row audit emit is unnecessary — the paired audit emit defined in §11.10.3 fires at supervisor backfill time; this cron only performs redundant observability cleanup.

§11.11.4 GDPR Art.17 cascade contract / GDPR Art.17 cascade 契約

JP: worker_job_lifecycle テーブルの web_page_id FK は onDelete: Cascade で declared (packages/database/prisma/schema.prisma:1989 webPage WebPage @relation(fields: [webPageId], references: [id], onDelete: Cascade))。GDPR Art.17 (Right to Erasure) 経路で data.delete(webPageId) が実行される際、PostgreSQL 側で Prisma-native CASCADE delete が発火し、parent web_pages row 削除と同時に該当 worker_job_lifecycle rows も削除される。

3管轄 boundary 整合 / 3-jurisdiction boundary alignment:

経路 / Path担当 / ResponsibilitySLA / 契約対象範囲 / Scope
Art.17 Right to Erasure (synchronous)data.delete MCP tool + Prisma-native CASCADE (FK onDelete: Cascade)"without undue delay" (ICO 1-month guidance) — synchronous deletionData subject 由来の erasure request に対し、parent web_pages row 削除と同時に同期的に削除。allowlist 列挙 (PAGE_RELATED_EMBEDDING_TABLES / PAGE_RELATED_PATTERN_TABLES) は 不要 — Prisma-native CASCADE が PostgreSQL 側で確実に発火するため、explicit allowlist enumeration redundant。
Art.5(1)(c)/(e) Data Minimisation (operational cleanup)FL-2 cleanup cron (30d retention)30d retention boundary、cron tick 単位で実施 (operational SLA、not user-facing)Data subject erasure request が無い residual rows のみ。Art.17 削除済みの parent web_pages row に対しては既に CASCADE で削除済のため対象外。

INV-DATA-DELETE-002 standing regression coverage 拡張要請 (UNBLOCK-V2-05b): 現状の INV-DATA-DELETE-002 contract test (apps/mcp-server/tests/regression/standing/gdpr-delete/) は 11-table cascade を contract レベルで検証しているが、Wave 1 で追加された worker_job_lifecycle table の cascade coverage は 未拡張 (Wave 3 dispatch scope 内では本 docs subsection landing が closure 範囲、test 拡張は test-qa-engineer 並列 dispatch UNBLOCK-V2-05b の owner)。docs 側では cascade contract を Prisma schema declaration ベースで明文化し、test landing は UNBLOCK-V2-05b で independent に実施。

EN: The worker_job_lifecycle table's web_page_id FK is declared with onDelete: Cascade (packages/database/prisma/schema.prisma:1989 webPage WebPage @relation(fields: [webPageId], references: [id], onDelete: Cascade)). When data.delete(webPageId) runs on the GDPR Art.17 (Right to Erasure) path, Prisma-native CASCADE delete fires at the PostgreSQL level — parent web_pages row deletion synchronously deletes the corresponding worker_job_lifecycle rows.

3-jurisdiction boundary alignment:

PathResponsibilitySLAScope
Art.17 Right to Erasure (synchronous)data.delete MCP tool + Prisma-native CASCADE (FK onDelete: Cascade)"without undue delay" (ICO 1-month guidance) — synchronous deletionFor data-subject-originated erasure requests, the table is deleted synchronously together with the parent web_pages row. Allowlist enumeration (PAGE_RELATED_EMBEDDING_TABLES / PAGE_RELATED_PATTERN_TABLES) is unnecessary — Prisma-native CASCADE fires reliably at the PostgreSQL level, so explicit allowlist enumeration is redundant.
Art.5(1)(c)/(e) Data Minimisation (operational cleanup)FL-2 cleanup cron (30d retention)30d retention boundary; executed per cron tick (operational SLA)Only residual rows where no data-subject erasure request was made. Rows whose parent web_pages row was Art.17-deleted are already CASCADE-removed and out of scope.

INV-DATA-DELETE-002 standing regression coverage extension request (UNBLOCK-V2-05b): The current INV-DATA-DELETE-002 contract tests (apps/mcp-server/tests/regression/standing/gdpr-delete/) verify the 11-table cascade at the contract level; cascade coverage for the Wave 1-introduced worker_job_lifecycle table is not yet extended (within the Wave 3 dispatch scope, this docs subsection landing is the closure boundary; test extension is owned by the parallel test-qa-engineer dispatch UNBLOCK-V2-05b). The docs side codifies the cascade contract via the Prisma schema declaration; the test landing is performed independently under UNBLOCK-V2-05b.

§11.11.5 LCC sign-off marker / LCC sign-off マーカー

Field / 項目Value / 値
LCC originator authority / 発行 originatorlegal-compliance-counsel (Plan v3 Track T4 Phase 2 Z-a Wave 3 dispatch owner, Impl Audit V1 face audit owner)
Sign-off date / sign-off 日付2026-05-05
Jurisdictions covered / 対象管轄[JP] APPI 第2条第1項 + 第26条 / [US] CCPA/CPRA Cal. Civ. Code §1798.140(o)(1) / [EU] GDPR Art.4(1) + Art.5(1)(c)/(e) + Art.17 + Art.30(1)(b)(c) + Art.33
Source LCC face audit anchor / 監査 anchor019df7e1-516b (LCC Impl Audit V1, FIND-IMPL-T4-LCC-V1-M-01)
IO Decision V2.1 anchor / IO 判定 anchor019df7ec-ce8d (UNBLOCK-V2-05a partial closure dispatch entry)
Wave anchors / Wave 関連Wave 1 pipeline-engineer decision 019df795-5ae3 (NEW Prisma model + active row writes); Wave 2 SEC-H-01 decision 019df7ab-2f5a (cascade boundary security review)
T+1d deadline / T+1d 期限2026-05-06 (per User permanent directive 2026-05-04, feedback_deadline_one_day.md)
EU CRA Art.14 cross-link / EU CRA 相互参照T4 closure (deadline 2026-08-31) は EU CRA (Regulation (EU) 2024/2847) Article 14 effective date 2026-09-11 より 11日早く着地 (§11.10.4 4-axis disclaimer historical reference preserved; worker_job_lifecycle operational events も同 4 axes に該当しない)。
Wave 5 contract equivalence / 契約 同等性bilingual JP/EN landing required (Wave 5 LCC-endorsed contract per ` §"Canonical CWE-209 PII Protection Pattern (LCC-endorsed)")
Cross-ref / 相互参照(a) FIND-IMPL-T4-LCC-V1-M-01 (this section); (b) §11.10 sibling sub-section (worker_pid column standalone classification, §11.10.5 collapsed reference preserved); (c) UNBLOCK-V2-05b (test-qa-engineer parallel dispatch, INV-DATA-DELETE-002 cascade test extension)
Authority preservation / 発行元 sign-offLCC blocker の独断降格禁止 (` IO authority boundary)。本 §11.11 landing は LCC originator authority によって signed-off ずみ、IO は本 sign-off の整合性 (5-check evidence) のみ verify する。

Cross-ref / 相互参照:

  • T4 design.md §6.3 + §7.1 (implementation contract、Sub-C cross-PID-reuse defense)
  • ADR-0009 Amendment 2 §A2.4 / §A2.5 / §A2.6 (Forces + Alternatives recording these classifications)
  • ADR-0011 (Worker Dual-run Lock semantics、nonce-based UUID lock; worker_job_lifecycle.nonce は observability link only)
  • DATA_RETENTION §11 (audit_logs 365d retention contract)
  • DATA_RETENTION §11.10 (sibling sub-section、worker_pid column standalone non-PII classification)
  • Prisma schema WorkerJobLifecycle model doc-comment SSOT (packages/database/prisma/schema.prisma:1955-1994)
  • INV-DATA-DELETE-002 standing regression (gdpr-delete domain、cascade enforcement; UNBLOCK-V2-05b で worker_job_lifecycle cascade coverage 拡張)
  • INV-WORKER-PID-IDENTITY-005 Sub-C (cross-PID-reuse defense join key contract)
  • apps/mcp-server/scripts/cleanup-orphaned-web-pages.ts (FL-2 cleanup cron implementation, lines 1-200)
  • apps/mcp-server/src/workers/page-analyze-worker.ts:1490 + :2641 (active row write call sites)
  • apps/mcp-server/src/services/audit-log.service.ts AUDIT_LOG_CONSTANTS.TARGET_ID_TRUNCATE_LENGTH SSOT (Wave 5 canonical CWE-209 truncation pattern, table-level summary では targetId=null 適用なし)

§11.12 Env Var + Audit Action Deprecation Table (Plan v4.5 V2 NEW-U-13、BREAKPAD scope correction)

Scope / 適用範囲:

JP: 本サブセクションは Plan v4.5 V2 NEW-U-13 (IO V1 anchor 019e3822-1438-75a7-9f6e-8b8a234668e2 CONDITIONAL ruling) で確定した BREAKPAD dead env var + audit_logs action deprecation を T1 SSOT として記録する。Plan v3 Wave 3 Task #41 で promise した Google Breakpad → 実装で採用された Node.js process.report (BSD-3-Clause、AGPL-3.0-only compatible) という scope change により、REFTRIX_BREAKPAD_UPLOAD_URL env var と audit_logs.worker_crash_dump_uploaded action は 意味喪失 (semantic dead) 状態となった (emit rate ≈ 0 / upload endpoint 概念消滅)。Plan v4.5 V2 で DEPRECATED に格下げ、1-cycle T+30d 2026-06-20 backward compat 期間後 Plan v4.6 で formal removal。AGPL-3.0 §5(a)/(b) "Modified versions must carry prominent notices" contract により 3-CHANGELOG bilingual + 本 §11.12 で retroactively disclose、INV-AGPL-MODIFICATION-NOTICE-001 standing test で structural enforce。

EN: This subsection records the BREAKPAD dead env var + audit_logs action deprecation confirmed by Plan v4.5 V2 NEW-U-13 (IO V1 anchor 019e3822-1438-75a7-9f6e-8b8a234668e2 CONDITIONAL ruling) as the T1 SSOT. The scope change in Plan v3 Wave 3 Task #41 — promising Google Breakpad adoption but implementing Node.js process.report (BSD-3-Clause, AGPL-3.0-only compatible) — left REFTRIX_BREAKPAD_UPLOAD_URL env var and audit_logs.worker_crash_dump_uploaded action semantically dead (emit rate ≈ 0 / upload endpoint concept eliminated). Plan v4.5 V2 downgrades both to DEPRECATED, with 1-cycle T+30d 2026-06-20 backward compat period before formal removal in Plan v4.6. Per AGPL-3.0 §5(a)/(b) "Modified versions must carry prominent notices" contract, the deprecation is retroactively disclosed via the 3-CHANGELOG bilingual entries and the present §11.12, structurally enforced by the INV-AGPL-MODIFICATION-NOTICE-001 standing test.

§11.12.1 Deprecation table / 廃止予定 table

ArtifactTypePre-V2 documented inDeprecation Status (V2)T+30d removal deadlinePlan v4.6 formal removal target
REFTRIX_BREAKPAD_UPLOAD_URLenv varWave 3 retro /apps/mcp-server/DATA_RETENTION.md` §11.11 Wave 3 sub-sectionDEPRECATED Plan v4.5 V2 — 1-cycle backward compat、set されても警告のみ、削除 trigger なし / Backward-compat period only; setting it raises warning, triggers no removal2026-06-20 (T+30d JST)Plan v4.6 で env var loader + Zod schema + 関連 doc references 全削除
audit_logs.worker_crash_dump_uploaded actionaudit_logs action literalDATA_RETENTION §11.11.5 LCC sign-off marker / ` Wave 3 retroDEPRECATED Plan v4.5 V2 — 365d retention 維持 (historical entries 用)、新規 emit 禁止 / 365d retention preserved for historical entries; new emits forbidden2026-06-20 (T+30d JST、新規 emit 禁止 trigger) + 365d 後 historical 削除Plan v4.6 で SSOT 定数 AUDIT_ACTION_WORKER_CRASH_DUMP_UPLOADED + 全 callsite + ADR-0021 references 全削除

§11.12.2 GDPR Art.30 records of processing activities 整合性 / GDPR Art.30 alignment

JP: audit_logs.worker_crash_dump_uploaded action は emit rate ≈ 0 (semantic dead state) であるが、DEPRECATED 状態でも 365d retention 契約は継承 (GDPR Art.30(1)(b)(c) records of processing activities completeness 要求)。historical entries が DB に残存する可能性がある場合 (Plan v3 Wave 3 当時の試験運用等)、365d retention 期間内は audit trail として保持義務あり。新規 emit は Plan v4.5 V2 PR3-Track4 landing 後禁止 (DEPRECATED JSDoc markup + CI sweep で structural enforce)、Plan v4.6 で SSOT 定数 + callsite 全削除時点で historical entries が 365d 超過していることを確認後、formal removal 可能。

EN: The audit_logs.worker_crash_dump_uploaded action is in a semantically dead state (emit rate ≈ 0), but the 365d retention contract is inherited even under DEPRECATED status (per GDPR Art.30(1)(b)(c) records of processing activities completeness requirement). If historical entries may remain in the DB (e.g., from Plan v3 Wave 3 trial runs), they MUST be preserved as audit trail within the 365d retention window. New emits are forbidden after Plan v4.5 V2 PR3-Track4 landing (structurally enforced by DEPRECATED JSDoc markup + CI sweep). Plan v4.6's formal removal of the SSOT constant + all callsites is permitted only after confirming that historical entries have exceeded the 365d retention window.

§11.12.3 AGPL-3.0 §5(a)/(b) modification notice contract / AGPL-3.0 §5(a)/(b) 改変通知契約

JP: AGPL-3.0 §5(a)/(b) "If the work is interactive, normally prints, displays, or otherwise communicates information regarding its authors or its conditions, it must continue to do so in modified versions" + "Modified versions must carry prominent notices stating that you modified it" により、Google Breakpad → Node.js process.report scope change および本 BREAKPAD deprecation の双方が modification notice 対象。Plan v4.5 V2 PR3-Track4 内で 3-CHANGELOG bilingual entry に以下を必須記載:

  • root CHANGELOG.md: "Plan v3 Wave 3 Task #41 scope correction — Google Breakpad → Node.js process.report implementation style change + BREAKPAD dead env var deprecation retroactively disclosed (T+30d 2026-06-20 removal)" (bilingual)
  • 同上 + Plan v4.5 V2 anchor + IO V1 anchor019e3822-1438-75a7-9f6e-8b8a234668e2` cross-reference
  • apps/mcp-server/CHANGELOG.md: 同上 + per-file LoC delta (Wave 3 当時 implementation) + DATA_RETENTION §11.12 cross-reference

EN: Under AGPL-3.0 §5(a)/(b) — "If the work is interactive, normally prints, displays, or otherwise communicates information regarding its authors or its conditions, it must continue to do so in modified versions" and "Modified versions must carry prominent notices stating that you modified it" — both the Google Breakpad → Node.js process.report scope change AND the present BREAKPAD deprecation fall under the modification notice obligation. The 3-CHANGELOG bilingual entries under Plan v4.5 V2 PR3-Track4 MUST include the entries enumerated above.

§11.12.4 INV-AGPL-MODIFICATION-NOTICE-001 mandate scope expansion / INV mandate scope 拡張

JP: Plan v4.5 V1 既存 INV-AGPL-MODIFICATION-NOTICE-001 standing test の mandate scope を V2 で expand:

  1. V1 既存 mandate: 3-CHANGELOG bilingual entry に "Wave 3 Task #41 scope correction (Google Breakpad → process.report)" 記載必須
  2. V2 expand (NEW-U-13): 3-CHANGELOG bilingual entry に BREAKPAD deprecation entry 記載必須 — regex /REFTRIX_BREAKPAD_UPLOAD_URL.*DEPRECATED.*T\+30d 2026-06-20/ を 3 paths (CHANGELOG.md / /apps/mcp-server/CHANGELOG.md`) すべてで match 確認

配置: apps/mcp-server/tests/regression/standing/license-compliance/inv-agpl-modification-notice-001.test.ts (V1 既定 file、V2 で test cases expand)。

EN: Plan v4.5 V2 expands the mandate scope of the existing INV-AGPL-MODIFICATION-NOTICE-001 standing test:

  1. V1 existing mandate: 3-CHANGELOG bilingual entry MUST contain "Wave 3 Task #41 scope correction (Google Breakpad → process.report)"
  2. V2 expansion (NEW-U-13): 3-CHANGELOG bilingual entry MUST additionally contain a BREAKPAD deprecation entry — regex /REFTRIX_BREAKPAD_UPLOAD_URL.*DEPRECATED.*T\+30d 2026-06-20/ MUST match in all 3 paths (CHANGELOG.md / /apps/mcp-server/CHANGELOG.md`)

Location: apps/mcp-server/tests/regression/standing/license-compliance/inv-agpl-modification-notice-001.test.ts (existing V1 file, test cases expanded under V2).

§11.12.5 LCC sign-off marker / LCC sign-off マーカー

Field / 項目Value / 値
LCC originator authority / 発行 originatorlegal-compliance-counsel (Plan v4.5 V1 LCC docs-only confirmation owner — anchor 019e381b-b09f-73ec-9ead-c5441410e823、PASS with 0 NEW findings)
Sign-off date / sign-off 日付2026-05-18
Jurisdictions covered / 対象管轄[EU] AGPL-3.0 §5(a)/(b) modification notice + GDPR Art.30(1)(b)(c) records of processing activities completeness
Plan v4.5 V2 NEW-U-13 source anchorSEC re-audit anchor 019e381b-a843-7598-81de-509c0c1e3553 (BREAKPAD env var dead 指摘)
IO V1 anchor019e3822-1438-75a7-9f6e-8b8a234668e2 (CONDITIONAL with 6 NEW unblock conditions、NEW-U-13 included)
T+1d deadline / T+1d 期限2026-05-19 (per User permanent directive ⑥ T+1d 厳守、feedback_deadline_one_day.md)
T+30d removal deadline / T+30d 廃止期限2026-06-20 (relevant date = T0 2026-05-21 + 30d 1-cycle backward-compat window; IO Impl Decision V0 anchor 019e4694-24fa U-2 closure; 1-cycle backward compat 期間後 Plan v4.6 で formal removal)
Cross-ref / 相互参照(a) ADR-0036 §D5.2.1 BREAKPAD dead env var + audit_logs action deprecation section; (b) ADR-0021 (Worker Crash Dump Observability、Option α 採用根拠); (c) Plan v4.5 V2 §T4.5 + §0 NEW-U-13 row; (d) INV-AGPL-MODIFICATION-NOTICE-001 standing test (V2 expanded mandate)
Authority preservation / 発行元 sign-offLCC originator authority によって signed-off、IO は本 sign-off の整合性 (cross-reference completeness + DEPRECATION schedule consistency) のみ verify する。

Cross-ref / 相互参照:

  • Plan v4.5 V2 §0 NEW-U-13 row / §T4.5 D5.2.1 BREAKPAD deprecation section
  • ADR-0036 §D5.2.1 (BREAKPAD dead env var + audit_logs action deprecation, V2 新規 per NEW-U-13)
  • ADR-0021 (Worker Crash Dump Observability、Option α 採用根拠 = Node.js process.report BSD-3-Clause)
  • Plan v3 Wave 3 Task #41 (Google Breakpad promise → Node.js process.report 実装 scope change の origin)
  • IO Plan Decision V1 anchor 019e3822-1438-75a7-9f6e-8b8a234668e2 (NEW-U-13 included)
  • SEC re-audit anchor 019e381b-a843-7598-81de-509c0c1e3553 (NEW-U-13 source、BREAKPAD env var dead 指摘)
  • ` §"Canonical CWE-209 PII Protection Pattern (LCC-endorsed)" + §"Worker actor naming SSOT (Plan v4.3 PR-M / ADR-0035)"
  • INV-AGPL-MODIFICATION-NOTICE-001 standing regression (license-compliance domain、V2 mandate scope expanded)
  • 3-CHANGELOG paths: CHANGELOG.md / /apps/mcp-server/CHANGELOG.md` (bilingual JP/EN BREAKPAD deprecation entry mandatory in PR3-Track4)

§11.13 Per-Job Fork-Only Model Migration audit_logs actions (Plan v4.5 PR3 Track 2+4, U-4 / LCC-V2-01 closure) / Per-job fork-only model migration の audit_logs アクション

§11.13.1 6 NEW audit_logs actions table / 6 個の NEW audit_logs アクション表

すべての action は 365d retention (§11 共通 audit_logs 契約 + GDPR Art.30(1) records of processing activities 継承)、targetIdtruncateAuditTargetId() (apps/mcp-server/src/audit/audit-actions.ts SSOT、Wave 5 LCC canonical CWE-209 PII protection pattern anchor 019df7ab-2f5a) で PII-truncate、details JSON は raw payload を含まない (PII minimisation 保全)。actor literal は SSOT 定数 import 経由 (hardcoded literal 禁止、INV-AUDIT-EMIT-SSOT-IMPORT-001 AST sweep で structural enforce、` §"Worker actor naming SSOT" 参照)。

All actions inherit 365d retention (the §11 common audit_logs contract + GDPR Art.30(1) records of processing activities), truncate targetId via truncateAuditTargetId() (SSOT in apps/mcp-server/src/audit/audit-actions.ts, Wave 5 LCC canonical CWE-209 PII protection pattern anchor 019df7ab-2f5a), and carry no raw payload in details JSON (PII minimisation preserved). The actor literal is imported via the SSOT constant (hardcoded literals forbidden, structurally enforced by the INV-AUDIT-EMIT-SSOT-IMPORT-001 AST sweep; see ` §"Worker actor naming SSOT").

Action (T1 SSOT)Actor (SSOT 定数 / SSOT constant)targetTyperetentionoutcome / branchdetails (no raw payload)
worker_config_legacy_env_var_detectedsystem:worker-config-validator (AUDIT_ACTOR_WORKER_CONFIG_VALIDATOR)worker365日 / 365 daysdetected (legacy flag 同時 set)legacyFlag, newFlag, resolvedValue
worker_sub_child_spawn_rate_limit_violatedsystem:embedding-backfill-worker (getWorkerActorName("embedding-backfill"))worker365日 / 365 daysrate_limited (fail-closed, CWE-770)minIntervalMs, elapsedMs
worker_per_job_fork_lock_acquiredsystem:embedding-backfill-worker (getWorkerActorName("embedding-backfill"))worker365日 / 365 dayssuccess (atomic acquire)bootEpoch, nonce (server-generated UUID)
worker_per_job_fork_lock_race_lostsystem:embedding-backfill-worker (getWorkerActorName("embedding-backfill"))worker365日 / 365 daysrace_lost (fail-closed, SET NX nil)lockKey (truncated)
worker_lock_service_unreachablesystem:embedding-backfill-worker (getWorkerActorName("embedding-backfill"))worker365日 / 365 daysredis_unreachable (fail-open, SEC-M-3)reason (connection_refused | timeout)
worker_lua_script_reloadsystem:embedding-backfill-worker (getWorkerActorName("embedding-backfill"))worker365日 / 365 dayssuccess (transparent NOSCRIPT recovery)scriptSha (truncated)

SSOT cross-ref / SSOT 相互参照: 6 action literals の正典は apps/mcp-server/src/audit/audit-actions.ts (AUDIT_ACTION_WORKER_CONFIG_LEGACY_ENV_VAR_DETECTED / _SUB_CHILD_SPAWN_RATE_LIMIT_VIOLATED / _PER_JOB_FORK_LOCK_ACQUIRED / _PER_JOB_FORK_LOCK_RACE_LOST / _LOCK_SERVICE_UNREACHABLE / _LUA_SCRIPT_RELOAD)。本表と T1 SSOT が乖離した場合は T1 が勝つ (Source of Truth Hierarchy、CLAUDE.md §"真実の源泉")。

The canonical source of the 6 action literals is apps/mcp-server/src/audit/audit-actions.ts. If this table and the T1 SSOT diverge, T1 wins (Source of Truth Hierarchy, CLAUDE.md §"真実の源泉").

§11.13.2 Per-job sub-child crash capture = Node.js process.report (NOT Google Breakpad) / per-job sub-child クラッシュ採取は Node.js process.report

JP: per-job fork-only model では各 job ごとに sub-child process を fork → exit(0) で OS が全メモリを回収する。sub-child の異常終了 (SIGABRT 等) 時の crash capture は Node.js 組込 process.report (BSD-3-Clause、AGPL-3.0-only compatible、ADR-0021 Option α) を使用し、Google Breakpad は使用しない。これは §11.12 で記録済の Plan v3 Wave 3 Task #41 scope correction (Google Breakpad promise → Node.js process.report 実装) と同一の reframe であり、本 PR3 per-job fork-only migration でも process.report 経路を継承する。REFTRIX_BREAKPAD_UPLOAD_URL env var + audit_logs.worker_crash_dump_uploaded action は §11.12 で DEPRECATED (T+30d 2026-06-20 backward compat、Plan v4.6 formal removal) 済であり、本 §11.13 の 6 actions は Breakpad upload 経路に依存しない。LCC-V1-02 / LCC-V2-02 reframe sign-off。

EN: In the per-job fork-only model, each job forks a sub-child process and exit(0) lets the OS reclaim all memory. Crash capture on abnormal sub-child termination (e.g., SIGABRT) uses Node.js built-in process.report (BSD-3-Clause, AGPL-3.0-only compatible, ADR-0021 Option α) and does NOT use Google Breakpad. This is the same reframe already recorded in §11.12 (the Plan v3 Wave 3 Task #41 scope correction: Google Breakpad promise → Node.js process.report implementation); the PR3 per-job fork-only migration inherits the process.report path. The REFTRIX_BREAKPAD_UPLOAD_URL env var + audit_logs.worker_crash_dump_uploaded action are already DEPRECATED in §11.12 (T+30d 2026-06-20 backward compat, formal removal in Plan v4.6); the 6 actions in this §11.13 do not depend on the Breakpad upload path. LCC-V1-02 / LCC-V2-02 reframe sign-off.

§11.13.3 GDPR Art.30 / APPI Art.23 整合性 / GDPR Art.30 / APPI Art.23 alignment

JP: 6 NEW actions はいずれも Worker lifecycle / lock / config の operational observation のみを目的とし、user-facing data subject ID・user content・user behavior とは構造的に紐付かない (§11.11.2 worker_job_lifecycle non-PII classification と同一根拠)。nonce (server-generated UUID)・bootEpoch (process boot epoch)・scriptSha (Lua script SHA) はいずれも自然人と関連を持たない server-side ephemeral identifiers (GDPR Art.4(1) "personal data" scope 外、EDPB Guidelines 4/2019 §3 が確認する operational system metadata)。365d retention は GDPR Art.30(1) records of processing activities completeness 要求を満たし、APPI 第23条 (安全管理措置) の技術的安全管理措置 scope を per-job fork lifecycle 監視に拡張する。

EN: All 6 NEW actions target operational observation of Worker lifecycle / lock / config only and are structurally unlinked to user-facing data subject IDs, user content, or user behavior (same rationale as the §11.11.2 worker_job_lifecycle non-PII classification). The nonce (server-generated UUID), bootEpoch (process boot epoch), and scriptSha (Lua script SHA) are server-side ephemeral identifiers unrelated to any natural person (outside GDPR Art.4(1) "personal data" scope; operational system metadata per EDPB Guidelines 4/2019 §3). The 365d retention satisfies the GDPR Art.30(1) records-of-processing-activities completeness requirement and extends the APPI Article 23 (technical safeguarding measures) scope to per-job fork lifecycle supervision.

§11.13.4 LCC sign-off marker / LCC sign-off マーカー

項目 / Item内容 / Content
Finding / 紐付 findingU-4 / FIND-PLAN-LCC-01 (AGPL §5(a) modification notice scope) + LCC-V2-01 (per-job fork audit_logs 365d retention + Breakpad reframe closure)
IO Impl Decision / IO 実装判定IO Impl Decision V1 anchor 019e46c1-79ae (CONDITIONAL → consent + LCC-V2-01 closure で APPROVE 充足)
PII minimisation / PII 最小化全 6 actions が Wave 5 CWE-209 truncateAuditTargetId SSOT 継承 (anchor 019df7ab-2f5a)、actor literal は SSOT 定数 import (INV-AUDIT-EMIT-SSOT-IMPORT-001 AST sweep)
Authority preservation / 発行元 sign-offLCC blocker の独断降格禁止 (` IO authority boundary)。本 §11.13 landing は LCC originator authority によって signed-off ずみ、IO は本 sign-off の整合性 (consent + SSOT + retention) のみ verify する。

Cross-ref / 相互参照:

  • T1 SSOT: apps/mcp-server/src/audit/audit-actions.ts (6 action literals + 2 actor literals + getWorkerActorName exhaustive helper)
  • DATA_RETENTION §11 (audit_logs 365d retention contract) / §11.10 / §11.11 (worker observability sibling sub-sections) / §11.12 (BREAKPAD → process.report reframe + deprecation)
  • ADR-0011 Amendment 6 (Redis TIME server-side monotonic pin, CWE-770) / ADR-0035 Amendment 2 (per-job fork-only supersede matrix + AGPL §5(a) notice scope) / ADR-0037 (technical PR Red-band cross-sign-off) / ADR-0021 (Worker Crash Dump Observability, process.report Option α)
  • ` §"Canonical CWE-209 PII Protection Pattern (LCC-endorsed)" + §"Worker actor naming SSOT (Plan v4.3 PR-M / ADR-0035)"
  • INV-AUDIT-EMIT-SSOT-IMPORT-001 (audit-log SSOT import AST sweep) / INV-WORKER-PER-JOB-FORK-LOCK-CONTRACT-001 / INV-WORKER-LUA-SHA-PIN-001 / INV-WORKER-CONFIG-LEGACY-ENV-VAR-DETECTION-001 standing regression tests
  • 3-CHANGELOG paths: CHANGELOG.md / /apps/mcp-server/CHANGELOG.md` (bilingual JP/EN Plan v4.5 PR3 entry)

§11.14 Phase 5 GPU-COORD CPU-fallback degraded audit action (v0.5.0 PR-1 GPU-COORD, FIND-IMPL-LCC-PR1-L-01) / Phase 5 GPU-COORD CPU フォールバック degraded 監査アクション

JP: 本サブセクションは PR-1 GPU-COORD (Phase 5 fork-child VRAM probe、ADR-0038 Decision 1/2) で apps/mcp-server/src/workers/phases/phase-5-fork-orchestrator.ts に導入される 1 個の NEW audit_logs action (embedding_cpu_fallback_degraded) の 365d retention 契約、PII minimisation (Wave 5 CWE-209 truncateAuditTargetId 継承)、GDPR Art.30 records of processing activities 整合性を T1 SSOT (apps/mcp-server/src/audit/audit-actions.ts) に整合する形で記録する。本 action は fork-child の VRAM probe が free VRAM 不足 / contention により CUDA を選べず CPU fallback (degraded mode) になった run を parent 側 DB write で可視化する (新規 IPC type 0、FIND-PLAN-M-01)。directive ⑤ no-fake-success: CPU fallback 完走を健全な GPU run と silently 同一視しない。FIND-IMPL-LCC-PR1-L-01 (L) bilingual JP/EN landing。

EN: This subsection records, in alignment with the T1 SSOT (apps/mcp-server/src/audit/audit-actions.ts), the 365d retention contract, PII minimisation (Wave 5 CWE-209 truncateAuditTargetId inheritance), and GDPR Art.30 records-of-processing-activities consistency for the 1 NEW audit_logs action (embedding_cpu_fallback_degraded) introduced into apps/mcp-server/src/workers/phases/phase-5-fork-orchestrator.ts by PR-1 GPU-COORD (Phase 5 fork-child VRAM probe, ADR-0038 Decision 1/2). The action surfaces — via a parent-side DB write (zero new IPC types, FIND-PLAN-M-01) — runs where the fork-child VRAM probe fell back to CPU (degraded mode) because free VRAM was insufficient / contended. Per directive ⑤ no-fake-success, a CPU-fallback completion is NOT silently treated as a healthy GPU run. FIND-IMPL-LCC-PR1-L-01 (L) bilingual JP/EN landing.

§11.14.1 NEW audit_logs action table / NEW audit_logs アクション表

action は 365d retention (§11 共通 audit_logs 契約 + GDPR Art.30(1) records of processing activities 継承)、targetIdtruncateAuditTargetId() (apps/mcp-server/src/audit/audit-actions.ts SSOT、Wave 5 LCC canonical CWE-209 PII protection pattern anchor 019df7ab-2f5a) で PII-truncate、details JSON は VRAM 数値 + reason/workload enum のみで raw payload / URL / user identifier を含まない (PII minimisation 保全)。actor literal は SSOT 定数 import 経由 (hardcoded literal 禁止、INV-AUDIT-EMIT-SSOT-IMPORT-001 AST sweep で structural enforce)。

The action inherits 365d retention (the §11 common audit_logs contract + GDPR Art.30(1)), truncates targetId via truncateAuditTargetId() (SSOT in apps/mcp-server/src/audit/audit-actions.ts, Wave 5 LCC canonical CWE-209 pattern anchor 019df7ab-2f5a), and carries only numeric VRAM values + reason/workload enums in details JSON (no raw payload / URL / user identifier; PII minimisation preserved). The actor literal is imported via the SSOT constant (hardcoded literals forbidden, structurally enforced by INV-AUDIT-EMIT-SSOT-IMPORT-001 AST sweep).

Action (T1 SSOT)Actor (SSOT 定数 / SSOT constant)targetTyperetentionresult / branchdetails (PII-free)
embedding_cpu_fallback_degradedsystem:phase5-init (AUDIT_ACTOR_PHASE5_INIT)web_page365日 / 365 dayssuccess (degraded CPU-fallback emit のみ / degraded CPU-fallback only)reason (fork_child_below_threshold | vram_contention | cuda_ep_unavailable), workload (text | visual), freeVramMb (number | null), thresholdMb (number)

cuda_ep_unavailable reason (PR-1 GPU-COORD H regression remediation, 85e8fb3b, FIND-IMPL-PR1-H-NEW-01 closure):

JP: cuda_ep_unavailable は free VRAM が閾値以上であるにもかかわらず、CUDA Execution Provider の共有ライブラリ (libonnxruntime_providers_cuda.so) が host に未インストールのため CUDA が実推論できず、安全に CPU へ fallback した degraded run を表す reason 値である。これは実機 GPU 検証で発見された H regression (probe が free VRAM 空きのみで CUDA を選択し、e5/text in-process init が EP .so 不在で raw throw → embedding phase 164 失敗) の remediation で導入された (phase-5-gpu-probe.tsverifyCudaAvailability() gate)。「probe が CUDA を意図する」≠「ONNX が CUDA で実推論可能」(directive ⑤ no-fake-success)。本 reason も VRAM 数値 + enum のみで PII-free、GDPR Art.30/Art.4(1) 整合性は他 reason と同一根拠で継承する。

EN: cuda_ep_unavailable is the reason value for a degraded run where free VRAM was at or above the threshold but the CUDA Execution Provider shared library (libonnxruntime_providers_cuda.so) was not installed on the host, so CUDA could not actually run and the run safely fell back to CPU. It was introduced in the remediation of the H regression found in real-machine GPU verification (the probe selected CUDA on free VRAM alone, and the e5/text in-process init raw-threw when the EP .so was absent → 164 embedding failures), via the verifyCudaAvailability() gate in phase-5-gpu-probe.ts. "The probe intends CUDA" ≠ "ONNX can run CUDA" (directive ⑤ no-fake-success). This reason is likewise PII-free (numeric VRAM values + enums only); its GDPR Art.30 / Art.4(1) consistency is inherited on the same rationale as the other reasons.

emit 条件 / Emit condition (T1 検証済 / verified): emit は degraded (CPU-fallback) decision のみで発火する (resolveProviderAndAuditDegradedisDegradedDecision(decision) 分岐; isDegradedDecisionfork_child_below_threshold / vram_contention / cuda_ep_unavailable の 3 reason を degraded と判定)。健全な CUDA-selected path (cuda_selected) と明示 rollback (PHASE5_FORK_GPU_PROBE_ENABLED=falseprobe_disabled) は degradation ではないため emit しない。audit emit 失敗は非致命 (graceful degradation、logger.warn で記録、AuditLogService.log() から継承)。

SSOT cross-ref / SSOT 相互参照: action / actor literal の正典は apps/mcp-server/src/audit/audit-actions.ts (AUDIT_ACTION_EMBEDDING_CPU_FALLBACK_DEGRADED / AUDIT_ACTOR_PHASE5_INIT)。本表と T1 SSOT が乖離した場合は T1 が勝つ (Source of Truth Hierarchy、CLAUDE.md §"真実の源泉")。

The canonical source of the action / actor literals is apps/mcp-server/src/audit/audit-actions.ts. If this table and the T1 SSOT diverge, T1 wins (Source of Truth Hierarchy).

§11.14.2 GDPR Art.30 / Art.4(1) 整合性 / GDPR Art.30 / Art.4(1) alignment

JP: embedding_cpu_fallback_degraded は Phase 5 fork-child の GPU coordination の operational observation のみを目的とし、user-facing data subject ID・user content・user behavior とは構造的に紐付かない (§11.13.3 worker observability sibling と同一根拠)。freeVramMb / thresholdMb は VRAM 数値、reason / workload は固定 enum で、いずれも自然人と関連を持たない server-side operational metadata (GDPR Art.4(1) "personal data" scope 外、CWE-209 information exposure リスクなし)。targetIdwebPageIdtruncateAuditTargetId() で truncate 済 (page identifier の最小化)。365d retention は GDPR Art.30(1) records of processing activities completeness を満たす。GPU-COORD 自体は PII を扱わない (VRAM coordination のみ) ため LCC sign-off は audit emit の PII-free 性 + SSOT 整合の verify に限定 (ADR-0038 §GDPR/Privacy)。

EN: embedding_cpu_fallback_degraded targets operational observation of the Phase 5 fork-child GPU coordination only and is structurally unlinked to user-facing data subject IDs, user content, or user behavior (same rationale as the §11.13.3 worker observability sibling). freeVramMb / thresholdMb are VRAM numerics, and reason / workload are fixed enums — all server-side operational metadata unrelated to any natural person (outside GDPR Art.4(1) "personal data" scope; no CWE-209 information exposure risk). targetId is the webPageId truncated via truncateAuditTargetId() (page-identifier minimisation). The 365d retention satisfies GDPR Art.30(1) records-of-processing-activities completeness. GPU-COORD itself handles no PII (VRAM coordination only), so LCC sign-off is limited to verifying the audit emit's PII-free property + SSOT consistency (ADR-0038 §GDPR/Privacy).

Cross-ref / 相互参照:

  • T1 SSOT: apps/mcp-server/src/audit/audit-actions.ts (AUDIT_ACTION_EMBEDDING_CPU_FALLBACK_DEGRADED + AUDIT_ACTOR_PHASE5_INIT)
  • Emit callsite: apps/mcp-server/src/workers/phases/phase-5-fork-orchestrator.ts (resolveProviderAndAuditDegraded)
  • ADR-0038 Decision 1 (probe wiring) / Decision 2 (CPU-fallback degraded audit emit) / FIND-PLAN-M-03 (SSOT actor/action) / FIND-IMPL-LCC-PR1-L-01 (this docs-sync landing)
  • ` §"Canonical CWE-209 PII Protection Pattern (LCC-endorsed)" + §"Worker actor naming SSOT (Plan v4.3 PR-M / ADR-0035)"
  • INV-AUDIT-EMIT-SSOT-IMPORT-001 (audit-log SSOT import AST sweep)
  • 3-CHANGELOG paths: CHANGELOG.md / /apps/mcp-server/CHANGELOG.md` (bilingual JP/EN PR-1 GPU-COORD entry, M-09)

12. 検索ログデータ保持 / Search Log Data Retention

対象データ / Data in Scope

テーブル / Tableカラム / Columns内容 / Description
search_logsid, timestamp, query, query_type, services, result_count, top_result_id, filters, latency_ms, cache_hit, profile_id検索クエリ・パフォーマンス・結果の記録。ファセット検索分析とMLフィードバックループ基盤。 / Search query, performance, and result recording. Foundation for facet search analysis and ML feedback loop.

PII評価 / PII Assessment

  • PII リスク: 低 / Low
  • profile_idtruncateId() 適用済み(先頭8文字 + ...、VarChar(50)) / profile_id has truncateId() applied (first 8 chars + ..., VarChar(50))
  • query は 200文字に制限 / query is truncated to 200 characters
  • top_result_id は truncated UUID / top_result_id is a truncated UUID

保持方針 / Retention Policy

  • 保持期間 / Retention Period: 90日推奨 / 90 days recommended
  • 設計 / Design: Append-only(通常の UPDATE/DELETE 不可) / Append-only (no regular UPDATE/DELETE)
  • 自動期限切れ / Auto-expiry: 90日超過レコードの定期クリーンアップ推奨 / Periodic cleanup of records older than 90 days recommended
  • 手動削除 / Manual deletion: data.delete target=profile 実行時に該当 profile_id を NULL化(匿名化) / On data.delete target=profile, the corresponding profile_id is set to NULL (anonymized)

GDPR対応 / GDPR Compliance

条項 / Article内容 / Subject対応 / Implementation
Art. 17忘れられる権利 / Right to erasureプロファイル削除時に search_logs.profile_id を NULL化(匿名化)。ログ自体は検索品質分析用途で匿名化済みレコードとして保持。 / On profile deletion, search_logs.profile_id is set to NULL (anonymized). Logs are retained as anonymized records for search quality analysis.
Art. 5(1)(c)データ最小化の原則 / Data minimisationquery 200文字制限、profile_id truncateId()、top_result_id truncated UUID による最小化。 / Minimised via query 200-char limit, profile_id truncateId(), top_result_id truncated UUID.
Art. 5(1)(e)保存制限の原則 / Storage limitation90日保持後にクリーンアップ推奨。 / Cleanup recommended after 90 days.

13. デザインスナップショットデータ保持 / Design Snapshot Data Retention

対象データ / Data in Scope

テーブル / Tableカラム / Columns内容 / Description
design_snapshotsid, web_page_id, snapshot_at, section_count, overall_score, metadata, created_at特定時点のWebページデザイン状態を記録するスナップショット。デザイン変更の時系列追跡に使用。 / Point-in-time snapshot of web page design state. Used for temporal design change tracking.
design_snapshot_sectionsid, snapshot_id, section_type, section_name, position_index, text_embedding, vision_embedding, created_atスナップショット内の個別セクションデータ(Embedding含む)。 / Individual section data within a snapshot (including embeddings).

PII評価 / PII Assessment

  • PII リスク: なし / None
  • design_snapshotsweb_page_id(FK)のみでユーザー識別情報を含まない / design_snapshots contains only web_page_id (FK), no user-identifying information
  • design_snapshot_sections は Embedding データのみ(text_embedding, vision_embedding)でPII不含 / design_snapshot_sections contains only embedding data (text_embedding, vision_embedding), no PII

保持方針 / Retention Policy

  • 保持期間 / Retention Period: 親レコード(web_pages)と同一 / Same as parent record (web_pages)
  • CASCADE DELETE: design_snapshotsweb_pages に対して onDelete: Cascadedesign_snapshot_sectionsdesign_snapshots に対して onDelete: Cascade。 / design_snapshots has onDelete: Cascade on web_pages. design_snapshot_sections has onDelete: Cascade on design_snapshots.
  • 自動期限切れ / Auto-expiry: なし(親レコードのライフサイクルに従う) / None (follows parent record lifecycle)
  • 手動削除 / Manual deletion: WebPage レコードの削除により CASCADE で自動実行 / Automatically executed via CASCADE when a WebPage record is deleted

GDPR対応 / GDPR Compliance

条項 / Article内容 / Subject対応 / Implementation
Art. 17忘れられる権利 / Right to erasureWebPage 削除時に全関連 design_snapshots および design_snapshot_sections が CASCADE 削除。ベクトルデータを含むすべてのデータが物理削除される。 / All associated design_snapshots and design_snapshot_sections are CASCADE deleted when WebPage is deleted. All data including vector data is permanently removed.
Art. 5(1)(c)データ最小化の原則 / Data minimisationPII を含まないデザイン構造データのみ保持。 / Only PII-free design structure data is retained.

14. BullMQ embedding-backfill Queue(v0.4.0 PR4)/ BullMQ embedding-backfill Queue (v0.4.0 PR4)

対象データ / Data in Scope

page.analyze の Phase 5 (Embedding) で 1 ページあたり Part 件数が PART_SYNC_THRESHOLD(デフォルト 100)を超えた場合、残余を非同期処理する BullMQ Queue。

BullMQ queue that asynchronously processes remaining Part embeddings when page.analyze Phase 5 (Embedding) exceeds PART_SYNC_THRESHOLD (default 100) Parts per page.

  • 保存先 / Storage: Redis (port 27379)
  • Queue 名 / Queue name: embedding-backfill
  • ジョブペイロード / Job payload:
    • webPageId (UUID v4/v7)
    • category (part_text | part_visual)
    • screenshotStoragePath (optional, part_visual 用の ${REFTRIX_SCREENSHOT_ROOT}/phase5/<webPageId>.png 絶対パス)
    • requiresBboxResolution (optional boolean)
    • createdAt (ISO 8601)
    • requestId (optional, ≤128 chars)

PII 評価 / PII Assessment

  • PII 含有 / PII: なし / None
  • webPageId は UUID v7 で個人識別性なし / webPageId is UUID v7 with no personal identifier
  • screenshotStoragePath は Phase 5 ディレクトリ配下の path のみで、validateScreenshotPath() により allowlist + realpath 検証済み / screenshotStoragePath is constrained to the Phase 5 directory and validated via validateScreenshotPath() (allowlist + realpath)
  • ジョブ結果 (EmbeddingBackfillJobResult) は件数・処理時間・エラー概要のみで PII を含まない / The job result contains only counts, durations, and a sanitized error summary — no PII

保持期間 / Retention Period

種別 / State保持期間 / RetentionBullMQ オプション / Option
完了ジョブ / Completed24時間 または 最新 1,000 件 / 24h or latest 1,000removeOnComplete: { age: 86400, count: 1000 }
失敗ジョブ / Failed7日 または 最新 500 件 / 7 days or latest 500removeOnFail: { age: 604800, count: 500 }
リトライ / Retry policy最大 3 回、exponential backoff (5s から)attempts: 3, backoff: { type: "exponential", delay: 5000 }

削除経路 / Deletion Path

  • 自動 / Automatic: 上記 BullMQ retention policy による自動失効 / Automatic expiry via the BullMQ retention policy above
  • 手動 / Manual: data.delete MCP ツールで web_page を削除する際、Queue 側のジョブも連動削除(GDPR Art. 17 忘れられる権利) / When data.delete MCP tool removes a web_page, related queue jobs are removed in tandem (GDPR Art. 17 Right to Erasure)
  • Jobs 手動削除 / Manual job removal: Redis から直接 bull:embedding-backfill:* キーを削除することも可(運用者向け) / Operators may directly delete bull:embedding-backfill:* keys from Redis

セキュリティ措置 / Security Measures

  • 入力検証 / Input validation: 投入時 (addEmbeddingBackfillJob) および Worker 受信時 (processBackfillJob) の 2 か所で Zod スキーマ (EmbeddingBackfillJobDataSchema) により parse(defense in depth、SEC M-1 / v0.4.0 PR4 audit)。不正 UUID / 改行混入 / 長大文字列 (>512 chars) を拒否 / Zod-validated at both enqueue (addEmbeddingBackfillJob) and worker receipt (processBackfillJob) boundaries (defense in depth, SEC M-1 / v0.4.0 PR4 audit). Rejects invalid UUID / newline injection / oversized strings (>512 chars)
  • Path Traversal 防御 / Path traversal defense: screenshotStoragePath は Worker 内で validateScreenshotPath() を通し、allowlist + fs.promises.realpath で symlink 解消 + 実ファイル確認を実施(SEC H-1 / L-1 / v0.4.0 PR4 audit) / screenshotStoragePath passes through validateScreenshotPath() inside the worker — allowlist + fs.promises.realpath symlink resolution + real-file check (SEC H-1 / L-1 / v0.4.0 PR4 audit)
  • PII ログ保護 / PII-safe logging: 全ログで webPageId は先頭 8 文字 + ... に truncate / All logs truncate webPageId to the first 8 chars + ...
  • エラーサニタイズ / Error sanitization: Queue / Worker 内の全 catch で sanitizeErrorMessage を経由してスタックトレースや DB 内部構造の漏洩を防御 / Every catch in queue / worker routes error messages through sanitizeErrorMessage to avoid leaking stack traces or DB internals

GDPR 対応 / GDPR Compliance

条項 / Article内容 / Subject対応 / Implementation
Art. 17忘れられる権利 / Right to erasuredata.delete による web_page 削除時に、対応する <webPageId>__<category> ジョブを BullMQ Queue から削除 / On data.delete web_page removal, the associated <webPageId>__<category> jobs are removed from the BullMQ queue
Art. 5(1)(c)データ最小化の原則 / Data minimisationペイロードには webPageId / category / (optional) storage path のみ保持。HTML / スクリーンショット / embedding ベクトルは Queue に持たない / Payload contains only webPageId / category / (optional) storage path; HTML / screenshots / embeddings never live in the queue
Art. 5(1)(e)保存制限の原則 / Storage limitationBullMQ の removeOnComplete / removeOnFail TTL により自動失効 / Automatic expiry via BullMQ removeOnComplete / removeOnFail TTL

自動メンテナンスタスク(v0.4.0 PR6)/ Automated Maintenance Tasks (v0.4.0 PR6)

v0.4.0 PR6 で start-workers.ts 起動時に以下の定期タスクが自動セットアップされる。Worker shutdown 時に stop() で停止される。

v0.4.0 PR6 sets up the following periodic tasks automatically at start-workers.ts startup, and stops them via stop() on worker shutdown.

タスク / Task頻度 / Frequency目的 / Purposeソース / Source環境変数 / Env Vars
Screenshot TTL cleanup (retired in PR-SS-B / ADR-0041)— (撤去済 / removed)旧: 7 日を超過した screenshot を削除 + DB screenshotStoragePath を NULL 化。PR-SS-B (ADR-0041) で cron module + cleanupExpired + SCREENSHOT_CLEANUP_* env var を構造ごと撤去、保持 = data.delete まで (INV-SCREENSHOT-RETENTION-001) / Formerly: deleted screenshots older than 7d and NULLed the DB path. Removed together with the cron module + cleanupExpired + the SCREENSHOT_CLEANUP_* env vars in PR-SS-B (ADR-0041); retention is until data.delete (INV-SCREENSHOT-RETENTION-001)cron/screenshot-cleanup-cron.ts (削除済 / deleted)— (SCREENSHOT_CLEANUP_* retired)
Backfill stale reconciliation1時間ごと / every 1hembedding_backfill_status = 'in_progress' かつ Queue に job が無い stale 行を DB 完全性で completed / failed に補正(CAS で worker 競合回避) / Reconciles stale in_progress rows with no queue job to completed / failed based on DB completeness (CAS guard against worker race)cron/backfill-reconciliation-cron.tsBACKFILL_RECONCILIATION_INTERVAL_MS, BACKFILL_RECONCILIATION_STALE_THRESHOLD_MS, BACKFILL_RECONCILIATION_BATCH_LIMIT
Worker orphan recovery起動時 / at startup前回クラッシュ / 再起動時に active 状態のまま残ったジョブを categorizeByProgress() で failed / retry に振り分け / On startup, reroute orphaned active jobs to failed / retry via categorizeByProgress()scripts/start-workers.ts

設計判断 / Design decisions:

  • 自動タスクはすべて non-fatal: エラー発生時も worker 本体の動作に影響しない。 / All maintenance tasks are non-fatal: errors never block the worker's main loop.
  • 前回実行が未完了の場合 tick は skip(pile-up 防止)。 / Ticks overlapping a still-running sweep are skipped (pile-up prevention).
  • CLI apps/mcp-server/src/scripts/reconcile-backfill.ts は運用者手動実行専用として残存。production では --confirm または --dry-run 必須(誤発火防止、SEC LOW-2)。 / CLI remains for manual operator use; production requires --confirm or --dry-run (SEC LOW-2, prevents accidental runs).

遡及修復スクリプト repair-page-analyze.ts (v0.4.0 PR7e-α) / Retrospective repair script (v0.4.0 PR7e-alpha)

apps/mcp-server/scripts/repair-page-analyze.ts は、PR7e-Ω 7 バグクラスタにより embeddingBackfillStatusin_progress / skipped_memory_pressure / skipped_fork_error のまま 1 時間以上放置されたページを救出する遡及修復スクリプト。part_visual + section_visual ジョブを embedding-backfill Queue に再投入し、embeddingBackfillStartedAt を現在時刻で再設定する。遡及処理は GDPR Art. 5(1)(e) (storage limitation) の範囲内であり、personal data の保管期間を延長しない。本スクリプトは embeddingBackfillStatus の遷移とジョブ再投入のみを行い、新規 embedding は通常の Queue-based Backfill Worker が既存 retention 規約 (screenshot 保持 = data.delete まで、PR-SS-B / ADR-0041 で 7d TTL cron 撤去済) に従って生成する。全実行 (dry-run / confirm 両モード) は audit_logsaction=embedding_backfill_repair / embedding_backfill_repair_dryrun で記録される (GDPR Art. 30 処理活動記録)。

apps/mcp-server/scripts/repair-page-analyze.ts is a retrospective repair script that rescues pages whose embeddingBackfillStatus has been stuck at in_progress / skipped_memory_pressure / skipped_fork_error for more than 1 hour due to the PR7e-Ω seven-bug cluster. It re-enqueues part_visual + section_visual jobs onto the embedding-backfill Queue and resets embeddingBackfillStartedAt to NOW. The retrospective reprocessing is within the scope of GDPR Art. 5(1)(e) (storage limitation) and does not extend the retention period of personal data. The script only transitions embeddingBackfillStatus and re-enqueues jobs; new embeddings are generated by the regular Queue-based Backfill Worker under the existing retention policy (screenshot retention = until data.delete; the 7d TTL cron was removed in PR-SS-B / ADR-0041). Every execution (both dry-run and confirm modes) is recorded in audit_logs with action=embedding_backfill_repair / embedding_backfill_repair_dryrun (GDPR Art. 30 processing activity records).

運用要件 / Operational requirements:

  • REFTRIX_REPAIR_ALLOW_PRODUCTION=true を production で明示 (未設定時は拒否 / refused when unset)
  • --operator=<name> 必須 (actor 記録) / mandatory (actor tracking)
  • 二段確認 --confirm --yes が揃わないと書込禁止 / writes require both --confirm and --yes
  • REFTRIX_REPAIR_MAX_PAGES=100 上限 (CWE-770) / upper bound (CWE-770)
  • WorkerActiveLockService.probeExistingLock pre-flight — active worker lock 検出で即 exit / exits immediately on active-worker-lock detection
  • updateMany CAS guard で並列 Worker の race を回避 / updateMany CAS guard avoids races with concurrent Workers
  • sha256(webPageId|operator|runId) idempotency key (SEC MED-4)
  • Details ADR-0012 参照 / See ADR-0012

Embedding Integrity SLO (v0.4.0 PR-D-5) / Embedding 完全性 SLO

JP: PR-D-5 で導入された INV-EMBEDDING-INTEGRITY-001 / INV-EMBEDDING-INTEGRITY-003 の terminal-state 整合性を運用観測するための 5-tier SLO。audit_logs primary emit (embedding_parity_check_failed) + sentinel emit (embedding_parity_schema_drift) + 同時 emit される log-based metric ([SLO_MARKER] audit_log_emit_failed) を 3 種の source として tier 判定する。

EN: A 5-tier SLO introduced in PR-D-5 to operationally monitor the terminal-state integrity of INV-EMBEDDING-INTEGRITY-001 / INV-EMBEDDING-INTEGRITY-003. Tier determination uses three sources: audit_logs primary emit (embedding_parity_check_failed), sentinel emit (embedding_parity_schema_drift), and the co-emitted log-based metric ([SLO_MARKER] audit_log_emit_failed).

SLO 5-tier thresholds / SLO 5 階層閾値

TierSignalThreshold (per 24h)Action / アクションSource
L0 OKaudit_logs.action='embedding_parity_check_failed' emits_24h0通常運用 / Normal operationaudit_logs SQL query
L1 WARNaudit_logs.action='embedding_parity_check_failed' emits_24h1 ≤ count < 10dashboard warn annotation、trend 観察 / Dashboard warn annotation, trend observationaudit_logs SQL query
L1.5 SLO_MARKER (NEW)[SLO_MARKER] audit_log_emit_failed log lines (primary emit failure)rate > 0 (log-based metric, Grafana Loki / equivalent)primary emit failure rate alert、observability blind spot 補完 / Primary-emit failure-rate alert; compensates observability blind spotLog aggregation (log-based metric)
L2 ALERTaudit_logs.action='embedding_parity_check_failed' emits_24hcount ≥ 10on-call page、pipeline-engineer escalation / On-call page, pipeline-engineer escalationaudit_logs SQL query
L3 CRITaudit_logs.action='embedding_parity_schema_drift' emits_24h (sentinel)≥ 1GDPR Art.33 breach notification gate trigger + pipeline-engineer + legal-compliance-counsel immediate escalation / GDPR Art.33 breach gate + dual escalationaudit_logs SQL query (sentinel)

L1.5 SLO_MARKER description / L1.5 SLO_MARKER 説明: L1.5 は audit_logs primary emit failure の log-based metric tier。primary emit が catch block で fail-open し warn log に degrade した場合 ([SLO_MARKER] audit_log_emit_failed 出力)、通常の audit_logs SQL query からは観測できない blind spot になる。L1.5 tier は log aggregation (Grafana Loki / equivalent) が当該 marker line の rate を継続観測し、primary emit failure rate が上昇した場合に alert する。ADR-0018 Amendment 5 §Privacy Considerations Update (a) Fail-open tension を補完する observability layer として機能。

L1.5 SLO_MARKER description (EN): L1.5 is the log-based-metric tier for audit_logs primary-emit failures. When the primary emit fails open in its catch block and degrades to a warn log (emitting [SLO_MARKER] audit_log_emit_failed), the usual audit_logs SQL query becomes blind to the event. The L1.5 tier has log aggregation (Grafana Loki or equivalent) continuously observe the marker-line rate and alert on rising primary-emit failure rate. It serves as the observability layer compensating the fail-open tension documented in ADR-0018 Amendment 5 §Privacy Considerations Update (a).

Query hooks / クエリ連携

L1 WARN / L2 ALERT (SQL, count-based):

-- L1 WARN: 1 ≤ count < 10
-- L2 ALERT: count ≥ 10
SELECT COUNT(*) AS emits_24h
FROM audit_logs
WHERE action = 'embedding_parity_check_failed'
  AND timestamp >= NOW() - INTERVAL '24 hours';

L3 CRIT (SQL, sentinel drift count):

-- L3 CRIT: ≥ 1 triggers GDPR Art.33 breach notification gate
SELECT COUNT(*) AS drift_24h
FROM audit_logs
WHERE action = 'embedding_parity_schema_drift'
  AND timestamp >= NOW() - INTERVAL '24 hours';

L1.5 SLO_MARKER (log-based metric): Grafana Loki / equivalent アグリゲータで [SLO_MARKER] audit_log_emit_failed line rate を継続観測。Reftrix repo 自体はクエリを同梱せず、operator の log-aggregation stack 設定に委譲。/ Continuously observe the [SLO_MARKER] audit_log_emit_failed line rate via Grafana Loki or equivalent aggregator. The Reftrix repo does not bundle the query; it is delegated to the operator's log-aggregation stack configuration.

Storage limitation (GDPR Art.5(1)(e)) / 保管制限 (FIND-LCC-02, deadline 2026-05-04 strict)

JP (3 sentence binding):

  1. SLO query layer は既存 audit_logs の 365 日保持 view / cron (§11) に依存しており、新規 retention horizon の追加は不要embedding_parity_check_failed / embedding_parity_schema_drift エントリは Art.30 処理活動記録として既存 365 日ポリシーをそのまま継承する (GDPR Art.5(1)(e) compliance 保持)。
  2. Grafana dashboard / Loki query stack の operator-side 設定は release-manager scope の別 PR で LCC 再監査を通す (本 PR-D-5 scope では Reftrix repo 同梱しない)。dashboard JSON / alert rule の retention / access control は release-manager が個別確認。
  3. Sentinel entry (embedding_parity_schema_drift) も既存 365 日 retention をそのまま継承し、個別 retention override は行わない。Art.5(1)(e) storage limitation 原則との整合性は §11 Audit Log Retention の既存ポリシーによって担保される。

EN (3 sentence binding):

  1. The SLO query layer relies on the existing audit_logs 365-day retention view / cron (§11); no new retention horizon is required. embedding_parity_check_failed / embedding_parity_schema_drift entries inherit the existing 365-day policy unchanged as GDPR Art.30 records of processing activities (Art.5(1)(e) compliance preserved).
  2. Operator-side configuration of the Grafana dashboard / Loki query stack is routed through a separate release-manager-scope PR with an LCC re-audit (not bundled with the Reftrix repo in PR-D-5). Retention and access control of dashboard JSON / alert rules are confirmed individually by release-manager.
  3. Sentinel entries (embedding_parity_schema_drift) also inherit the existing 365-day retention unchanged; no per-entry retention override is applied. Alignment with Art.5(1)(e) storage-limitation principles is guaranteed by the existing §11 Audit Log Retention policy.

Breach notification gate (GDPR Art.33) / 侵害通知ゲート

JP: SLO L3 CRIT (embedding_parity_schema_drift sentinel 発火) 時は GDPR Art.33 72-hour breach notification assessment gate を trigger する。downstream personal-data-bearing rows (component_parts / section_embeddings / component_part_embeddings 等) の terminal-state 整合性を verifyCategoryParity を越える範囲で独立評価し、schema drift が personal data integrity incident に該当するか 72 時間以内に判定する。判定結果に応じて DPA (Data Protection Authority) 通知を行う運用責務を release-manager / legal-compliance-counsel に付帯する。

EN: When SLO L3 CRIT (the embedding_parity_schema_drift sentinel) fires, it triggers the GDPR Art.33 72-hour breach-notification assessment gate. Downstream personal-data-bearing rows (component_parts / section_embeddings / component_part_embeddings, etc.) must be independently evaluated for terminal-state integrity beyond verifyCategoryParity, and within 72 hours a determination must be made whether the schema drift constitutes a personal-data integrity incident. DPA (Data Protection Authority) notification is then the operational responsibility of release-manager / legal-compliance-counsel.

audit_logs action 列挙表 — new action values (FIND-LCC-IMPL-01) / audit_logs action enumeration — new values

既存 audit_logs.action 列挙集 (§11 Audit Log Data Retention + §6.2 Phase 0 Stale Row Cleanup Cron + PR7d-3 Screenshot TTL cleanup + PR7e-α repair script) に PR-D-5 で 2 種 new action を追加。すべて既存の 365 日 retention + truncateTargetId PII minimisation + system:<role> actor naming convention を継承する。

Appends 2 new action values to the existing audit_logs.action enumeration (§11 Audit Log Data Retention + §6.2 Phase 0 Stale Row Cleanup Cron + PR7d-3 Screenshot TTL cleanup + PR7e-α repair script). Both inherit the existing 365-day retention, truncateTargetId PII minimisation, and system:<role> actor naming convention.

Action (new) / 新規 actionActorTarget typeEmit condition / 発火条件RetentionTier mapping
embedding_parity_check_failedsystem:embedding-backfill-workerweb_pageEmbedding Backfill Worker terminal transition 直前の verifyCategoryParity 結果が pending count > 0 を検出した場合 (primary emit、L1/L2 source) / Primary emit on parity failure365dL0 / L1 / L2
embedding_parity_schema_driftsystem:embedding-backfill-workerweb_pagedetectCategoryDrift が category map keys と EMBEDDING_BACKFILL_CATEGORIES SSOT の Set-equality 違反を検出した場合 (sentinel emit、L3 CRIT source) / Sentinel emit on schema drift365dL3 (CRIT)
embedding_backfill_collision_resolvedsystem:embedding-backfill-queueweb_pageaddEmbeddingBackfillJobWithGuard が terminal (completed/failed) incumbent を検出し retry jobId (<origJobId>__retry_<uuidv7>) を新規 enqueue した直後、emitCollisionAudit (embedding-backfill-queue.ts L756-785) 経由で primary emit。Zod CollisionAuditPayloadSchema strict 5-field contract (v0.4.0 PR-D-6) / Primary emit on BullMQ jobId collision retry enqueue365dL0 / L1 / L2
page_analyze_collision_resolvedsystem:page-analyze-queueweb_pageaddPageAnalyzeJobWithGuard が terminal incumbent を検出し retry jobId を新規 enqueue した直後、emitPageAnalyzeCollisionAudit (page-analyze-queue.ts L676-706) 経由で observability-only emit (Registry v3 §3 FIND-TPA-02 binding、schema-drift trigger 対象外、v0.4.0 PR-D-6) / Observability-only emit on page.analyze collision retry365dL0 / L1 / L2
parent_rss_ceiling_scaledsystem:phase5-init(none)Phase 5 init で親プロセス RSS ceiling が default 7168 → 8192 MB に scaling された際に 冪等に 1 回 emit される (operator override で明示的に 7168 を維持する deployment は emit しない、これは scaling event ではなく override であるため; PR-V3-T1a §3.4.2 / Plan v3 V2 §3.1 T1.3 / FIND-V3-IO-M-07 closure)。details{before_mb=7168, after_mb=8192, trigger='plan_v3_t1a_landing', commit_sha=<env T1A_COMMIT_SHA or "unknown">} の numeric / fixed-string のみ (PII-free、GDPR Art.5(1)(c) data minimisation 整合)。targetIdundefined (Phase 5 init は system-level event であり target individual は存在しない、truncateTargetId(undefined) は安全に null を返す)。365d retention + system: prefix actor naming convention + truncateTargetId PII minimisation はすべて既存 PR-D-8 SSOT contract から継承 / Idempotently emitted once during Phase 5 init when the parent-process RSS ceiling is scaled from default 7168 → 8192 MB (deployments that explicitly hold at 7168 via operator override do not emit, since override is not a scaling event). details carries only {before_mb=7168, after_mb=8192, trigger='plan_v3_t1a_landing', commit_sha=<env T1A_COMMIT_SHA or "unknown">} numeric / fixed-string fields (PII-free, GDPR Art.5(1)(c) data minimisation aligned). targetId is undefined (Phase 5 init is a system-level event with no target individual; truncateTargetId(undefined) safely returns null). 365d retention + system: prefix actor naming convention + truncateTargetId PII minimisation are all inherited from the existing PR-D-8 SSOT contract365d(n/a — not SLO-tier mapped; observability-only governance row per LCC F-M-07 cluster sign-off, PR-V3-T1a Phase 2 Step 8 IO V1 Impl Decision anchor 019df508-e803)

Cross-ref: Amendment 5 §Decision 3 / §Privacy Considerations Update (a)(b) / §Drift Detection Functional Delta / §Breach Notification Gate — in ` Amendment 6 §Privacy Considerations Update / §Test Coverage — in same file (PR-D-6 RC-A landing).


Visual Embedding 根本原因修復 (v0.4.0 PR7e-β1) / Visual Embedding Root-Cause Remediation (v0.4.0 PR7e-β1)

PR7e-α で stale backfill を救出しても Stripe 697 parts の再 analyze で再び visual embedding 0 件になった原因 (ADR-0012 §3 BLOCKER 4) を根本修復するため、β1 では以下の 5 点を変更:

PR7e-β1 fixes the root causes of the repeated zero-visual-embedding regression on the Stripe 697-parts re-analyze, even after the PR7e-α rescue (ADR-0012 §3 BLOCKER 4):

  1. ONNX Worker Thread resourceLimits: new Worker(scriptPath, { execArgv: [] }) が CPU fallback 時に親の --max-old-space-size を継承しない問題を、resourceLimits.maxOldGenerationSizeMb (default 4096 MB, env ML_WORKER_MAX_OLD_SPACE_MB) の明示指定で解消。DINOv2 / e5-base の CPU 推論 OOM を防止する。これにより Queue-based Backfill の child process が永久に in_progress に留まる挙動が根本的に解消される。個人データの保持/削除ポリシーは変更なし。/ Explicitly set resourceLimits.maxOldGenerationSizeMb (default 4096 MB, env ML_WORKER_MAX_OLD_SPACE_MB) when spawning ONNX Worker Threads so they inherit an adequate V8 old-space ceiling even under CPU fallback. Prevents OOM crashes in DINOv2 / e5-base CPU inference and eliminates stuck-in_progress Queue-based Backfill child processes. No change to personal-data retention or deletion policy.
  2. Phase 5 parent RSS ceiling 段階緩和: PHASE5_PARENT_RSS_MAX_MB のデフォルトを 3072 → 4096 に引き上げ (β3 で再評価)。Stripe 相当の重いページが Phase 5 early skip の対象外になる。RSS ceiling 緩和による個人データ取り扱いへの影響なし。/ Relaxed the default PHASE5_PARENT_RSS_MAX_MB from 3072 → 4096 (to be re-evaluated in β3). Stripe-class heavy pages no longer trigger Phase 5 early-skip. No impact on personal-data handling.
  3. Phase 5 sections 件数上限 (GDPR Art. 5(1)(c) data minimisation): PHASE5_MAX_SECTIONS_INPUT (default 50, range 1..500) を新設し、Phase 4 → Phase 5 handoff で layoutResultForNarrative.sections を上限で slice する。大量セクション投入による per-section DINOv2 RSS 急増を防ぐと同時に、Phase 5 で処理される section 数を必要最小限にとどめることで GDPR データ最小化原則に整合する。/ Introduced PHASE5_MAX_SECTIONS_INPUT (default 50, range 1..500, GDPR Art. 5(1)(c) data minimisation). Caps layoutResultForNarrative.sections at Phase 4 → Phase 5 handoff, preventing per-section DINOv2 RSS spikes and aligning with GDPR data-minimisation by processing only the minimum necessary sections in Phase 5.
  4. loadEnvLocal() 共通ヘルパー (SEC-β-01 / SEC-β-07): packages/core/src/config/env-local.ts に抽出し、5 script (start-workers.ts / check-embedding-coverage.ts / backfill-embeddings.ts / repair-page-analyze.ts / repair-orphaned-backfill-records.ts) の重複実装を解消。デフォルト maxDepth=5 (無限遡上防止, SEC-β-01)、verbose=false (DATABASE_URL password 等の秘密情報をログに出さない, SEC-β-07)、既存 process.env 絶対非上書き。unit test 16 cases でパスワード漏洩不可を検証。/ Shared helper packages/core/src/config/env-local.ts. Default maxDepth=5 (SEC-β-01 prevents unbounded walk), verbose=false (SEC-β-07 prevents DATABASE_URL password leakage), never overwrites existing process.env. 16 unit tests verify password-leak-proofness.
  5. bootstrapAuditLogServiceForScript() (GDPR Art. 30 処理活動記録): CLI script で PrismaClient を new した後に DI を登録するヘルパーを追加。repair-page-analyze.ts / repair-orphaned-backfill-records.ts で呼び出すことで、getAuditLogService().log() がサイレントに warn ログだけ出す状態を解消し、audit_logs への書き込みが確実に永続化される。実 DB integration test 2 シナリオで検証 (bootstrap あり = INSERT 成功 / bootstrap なし = graceful degradation)。/ Added helper for CLI scripts to register PrismaClient into AuditLogService DI after new PrismaClient(). Ensures audit_logs writes persist reliably — previously the service silently degraded to warn-only. Validated via 2 real-DB integration test scenarios (with / without bootstrap).

運用ガイド (SEC-β-02) / Operational guidance (SEC-β-02):

ONNX CPU fallback 時に子プロセスが OOM した場合、glibc による coredump が /var/lib/systemd/coredump/ などに生成されるとメモリダンプ経由で個人データ (PII を含む HTML / screenshot バッファ) が漏洩するリスクがあるため、本番環境では ulimit -c 0 または systemd LimitCORE=0 / Docker --ulimit core=0 による coredump 抑止を強く推奨する。Reftrix リポジトリ自体はこれらの OS 設定を変更しないため、operator の明示対応が必要。

If a child process OOMs during ONNX CPU fallback, glibc may write a coredump to /var/lib/systemd/coredump/ and similar locations, creating a PII leakage risk (HTML / screenshot buffers in memory). Strongly recommend disabling coredumps in production via ulimit -c 0, systemd LimitCORE=0, or Docker --ulimit core=0. The Reftrix repo itself does not modify these OS-level settings, so operators must apply them explicitly.

既存 retention ポリシー (screenshot 7d TTL / audit_logs 365d / etc.) には一切変更なし。β1 の変更はメモリ挙動と観測性の改善に限定される。/ No changes to existing retention policies (screenshot 7d TTL / audit_logs 365d / etc.). β1 is limited to memory behaviour and observability improvements.


Plan v4.2 Worker Lifecycle Audit Log Contract (PR-A/B/C, 2026-05-17) / Plan v4.2 Worker ライフサイクル監査ログ契約

Plan v4.2 (PR-A/B/C, PR-A commit 795b7217) で worker lifecycle pattern が callback-based exit (BullMQ native worker.once('completed', () => process.exit(0)) pre-registered listener) に変更され、新規 audit_logs actions は 追加されない

Plan v4.2 (PR-A/B/C, PR-A commit 795b7217) modifies the worker lifecycle to a callback-based exit pattern (BullMQ native worker.once('completed', () => process.exit(0)) pre-registered listener); no new audit_logs actions are added.

Audit Log Contract Delta / 監査ログ契約差分

項目 / ItemPlan v4.1 (proposed, dropped)Plan v4.2 (accepted, this PR)
worker_timeout_fired audit_logs actionproposed (timeout-based orphan path)dropped (Option B timeout-less)
worker_job_orphan_detected audit_logs actionproposed (active 1h orphan)dropped (BullMQ native stalled event)
Orphan detection mechanismcustom timeout cronBullMQ native stalled event + existing worker_job_lifecycle.event_type=release
新規 retention horizon365d (proposed)none (no new actions)
GDPR Art.30 records of processing coverage365d retention via new actionsinherited (existing 365d retention contract unchanged)

GDPR Art.30 / APPI Article 23 Impact / GDPR Art.30 / APPI 第23条 影響

Plan v4.2 PR-A/B/C は worker lifecycle internals refactor (callback-based exit + AST gate + 24h smoke harness) に限定され、personal data 処理経路 (page.analyze pipeline / preference profiling / search / data.delete / data.export) は不変。新規 audit_logs action 追加なし、新規 retention horizon 追加なし、SBOM drift ゼロ (新規依存追加なし、既存 bullmq / ioredis API のみ使用、EU CRA 2026-09-11 compliance 保持)。GDPR Art.32(1)(b) processing integrity (BullMQ Lua transaction completion guarantee) + APPI 第23条 安全管理措置 (operational reliability 向上) を充足。本 PR は GDPR Art.33 / APPI 第26条 personal data breach trigger に該当しない (worker lifecycle internals only、PII path 不変)。

Plan v4.2 PR-A/B/C is limited to worker lifecycle internals refactor (callback-based exit + AST gate + 24h smoke harness); personal-data processing paths (page.analyze pipeline / preference profiling / search / data.delete / data.export) are unchanged. No new audit_logs actions added, no new retention horizon added, zero SBOM drift (no new dependency; uses existing bullmq / ioredis APIs only; EU CRA 2026-09-11 compliance preserved). Satisfies GDPR Art.32(1)(b) processing integrity (BullMQ Lua transaction completion guarantee) + APPI Article 23 安全管理措置 (operational reliability improvement). This PR does not trigger GDPR Art.33 / APPI Article 26 personal data breach (worker lifecycle internals only, PII path unchanged).

Cross-ref

ADR-0034 (Callback-Based Worker Exit Pattern) / ADR-0009 (Bug 1 portion Superseded by ADR-0034; Bug 2 / Screenshot deletion unification was active under the PR6 7d TTL cron at the time of this entry, later superseded by PR-SS-B / ADR-0041 which removed the TTL cron — retention is now until data.delete) / ADR-0030 Amendment 2 Decision 8 (24h Integration Smoke Pre-Merge Gate, T+2d ADR-bound exception) / Plan v4.2 anchor 019e2c7e-3b25 / IO Plan Decision V2 anchor 019e2c88-7d5f / PR-A implementation anchor 019e2cbb-15b0.


Plan v4.3 PR-M Callback-Exit Teardown Symmetry L1.5 SLO_MARKER (ADR-0035, 2026-05-16) / Plan v4.3 PR-M コールバック exit teardown 対称性 L1.5 SLO_MARKER

Plan v4.3 PR-M (ADR-0035 Callback-Exit Teardown Symmetry) で registerCompletedListenerAndExit helper に disposeFn?: () => Promise<void> optional parameter が追加され、Promise.race([disposeFn(), setTimeout(EMBEDDING_DISPOSE_CEILING_MS)]) の ceiling 経路 fail-open に伴い 新規 audit_logs action embedding_dispose_timeout を 1 件追加。本セクションは PR-D-5 §Embedding Integrity SLO の L1.5 SLO_MARKER pattern (ADR-0018 Amendment 5) と ADR-0021 §Decision 4 L1.5 SLO_MARKER pattern alignment を踏襲し、embedding_dispose_timeoutoperational evaluation gate (Plan v4.4 dispatch trigger) として構造的に観測可能化する。

Plan v4.3 PR-M (ADR-0035 Callback-Exit Teardown Symmetry) extends registerCompletedListenerAndExit helper with an optional disposeFn?: () => Promise<void> parameter, and the Promise.race([disposeFn(), setTimeout(EMBEDDING_DISPOSE_CEILING_MS)]) ceiling-path fail-open adds a single new audit_logs action embedding_dispose_timeout. This section inherits the L1.5 SLO_MARKER pattern from PR-D-5 §Embedding Integrity SLO (ADR-0018 Amendment 5) and the L1.5 SLO_MARKER pattern alignment from ADR-0021 §Decision 4, structurally exposing embedding_dispose_timeout as an operational evaluation gate (Plan v4.4 dispatch trigger).

audit_logs action 列挙表 — new action value / audit_logs action enumeration — new value

既存 audit_logs.action 列挙集 (§11 Audit Log Data Retention + Worker lifecycle audit actions PR-D-8 + PR-D-5 SLO Embedding Integrity 2 actions + PR-D-6 collision-resolved 2 actions + PR-V3-T1a parent_rss_ceiling_scaled 1 action + ADR-0021 worker crash dump 3 actions) に PR-M で 1 種 new action を additive 追加。既存の 365 日 retention + truncateTargetId PII minimisation + system:<role> actor naming convention をすべて継承。

Appends 1 new action value to the existing audit_logs.action enumeration (§11 Audit Log Data Retention + Worker lifecycle audit actions PR-D-8 + PR-D-5 Embedding Integrity SLO 2 actions + PR-D-6 collision-resolved 2 actions + PR-V3-T1a parent_rss_ceiling_scaled 1 action + ADR-0021 worker crash dump 3 actions). Inherits the existing 365-day retention, truncateTargetId PII minimisation, and system:<role> actor naming convention.

Action (new) / 新規 actionActorTarget typeEmit condition / 発火条件RetentionTier mapping
embedding_dispose_timeoutsystem:embedding-backfill-workerworker (BullMQ job id, PII-truncated via truncateAuditTargetId)registerCompletedListenerAndExit の callback-exit listener body 内で Promise.race([disposeFn(), setTimeout(EMBEDDING_DISPOSE_CEILING_MS)]) の ceiling branch (default 5000ms) が disposeFn より先に firing し、disposeEmbeddingPipeline() 未完了のまま process.exit(0) を強制呼ぶ ceiling fail-open 経路で primary emit (ADR-0035 §Decision 1 canonical listener body pattern)。details JSON は { workerType, ceilingMs } 固定 numeric / enum string のみ (PII-free)、raw IPC payload や job-internal state は含まない (CWE-209 Information Exposure defense + GDPR Art.5(1)(c) data minimisation 維持)。/ Primary emit from the Promise.race ceiling fail-open branch inside the callback-exit listener body when the ceiling (default 5000ms) fires before disposeFn resolves, forcing process.exit(0) with disposeEmbeddingPipeline() incomplete. details carries only { workerType, ceilingMs } fixed numeric / enum string fields (PII-free).365dL1.5 SLO_MARKER (log-based metric source; Plan v4.4 dispatch trigger evaluation gate at firing rate > 5/hour, see §Operational evaluation gate below)

Operational evaluation gate (Plan v4.4 dispatch trigger) / 運用評価ゲート

JP: embedding_dispose_timeout primary emit の rate を per-100-cycle で集計し、< 5 rows/100 cyclesoperational stability threshold とする (ADR-0035 §Production verification protocol step 2)。本閾値を 超過 した場合、ADR-0035 §Consequences Negative §Trigger contract に従い Plan v4.4 dispatch MUST evaluate (per-job fork-only model 移行: outer Worker から ONNX Worker Thread を完全排除し、per-job child fork で ONNX inference を実行する設計)。本 ADR は Plan v4.4 dispatch を 約束しない が、L1.5 SLO_MARKER metric source として trigger gate を 構造的に観測可能 にする。

EN: Aggregate the primary-emit rate of embedding_dispose_timeout per-100-cycle; the operational stability threshold is < 5 rows / 100 cycles (ADR-0035 §Production verification protocol step 2). If exceeded, per ADR-0035 §Consequences Negative §Trigger contract, Plan v4.4 dispatch MUST evaluate migration to a per-job fork-only model (outer Worker excludes ONNX Worker Thread entirely; per-job child fork runs ONNX inference). This contract does NOT commit to Plan v4.4 dispatch but structurally exposes the trigger gate as an L1.5 SLO_MARKER metric source.

Query hooks / クエリ連携

Primary emit count (SQL, per-100-cycle aggregate via job_count window):

-- Operational stability evaluation: must remain < 5 rows / 100 cycles
-- Cycle = WorkerSupervisor planned-restart trigger (EMBEDDING_BACKFILL_MAX_JOBS_BEFORE_RESTART=3 default → ~33 cycles per ~100 jobs)
SELECT COUNT(*) AS dispose_timeout_count
FROM audit_logs
WHERE action = 'embedding_dispose_timeout'
  AND timestamp >= NOW() - INTERVAL '100 cycles equivalent';

L1.5 SLO_MARKER (log-based metric, separate from primary emit): Grafana Loki / equivalent アグリゲータで [SLO_MARKER] audit_log_emit_failed line rate を継続観測 (ADR-0018 Amendment 5 pattern 継承)。primary emit embedding_dispose_timeout 自体が fail-open path から発火するため、emitAuditLog(...).catch(() => {/* fail-open */}) で secondary degrade が発生した場合の blind spot を補完する。Reftrix repo 自体はクエリを同梱せず、operator の log-aggregation stack 設定に委譲。

L1.5 SLO_MARKER (log-based metric, separate from primary emit, EN): Continuously observe the [SLO_MARKER] audit_log_emit_failed line rate via Grafana Loki or equivalent aggregator (inheriting the ADR-0018 Amendment 5 pattern). Since the primary emit embedding_dispose_timeout itself fires from a fail-open path, this compensates the blind spot when a secondary degrade occurs in emitAuditLog(...).catch(() => {/* fail-open */}). The Reftrix repo does not bundle the query; it is delegated to the operator's log-aggregation stack configuration.

PII-safe 設計詳細 / PII-safe design details

  • targetId: truncateAuditTargetId() SSOT (8-char prefix + ... suffix) で BullMQ job id を truncate。AUDIT_LOG_CONSTANTS.TARGET_ID_TRUNCATE_LENGTH SSOT 経由で導出 (Wave 5 LCC endorsement、` §Canonical CWE-209 PII Protection Pattern)。

  • details: { workerType: 'embedding-backfill', ceilingMs: 5000 } 固定 numeric / enum string field のみ。raw IPC payload、job-internal embedding vector、screenshot blob 等は含まない (CWE-209 Information Exposure defense + GDPR Art.5(1)(c) data minimisation 維持)。

  • Actor: system:embedding-backfill-worker (PR-D-5 SSOT convention 継承、apps/mcp-server/src/audit/audit-actions.ts AUDIT_ACTOR_EMBEDDING_BACKFILL_WORKER constant 経由)。

  • Retention: 365 days inherited via AUDIT_LOG_CONSTANTS.DEFAULT_RETENTION_DAYS (§11 共通方針継承、新規 retention horizon 追加なし)。

  • targetId: BullMQ job id truncated via truncateAuditTargetId() SSOT (8-char prefix + ... suffix), derived through the AUDIT_LOG_CONSTANTS.TARGET_ID_TRUNCATE_LENGTH SSOT (Wave 5 LCC endorsement, ` §Canonical CWE-209 PII Protection Pattern).

  • details: only the fixed numeric / enum-string fields { workerType: 'embedding-backfill', ceilingMs: 5000 }; no raw IPC payloads, job-internal embedding vectors, or screenshot blobs (CWE-209 Information Exposure defense + GDPR Art.5(1)(c) data minimisation preserved).

  • Actor: system:embedding-backfill-worker (inherits the PR-D-5 SSOT convention via the AUDIT_ACTOR_EMBEDDING_BACKFILL_WORKER constant in apps/mcp-server/src/audit/audit-actions.ts).

  • Retention: 365 days inherited via AUDIT_LOG_CONSTANTS.DEFAULT_RETENTION_DAYS (inherits §11 common policy; no new retention horizon).

Storage limitation (GDPR Art.5(1)(e)) / 保管制限

JP (3 sentence binding):

  1. embedding_dispose_timeout エントリは既存 audit_logs の 365 日保持 view / cron (§11) に依存しており、新規 retention horizon の追加は不要。Art.30 処理活動記録として既存 365 日ポリシーをそのまま継承する (GDPR Art.5(1)(e) compliance 保持)。
  2. PR-D-5 Embedding Integrity SLO entries (embedding_parity_check_failed / embedding_parity_schema_drift) と同様、L1.5 SLO_MARKER の operator-side Grafana / Loki 設定は release-manager scope の別 PR で LCC 再監査を通す (本 PR-M scope では Reftrix repo 同梱しない)。
  3. details JSON は raw IPC payload を一切含まないため Art.5(1)(c) data minimisation 原則と整合、Art.5(1)(e) storage limitation 原則との整合性は §11 Audit Log Retention の既存ポリシーによって担保される。

EN (3 sentence binding):

  1. embedding_dispose_timeout entries rely on the existing audit_logs 365-day retention view / cron (§11); no new retention horizon is required. The entry inherits the existing 365-day policy unchanged as a GDPR Art.30 record of processing activity (Art.5(1)(e) compliance preserved).
  2. As with the PR-D-5 Embedding Integrity SLO entries (embedding_parity_check_failed / embedding_parity_schema_drift), operator-side configuration of the Grafana / Loki stack for L1.5 SLO_MARKER is routed through a separate release-manager-scope PR with an LCC re-audit (not bundled with the Reftrix repo in PR-M).
  3. The details JSON contains no raw IPC payload, aligning with Art.5(1)(c) data minimisation. Alignment with Art.5(1)(e) storage-limitation principles is guaranteed by the existing §11 Audit Log Retention policy.

GDPR Art.33 / EU CRA Article 14 disclaimer (4-axis) / GDPR Art.33 / EU CRA Article 14 4 軸 disclaimer

ADR-0035 §LCC compliance preservation block (e) を文書 mirror。

Mirrors ADR-0035 §LCC compliance preservation block (e).

軸 / Axis適用判定 / Applicability
GDPR Art.33 72h breach notificationNOT triggeredembedding_dispose_timeoutsystem availability incident (SIGABRT race window structural fail-open) であり personal data confidentiality / integrity breach に該当しない / embedding_dispose_timeout is a system availability incident (SIGABRT race window structural fail-open), not a personal-data confidentiality/integrity breach
EU CRA Article 14 24h ENISA reportNOT required — attacker-exploitable vulnerability に該当せず (external attacker surface 不在、privilege escalation 不能、information disclosure 不能、DoS amplification 不能: WorkerSupervisor maxRestartAttempts=10 で bounded) / Not attacker-exploitable (no external attacker surface, no privilege escalation, no information disclosure, no DoS amplification: bounded by maxRestartAttempts=10)
APPI 第26条 (personal data leak)NOT triggereddetails JSON は PII-free ({ workerType, ceilingMs } fixed enum/numeric only)、CWE-209 Information Exposure defense 維持 / Not triggered — details JSON is PII-free (fixed enum/numeric only), CWE-209 defense preserved
AGPL §5(a)/(b) modification notice満たす — 3-CHANGELOG bilingual JP/EN entry (root + apps/mcp-server + .claude/specs) を Phase 3 docs-sync で landing (per ` §3-CHANGELOG sync rules) / Satisfied — 3-CHANGELOG bilingual entries land in Phase 3 docs-sync

Cross-ref: ADR-0035 (Callback-Exit Teardown Symmetry) §Decision 1 canonical listener body pattern / §Decision 4 SSOT action / §Consequences Negative §Trigger contract / §LCC compliance preservation block (a)-(e). ADR-0018 Amendment 5 §L1.5 SLO_MARKER pattern. ADR-0021 §Decision 4 L1.5 SLO_MARKER pattern alignment. Plan v4.3 PR-M IO Plan Decision V0 anchor 019e306d-364d. Plan v4.3 PR-M IO Impl Decision V1 anchor 019e30c1-597e. SEC Impl Audit anchor 019e30ae-cef5. INV-AUDIT-EMIT-SSOT-IMPORT-001 ext Test 8 AST sweep (production sweep guarantees bare system:embedding-backfill literal 0 件、Wave 5 LCC canonical 019df7ab-2f5a 整合).


v0.5.1 ReDoS Mitigation Throughput Floor (v0.5.1 patch release, 2026-05-07) / v0.5.1 ReDoS 緩和 throughput 下限値

CO-PR21-REVIEW-01 (ADR-0020 Decision 5) で導入された CSS-animation parser ReDoS test の input-size 比例 threshold は、active exploit watch 期間中の CWE-770 mitigation completeness を構造的に保証する運用契約。本セクションは (1) throughput baseline の運用 disclaimer、(2) GDPR Art.33 / APPI 第26条 escalation path を documented contract として記録する。

The input-size proportional threshold for the CSS-animation parser ReDoS test, introduced in CO-PR21-REVIEW-01 (ADR-0020 Decision 5), is the operational contract structurally guaranteeing CWE-770 mitigation completeness during the active exploit watch window. This section records (1) throughput baseline operational disclaimers and (2) GDPR Art.33 / APPI 第26条 escalation paths as documented contracts.

Throughput floor contract / Throughput 下限値契約

  • EN: "CSS-animation parser ReDoS test threshold uses input-size proportional formula expectedMaxMs = input.length / throughputCharsPerMs + 200ms with REDOS_TEST_THROUGHPUT_CHARS_PER_MS env var override. Local default 100 chars/ms; CI sets 80 chars/ms via .github/workflows/ci.yml env injection (post-F1+F2 runner measured baseline per TPA-04⚯TDA-02 unified review). CWE-770 mitigation completeness verified per ADR-0020 Decision 5."
  • JP: "CSS-animation parser ReDoS test threshold は input-size 比例式 expectedMaxMs = input.length / throughputCharsPerMs + 200ms を採用、env var REDOS_TEST_THROUGHPUT_CHARS_PER_MS で override 可。Local default 100 chars/ms、CI は .github/workflows/ci.yml の env injection で 80 chars/ms (post-F1+F2 CI runner 実測 baseline、TPA-04⚯TDA-02 unified review per)。ADR-0020 Decision 5 per CWE-770 mitigation completeness 検証済。"

env var unset 時は default 100 chars/ms で fallback、後方互換を保つ。Phase 2.3.5 (Plan v0.5.1) で test-qa-engineer + security-engineer が CI runner 上で 3 input sizes (small ≤ 500 chars / medium 500-2000 / large 2000-5000) × 3 連続 measurement を実施し chars/ms median を実測。Phase 2.4 で env var を .github/workflows/ci.yml に injection。

When the env var is unset, the local default (100) applies as a backwards-compatible fallback. Phase 2.3.5 (Plan v0.5.1): test-qa-engineer + security-engineer perform a 3-input-size × 3-consecutive measurement on the CI runner; Phase 2.4 injects the env var into .github/workflows/ci.yml.

GDPR Art.33 / APPI 第26条 escalation path / GDPR Art.33 / APPI 第26条 エスカレーション経路

  • EN: "Active exploit watch incident detection → security-engineer + LCC engagement within 24h → 72h supervisory authority notification gate (only if PII processing path affected). v0.5.1 itself does not trigger Art.33 (test infrastructure + CI workflow only changes, no PII path). The CSS-animation parser is invoked during page.analyze for layout / motion classification; it does not directly process PII fields, but operates on web page HTML which may transit PII. ReDoS regression in the parser would degrade CI green path stability without breaching personal data confidentiality / integrity, so v0.5.1's CI flaky structural fixes do not constitute a personal data breach trigger."
  • JP: "Active exploit watch incident 検知 → 24時間以内に security-engineer + LCC エスカレート → 72時間 supervisory authority 通知ゲート (PII 処理パス影響時のみ)。v0.5.1 自体は Art.33 を trigger しない (test infra + CI workflow only changes、PII path 不変)。CSS-animation parser は page.analyze で layout / motion 分類に呼ばれ、PII field を直接処理しないが PII を含み得る web page HTML を operand とする。Parser の ReDoS regression は CI green path stability を degrade するが personal data confidentiality / integrity を breach しないため、v0.5.1 の CI flaky 構造的修復は personal data breach trigger に該当しない。"

active exploit watch (SEC sign-off condition 2 / 019de070-65f5-77bc-b3b9-6017b35d85c3) の incident detection は publish window (2026-05-01 〜 2026-05-07) に security-engineer が CISA KEV / NVD / GHSA の 6-day diff で reassess し、新規 advisory が apps/mcp-server SBOM (CycloneDX 1.6、1300 components、last-generated 2026-04-29 from v0.5.0 baseline) の dependency tree に該当する場合のみ publish BLOCK + IO escalate。Plan §2.4 Step 4.2.5 で signed-off。

Active exploit watch incident detection (SEC sign-off condition 2 / 019de070-65f5-77bc-b3b9-6017b35d85c3): during the publish window (2026-05-01 〜 2026-05-07), security-engineer reassesses CISA KEV / NVD / GHSA via 6-day diff; publish BLOCKs and escalates to IO only if a new advisory matches the apps/mcp-server SBOM (CycloneDX 1.6, 1300 components, last-generated 2026-04-29 from v0.5.0 baseline) dependency tree. Signed off in Plan §2.4 Step 4.2.5.

既存 retention ポリシー (screenshot 7d TTL / audit_logs 365d / preference profile retention / search log retention / etc.) には一切変更なし。v0.5.1 の変更は test infrastructure + CI workflow に限定され、personal data 処理経路に影響しない。

No changes to existing retention policies (screenshot 7d TTL / audit_logs 365d / preference profile retention / search log retention / etc.). v0.5.1 changes are limited to test infrastructure + CI workflow with no impact on personal-data processing paths.


変更履歴 / Changelog

日付 / Dateバージョン / Version内容 / Description
2026-05-172.19.0Plan v4.2 PR-A/B/C landing (Phase 3 docs sync): Worker lifecycle pattern を callback-based exit (BullMQ native worker.once('completed', () => process.exit(0)) pre-registered listener) に変更、AST gate scripts/verify-completed-listener-sync.mjs で listener body の synchronous-only 強制 (SEC M-NEW-1)。新規 audit_logs action 追加なし (Plan v4.1 で proposed された worker_timeout_fired / worker_job_orphan_detected は Option B timeout-less 採用により drop、orphan detection は BullMQ native stalled event + 既存 worker_job_lifecycle.event_type=release で代替)。新規 retention horizon 追加なし、既存 365d retention / truncateTargetId SSOT PII minimisation / system:<role> actor naming convention すべて継承、SBOM drift ゼロ (新規依存追加なし、既存 bullmq / ioredis API のみ使用、EU CRA 2026-09-11 compliance 保持)。GDPR Art.30 records of processing coverage は既存 365d retention 契約で継承、Art.32(1)(b) processing integrity (BullMQ Lua transaction completion guarantee) + APPI 第23条 安全管理措置 (operational reliability 向上) を充足。本 PR は GDPR Art.33 / APPI 第26条 personal data breach trigger に該当しない (worker lifecycle internals only、PII path 不変)。ADR-0034 (Accepted) + ADR-0009 (Bug 1 portion Superseded by ADR-0034) + ADR-0030 Amendment 2 Decision 8 (24h Integration Smoke Pre-Merge Gate, T+2d ADR-bound exception) + Plan v4.2 anchor 019e2c7e-3b25 + IO Plan Decision V2 anchor 019e2c88-7d5f + PR-A implementation anchor 019e2cbb-15b0 参照。 / Plan v4.2 PR-A/B/C landing (Phase 3 docs sync): Worker lifecycle pattern changed to callback-based exit (BullMQ native worker.once('completed', () => process.exit(0)) pre-registered listener); AST gate scripts/verify-completed-listener-sync.mjs enforces synchronous-only listener body (SEC M-NEW-1). No new audit_logs actions added (Plan v4.1's proposed worker_timeout_fired / worker_job_orphan_detected dropped due to Option B timeout-less; orphan detection replaced by BullMQ native stalled event + existing worker_job_lifecycle.event_type=release). No new retention horizon added; existing 365d retention / truncateTargetId SSOT PII minimisation / system:<role> actor naming convention all inherited; zero SBOM drift (no new dependency; uses existing bullmq / ioredis APIs only; EU CRA 2026-09-11 compliance preserved). GDPR Art.30 records of processing coverage continues under existing 365d retention contract; satisfies Art.32(1)(b) processing integrity (BullMQ Lua transaction completion guarantee) + APPI Article 23 安全管理措置 (operational reliability improvement). This PR does not trigger GDPR Art.33 / APPI Article 26 personal data breach (worker lifecycle internals only, PII path unchanged). See ADR-0034 (Accepted) + ADR-0009 (Bug 1 portion Superseded by ADR-0034) + ADR-0030 Amendment 2 Decision 8 (24h Integration Smoke Pre-Merge Gate, T+2d ADR-bound exception) + Plan v4.2 anchor 019e2c7e-3b25 + IO Plan Decision V2 anchor 019e2c88-7d5f + PR-A implementation anchor 019e2cbb-15b0.
2026-05-042.18.0PR-V3-T1a Phase 2 Step 5+6 implementation landing (Phase 3 docs sync, V2-CO-02): §11 audit_logs.action 列挙表に parent_rss_ceiling_scaled row を追加 (actor system:phase5-init, targetType (none), retention 365d, PII-free details payload {before_mb, after_mb, trigger, commit_sha} numeric/fixed-string only, idempotent per-Phase-5-init emission)。本 row は LCC F-M-07 cluster sign-off (V1-CO-05, deadline 2026-05-20、CONDITIONAL APPROVE rendered 16-day early) の docs sync 経路で land、PR-V3-T1a Phase 2 Step 7 LCC Impl Audit FIND-IMPL-LCC-L-01 closure。365d retention / truncateTargetId SSOT (AUDIT_LOG_CONSTANTS.TARGET_ID_TRUNCATE_LENGTH = 8) PII minimisation / system: prefix actor naming convention は既存 PR-D-8 SSOT contract から完全継承、新規 retention horizon 追加なし。GDPR Art.30 records of processing activities + Art.5(1)(c) data minimisation + Art.32(1)(b) integrity of processing 契約を充足、L3 CRIT breach notification gate には影響なし (embedding_parity_schema_drift sentinel が唯一 trigger)。SBOM drift ゼロ (新規依存追加なし、audit-actions.ts SSOT const-tuple のみ追加、EU CRA 2026-09-11 compliance 保持)。本 PR は GDPR Art.33 / APPI 第26条 personal data breach trigger に該当しない (Phase 5 init system event、PII path 不変)。ADR-0013 Amendment 1 §Consequences (actor naming mandate system:phase5-init 整合化、FIND-IMPL-LCC-M-01 Path A landing) + ADR-0007 Amendment 1 §Consequences (PR-D-8 SSOT inheritance paragraph 追加、FIND-IMPL-LCC-L-02 landing) も同 Phase 3 docs sync で land。PR-V3-T1a Phase 2 Step 8 IO V1 Impl Decision anchor 019df508-e803 + LCC Impl Audit anchor 019df4f1-fe2e 参照。 / PR-V3-T1a Phase 2 Step 5+6 implementation landing (Phase 3 docs sync, V2-CO-02): Added parent_rss_ceiling_scaled row to the §11 audit_logs.action enumeration table (actor system:phase5-init, targetType (none), 365d retention, PII-free details payload with {before_mb, after_mb, trigger, commit_sha} numeric/fixed-string fields only, idempotent per-Phase-5-init emission). This row is landed via the docs sync route for LCC F-M-07 cluster sign-off (V1-CO-05, deadline 2026-05-20, CONDITIONAL APPROVE rendered 16 days early), closing PR-V3-T1a Phase 2 Step 7 LCC Impl Audit FIND-IMPL-LCC-L-01. 365d retention / truncateTargetId SSOT (AUDIT_LOG_CONSTANTS.TARGET_ID_TRUNCATE_LENGTH = 8) PII minimisation / system: prefix actor naming convention are fully inherited from the existing PR-D-8 SSOT contract; no new retention horizon added. Satisfies GDPR Art.30 records of processing activities + Art.5(1)(c) data minimisation + Art.32(1)(b) integrity of processing contracts; no impact on the L3 CRIT breach-notification gate (embedding_parity_schema_drift sentinel remains the sole trigger). Zero SBOM drift (no new dependency; only audit-actions.ts SSOT const-tuple addition; EU CRA 2026-09-11 compliance preserved). This PR does not trigger GDPR Art.33 / APPI Article 26 personal data breach (Phase 5 init system event, PII path unchanged). ADR-0013 Amendment 1 §Consequences (actor-naming mandate aligned to system:phase5-init, FIND-IMPL-LCC-M-01 Path A landing) + ADR-0007 Amendment 1 §Consequences (PR-D-8 SSOT inheritance paragraph added, FIND-IMPL-LCC-L-02 landing) are landed in the same Phase 3 docs sync. See PR-V3-T1a Phase 2 Step 8 IO V1 Impl Decision anchor 019df508-e803 + LCC Impl Audit anchor 019df4f1-fe2e.
2026-05-032.17.0Plan v2 §1 base contract landing (PR-Bα-1, Unreleased): Worker lifecycle 内部実装の改修 (S1.1 pre-return pause + dispose helper の success-path 限定 type-level 復活、S1.2 IPC_SHUTDOWN_GRACE_MS env-overridable IIFE 化 default 30,000ms / range 1,000-120,000ms / WORKER_IPC_SHUTDOWN_GRACE_MS、Cond 7 EmbeddingBackfillQueue.attempts:1 contract-level 統一)、6 件の INV-* CI-failing executable test を tests/regression/standing/worker-lifecycle/ に新設、commit 5865e4a7apps/mcp-server/ 内 LoC delta +945/−89 (net +856)。保持契約への影響なし / no-impact note: 本 PR は GDPR Art.17 削除契約 (data.delete 経由 11 pgvector tables 同期削除、3s SLA、M2 実測 27.39ms) / Art.30 audit_logs retention (365日、truncateTargetId PII minimisation、system:<role> actor naming) / screenshot 7d TTL contract (<REFTRIX_SCREENSHOT_ROOT>/phase5/<webPageId>.png の TTL cron 削除、PR6 経路) / preference profile retention / search log retention のいずれにも影響しない (Worker ライフサイクル改修と retention contract は orthogonal)。Personal data 処理経路 (page.analyze pipeline / preference profiling / search / data.delete / data.export) は不変。新規 retention horizon 追加なし、SBOM drift ゼロ (新規依存追加なし、既存 bullmq / ioredis API のみ使用、EU CRA 2026-09-11 compliance 保持)。GDPR Art.32(1)(b) processing integrity に貢献 (BullMQ silent stall regression 解消による operational reliability 向上)、APPI 第23条 安全管理措置 (technical reliability 向上)。本 PR は GDPR Art.33 / APPI 第26条 personal data breach trigger に該当しない (test infra + worker lifecycle internals only、PII path 不変)。3-CHANGELOG [Unreleased] PR-Bα-1 entry + Plan v2 §1 base anchor 019de97f-1dcf + IO Impl Decision anchor 019dee28-3604 参照。 / Plan v2 §1 base contract landing (PR-Bα-1, Unreleased): Worker lifecycle internals refactor (S1.1 success-path-only type-level restoration of pre-return pause + dispose helper; S1.2 IPC_SHUTDOWN_GRACE_MS env-overridable IIFE — default 30,000ms / range 1,000-120,000ms / WORKER_IPC_SHUTDOWN_GRACE_MS; Cond 7 EmbeddingBackfillQueue.attempts:1 unified at contract level), with 6 new INV-* CI-failing executable tests landed under tests/regression/standing/worker-lifecycle/, commit 5865e4a7, LoC delta within apps/mcp-server/ +945/−89 (net +856). No-impact note: This PR has no effect on the GDPR Art.17 deletion contract (data.delete 11-pgvector-table synchronous deletion, 3s SLA, M2 measured 27.39ms) / Art.30 audit_logs retention (365d, truncateTargetId PII minimisation, system:<role> actor naming) / screenshot 7d TTL contract (<REFTRIX_SCREENSHOT_ROOT>/phase5/<webPageId>.png deletion via the PR6 TTL cron path) / preference profile retention / search log retention — worker lifecycle internals and retention contracts are orthogonal. Personal-data processing paths (page.analyze pipeline / preference profiling / search / data.delete / data.export) are unchanged. No new retention horizon added; SBOM drift zero (no new dependency; uses existing bullmq / ioredis APIs only; EU CRA 2026-09-11 compliance preserved). Contributes to GDPR Art.32(1)(b) processing integrity (resolution of BullMQ silent stall regression improves operational reliability); APPI Article 23 安全管理措置 (technical reliability improvement). This PR does not trigger GDPR Art.33 / APPI Article 26 personal data breach (test infra + worker lifecycle internals only, PII path unchanged). See 3-CHANGELOG [Unreleased] PR-Bα-1 entry + Plan v2 §1 base anchor 019de97f-1dcf + IO Impl Decision anchor 019dee28-3604.
2026-05-072.16.0v0.5.1 patch release (CI flaky systemic remediation, ADR-0020 Decisions 1-5): F1 (Vitest maxWorkers: 3 → 2、SSOT 一元化) + F2 (top-level fileParallelism: false) + F3 (actions/cache@v4 for Playwright browser at 3 callsites in .github/workflows/ci.yml lines 154/224/382) + F4 (LocalStorageProvider mock-replace in Phase 1 integration test、新規 compensation unit test 同一 commit landing per R-11 mandate; RENAME-PROVIDER-01: Plan/ADR の class identifier MemoryStorageProvider を T1 Canonical LocalStorageProvider に一元化、test compensation file path の historical 命名は v0.6.0 backlog) + CO-PR21-REVIEW-01 (CSS-animation parser ReDoS threshold input-size proportional expectedMaxMs = input.length / throughputCharsPerMs + 200ms + env var REDOS_TEST_THROUGHPUT_CHARS_PER_MS override、local default 100 chars/ms + CI 80 chars/ms via env injection per TPA-04⚯TDA-02 unified)。CWE-770 mitigation completeness は bounded-growth detection で維持。SEC sign-off APPROVE WITH CONDITIONS (019de070-65f5-77bc-b3b9-6017b35d85c3) 3 conditions 完全継承 (no scope shrink + active exploit watch + canonical bilingual CHANGELOG annotation in 3-CHANGELOG)。本 v0.5.1 の変更は test infrastructure + CI workflow に限定、personal data 処理経路 (page.analyze pipeline / preference profiling / search / data.delete / data.export 等) に影響しない。既存 retention ポリシー (screenshot 7d TTL / audit_logs 365d / preference profile retention / search log retention / etc.) 一切変更なし。SBOM (CycloneDX 1.6、1300 components、last-generated 2026-04-29 from v0.5.0 baseline) は v0.5.0 と bit-identical (production runtime / dependency tree 不変、pnpm-lock.yaml 不変)、EU CRA Art.13(8)/14(2) transparency obligation 履行証拠として機能、EU CRA 2026-09-11 compliance 保持。GDPR Art.32(1)(b) processing integrity に貢献 (CI green path 安定化)、APPI 第23条 安全管理措置 (継続的脆弱性低減 + technical reliability 向上)。本 v0.5.1 は GDPR Art.33 / APPI 第26条 personal data breach trigger に該当しない (test infra only、PII path 不変)。ADR-0020 + Plan v1.0 + 3-CHANGELOG [0.5.1] 参照。 / v0.5.1 patch release (CI flaky systemic remediation, ADR-0020 Decisions 1-5): F1 (Vitest maxWorkers: 3 → 2, SSOT consolidation) + F2 (top-level fileParallelism: false) + F3 (actions/cache@v4 for Playwright browser at 3 callsites in .github/workflows/ci.yml lines 154/224/382) + F4 (LocalStorageProvider mock-replace in Phase 1 integration test, new compensation unit test lands in same commit per R-11 mandate; RENAME-PROVIDER-01: Plan/ADR class identifier MemoryStorageProvider unified to T1 Canonical LocalStorageProvider; test compensation file path historical naming carried to v0.6.0 backlog) + CO-PR21-REVIEW-01 (CSS-animation parser ReDoS threshold input-size proportional expectedMaxMs = input.length / throughputCharsPerMs + 200ms + env var REDOS_TEST_THROUGHPUT_CHARS_PER_MS override; local default 100 chars/ms + CI 80 chars/ms via env injection per TPA-04⚯TDA-02 unified). CWE-770 mitigation completeness preserved as bounded-growth detection. SEC sign-off APPROVE WITH CONDITIONS (019de070-65f5-77bc-b3b9-6017b35d85c3) 3 conditions fully inherited (no scope shrink + active exploit watch + canonical bilingual CHANGELOG annotation in 3-CHANGELOG). v0.5.1 changes are limited to test infrastructure + CI workflow, with no impact on personal-data processing paths (page.analyze pipeline / preference profiling / search / data.delete / data.export / etc.). No changes to existing retention policies. SBOM (CycloneDX 1.6, 1300 components, last-generated 2026-04-29 from v0.5.0 baseline) is bit-identical to v0.5.0 (production runtime / dependency tree unchanged, pnpm-lock.yaml unchanged); functions as EU CRA Art.13(8)/14(2) transparency obligation evidence; EU CRA 2026-09-11 compliance preserved. Contributes to GDPR Art.32(1)(b) processing integrity (CI green path stabilisation); APPI 第23条 安全管理措置 (continuous vulnerability reduction + technical reliability). v0.5.1 does not trigger GDPR Art.33 / APPI 第26条 personal data breach (test infra only, PII path unchanged). See ADR-0020 + Plan v1.0 + 3-CHANGELOG [0.5.1].
2026-04-272.15.0v0.4.0 PR-D-9-patch (post-merge runtime silent stall regression fix): IPC schema jobId が <UUID><UUID>__<category> 複合キー両形式を BACKFILL_JOB_ID_REGEX SSOT z.union 経由で受容するよう修復。SLO 5-tier (L0/L1/L1.5/L2/L3) 構造は 不変 (本 patch は schema-side fix のみで audit_logs.action 追加なし、retention 365d / truncateTargetId PII minimisation / system:embedding-backfill-worker actor naming すべて継承)。Standing regression inv-worker-lock-003-embedding-backfill-supervisor.test.ts cases #20-#23 (post-renumber, INV-WORKER-LOCK-003 + INV-DATA-ACCURACY-001 cross-binding) を新設し、IPC handshake が両形式を accept することを CI で継続検証 (167/167 PASS pass^3 3 consecutive)。SEC + LCC sign-offs honored per ADR-0011 Amendment 3 (SEC-PATCH-01 Path B ADR accepted-risk via SEC sign-off 019dcabf-becdBACKFILL_JOB_ID_REGEX complete-anchor regex + EMBEDDING_BACKFILL_CATEGORIES SSOT enum + 5.89ms reject empirical evidence で per-string upper bound 不要)。GDPR Art.5(1)(d) accuracy invariant 保全、Art.32(1)(b) "ongoing resilience of processing systems" 文脈の operational reliability degradation 解消、Art.30 audit_logs 365d retention 不変、APPI 第23条 安全管理措置 reliability gap 解消。Not a CRA Article 14 vulnerability scope item — operational reliability blocker, not attacker-exploitable, no Article 14 disclosure obligation triggered。SBOM drift ゼロ (新規依存追加なし、既存 zod / @prisma/client / bullmq / ioredis / EMBEDDING_BACKFILL_CATEGORIES SSOT API のみ使用、EU CRA 2026-09-11 compliance 保持)。ADR-0011 Amendment 3 + Plan §13 + Registry §13 + §13.11 参照。 / v0.4.0 PR-D-9-patch (post-merge runtime silent stall regression fix): Restored IPC schema jobId acceptance for both <UUID> and <UUID>__<category> composite-key forms via BACKFILL_JOB_ID_REGEX SSOT z.union. SLO 5-tier (L0/L1/L1.5/L2/L3) structure unchanged (this patch is schema-side only; no new audit_logs.action; 365d retention / truncateTargetId PII minimisation / system:embedding-backfill-worker actor naming all inherited). New standing regression inv-worker-lock-003-embedding-backfill-supervisor.test.ts cases #20-#23 (post-renumber, INV-WORKER-LOCK-003 + INV-DATA-ACCURACY-001 cross-binding) continuously verifies in CI that IPC handshake accepts both forms (167/167 PASS pass^3 3 consecutive). SEC + LCC sign-offs honored per ADR-0011 Amendment 3 (SEC-PATCH-01 Path B ADR accepted-risk via SEC sign-off 019dcabf-becd; no per-string upper bound needed because of BACKFILL_JOB_ID_REGEX complete-anchor regex + EMBEDDING_BACKFILL_CATEGORIES SSOT enum + 5.89ms empirical reject evidence). Preserves GDPR Art.5(1)(d) accuracy invariant, eliminates Art.32(1)(b) "ongoing resilience of processing systems" operational reliability degradation, Art.30 audit_logs 365d retention unchanged, resolves APPI Article 23 安全管理措置 reliability gap. Not a CRA Article 14 vulnerability scope item — operational reliability blocker, not attacker-exploitable, no Article 14 disclosure obligation triggered. SBOM drift zero (no new dependency; uses only existing zod / @prisma/client / bullmq / ioredis / EMBEDDING_BACKFILL_CATEGORIES SSOT APIs; EU CRA 2026-09-11 compliance preserved). See ADR-0011 Amendment 3 + Plan §13 + Registry §13 + §13.11.
2026-04-232.14.0v0.4.0 PR-D-6: Embedding Integrity SLO §audit_logs action 列挙表に 2 new action 追加 — embedding_backfill_collision_resolved (actor system:embedding-backfill-queue, primary emit, L0/L1/L2 source, Zod CollisionAuditPayloadSchema 5-field contract) + page_analyze_collision_resolved (actor system:page-analyze-queue, observability-only emit, L0/L1/L2 source, Registry v3 §3 FIND-TPA-02 binding per observability-only scope)。両 action とも既存 365 日 retention / truncateId SSOT PII minimisation / system:<role> actor naming convention を継承、L3 CRIT breach notification gate には影響なし (embedding_parity_schema_drift sentinel が唯一 trigger)。domain-specific actor naming は T1 SoT Hierarchy resolution (ADR-0018 Amendment 6 §Privacy Considerations Update)。新規 retention horizon 追加なし、SBOM drift ゼロ (UUIDv7 は Node.js 20.19+ stdlib crypto.randomUUID({version:7}))、EU CRA 2026-09-11 compliance 保持。ADR-0018 Amendment 6 参照。 / v0.4.0 PR-D-6: Appended 2 new audit_logs.action values to the Embedding Integrity SLO action enumeration — embedding_backfill_collision_resolved (actor system:embedding-backfill-queue, primary emit, L0/L1/L2 source, Zod CollisionAuditPayloadSchema 5-field contract) + page_analyze_collision_resolved (actor system:page-analyze-queue, observability-only emit, L0/L1/L2 source, Registry v3 §3 FIND-TPA-02 binding per observability-only scope). Both inherit the existing 365-day retention, truncateId SSOT PII minimisation, and system:<role> actor naming convention; no impact on the L3 CRIT breach-notification gate (embedding_parity_schema_drift sentinel remains the sole trigger). Domain-specific actor naming reflects T1 SoT Hierarchy resolution (ADR-0018 Amendment 6 §Privacy Considerations Update). No new retention horizon added, zero SBOM drift (UUIDv7 via Node.js 20.19+ stdlib crypto.randomUUID({version:7})), EU CRA 2026-09-11 compliance preserved. See ADR-0018 Amendment 6.
2026-04-222.13.0v0.4.0 PR-D-5: Embedding Integrity SLO セクション追加 (INV-EMBEDDING-INTEGRITY-001 Partial → Full landing + 5-tier SLO L0/L1/L1.5/L2/L3)。audit_logs.actionembedding_parity_check_failed (primary emit, L1/L2 source) + embedding_parity_schema_drift (sentinel emit, L3 CRIT + GDPR Art.33 breach notification gate source) の 2 new value 追記。両 action とも既存 365 日 retention / truncateTargetId / system:embedding-backfill-worker actor naming を継承。L1.5 SLO_MARKER tier は [SLO_MARKER] audit_log_emit_failed log-based metric で primary emit failure rate を観測。FIND-LCC-02 (M, deadline 2026-05-04) binding: SLO query layer は既存 365 日 view に依存し新規 retention horizon 不要、Grafana dashboard は release-manager scope の別 PR で LCC 再監査、sentinel entry も既存 365 日継承 (Art.5(1)(e) compliance 保持)。ADR-0018 Amendment 5 参照。 / v0.4.0 PR-D-5: Added Embedding Integrity SLO section (INV-EMBEDDING-INTEGRITY-001 Partial → Full landing + 5-tier SLO L0/L1/L1.5/L2/L3). Appended 2 new audit_logs.action values: embedding_parity_check_failed (primary emit, L1/L2 source) + embedding_parity_schema_drift (sentinel emit, L3 CRIT + GDPR Art.33 breach-notification gate source). Both inherit the existing 365-day retention / truncateTargetId / system:embedding-backfill-worker actor naming. The L1.5 SLO_MARKER tier observes primary-emit failure rate via the [SLO_MARKER] audit_log_emit_failed log-based metric. FIND-LCC-02 (M, deadline 2026-05-04) binding: the SLO query layer relies on the existing 365-day view (no new retention horizon), Grafana dashboards are routed through a separate release-manager-scope PR with LCC re-audit, and sentinel entries also inherit the existing 365-day retention (Art.5(1)(e) compliance preserved). See ADR-0018 Amendment 5.
2026-04-192.12.0v0.4.0 PR7e-β4 M2 (Phase 3 docs sync): motion drift fix + 11 pgvector table enumeration。LCC-M2-01: §1 motion_patterns note を「onDelete: SetNull で残存」→「PAGE_RELATED_PATTERN_TABLES allowlist に明示含有で確実に CASCADE 削除」に訂正 (T1 Canonical gdpr-deletion.service.ts:59-71 に同期、INV-DATA-DELETE-002 standing regression で契約レベル検証)。LCC-M2-02: §3.2 に pgvector 11 table 削除契約の完全列挙表 (9 page-linked embedding + quality_benchmarks 二経路 OR + design_snapshot_sections cascade) と 3s SLA 契約 (M2 実測 27.39ms、limit 1% 未満) を追加 (ADR-0016 Amendment 2-A / 3-B と整合)。保持期間そのものに変更なし。OSS sync required by next release. / v0.4.0 PR7e-β4 M2 (Phase 3 docs sync): motion drift fix + 11 pgvector table enumeration. LCC-M2-01: Corrected §1 motion_patterns note from "retained under onDelete: SetNull" to "reliably CASCADE-deleted via explicit inclusion in PAGE_RELATED_PATTERN_TABLES allowlist" (synced with T1 Canonical gdpr-deletion.service.ts:59-71; contract-level verification via INV-DATA-DELETE-002 standing regression). LCC-M2-02: Added full enumeration table of 11-table pgvector deletion contract in §3.2 (9 page-linked embeddings + quality_benchmarks two-path OR + design_snapshot_sections cascade) along with the 3s SLA contract (M2 measured 27.39ms, <1% of limit), consistent with ADR-0016 Amendment 2-A / 3-B. No retention-period changes. OSS sync required by next release.
2026-04-172.11.0v0.4.0 PR7e-β3: §1 対象テーブル表に motion_patterns / motion_embeddings を追記。motion_patterns.webPageonDelete: SetNull のため GDPR Art.17 data.delete で web_page 削除時に残存する既知ギャップを明記 (別 PR で data.delete に motion カバレッジ拡張予定)。保持期間そのものに変更なし、既存の screenshot 7d TTL / audit_logs 365d / web_page ライフサイクル準拠ポリシーは不変。(2026-04-19 v2.12.0 で本 entry の「既知ギャップ」記述は訂正された。motion_patternsdata.delete allowlist に明示的に含まれており確実に削除される。v2.12.0 entry 参照。) / v0.4.0 PR7e-β3: Added motion_patterns / motion_embeddings rows to the §1 scope table. Documented the known GDPR Art.17 gap: motion_patterns.webPage uses onDelete: SetNull, so data.delete on a web_page leaves motion rows orphaned (full coverage will be added in a follow-up PR extending data.delete). No retention-period changes — screenshot 7d TTL / audit_logs 365d / web-page-lifecycle policies unchanged. (The "known gap" claim in this entry was corrected on 2026-04-19 in v2.12.0. motion_patterns is explicitly included in the data.delete allowlist and is reliably deleted. See the v2.12.0 entry.)
2026-04-172.10.0v0.4.0 PR7e-β1: Visual Embedding 根本原因修復 (ADR-0012)。ONNX Worker Thread resourceLimits.maxOldGenerationSizeMb=4096 (env ML_WORKER_MAX_OLD_SPACE_MB) 明示指定で CPU fallback OOM を解消、Phase 5 parent RSS ceiling 3072→4096 段階緩和、PHASE5_MAX_SECTIONS_INPUT=50 (GDPR Art. 5(1)(c) data minimisation) 新設、loadEnvLocal() 共通ヘルパー抽出 (SEC-β-01 maxDepth=5 / SEC-β-07 verbose=false, password 漏洩防止)、bootstrapAuditLogServiceForScript() 追加で repair script の audit_logs 書き込みを永続化 (GDPR Art.30)。SEC-β-02 運用ガイドで coredump 抑止 (ulimit -c 0) を推奨。既存 retention policy は不変 (screenshot 7d / audit_logs 365d)。DATABASE_URL sanitize regression test 追加。/ v0.4.0 PR7e-β1: Visual Embedding root-cause remediation (ADR-0012). Explicit resourceLimits.maxOldGenerationSizeMb=4096 (env ML_WORKER_MAX_OLD_SPACE_MB) on ONNX Worker Threads to stop CPU-fallback OOM, Phase 5 parent RSS ceiling relaxed 3072→4096, new PHASE5_MAX_SECTIONS_INPUT=50 (GDPR Art. 5(1)(c) data minimisation), shared loadEnvLocal() helper (SEC-β-01 maxDepth=5 / SEC-β-07 verbose=false, no password leak), bootstrapAuditLogServiceForScript() ensuring repair-script audit_logs writes actually persist (GDPR Art.30). SEC-β-02 operational guidance recommends coredump disablement (ulimit -c 0). Existing retention policies unchanged (screenshot 7d / audit_logs 365d). Adds DATABASE_URL sanitize regression test.
2026-04-162.9.0v0.4.0 PR7e-α: 遡及修復スクリプト repair-page-analyze.ts セクション追加。PR7e-Ω 7 バグクラスタで放置された stale backfill 行を救出。part_visual + section_visual 再投入。GDPR Art. 5(1)(e) 整合性を明記 (遡及処理は保管期間を延長しない)。audit_logs (embedding_backfill_repair / _repair_dryrun action) で Art. 30 処理活動記録。9 SEC ガードレール + SEC MED-4 idempotency key (sha256) + WorkerActiveLockService pre-flight probe。ADR-0012 参照。 / v0.4.0 PR7e-alpha: added retrospective repair script repair-page-analyze.ts section. Rescues stale backfill rows left behind by the PR7e-Omega 7-bug cluster, re-enqueuing part_visual + section_visual. Explicitly documents GDPR Art. 5(1)(e) alignment (retrospective reprocessing does not extend retention). audit_logs (embedding_backfill_repair / _repair_dryrun action) records Art. 30 processing activity. 9 SEC guardrails + SEC MED-4 idempotency key (sha256) + WorkerActiveLockService pre-flight probe. See ADR-0012.
2026-04-152.8.0v0.4.0 PR7d-3: ドキュメント同期 + 監査要件対応。cleanupExpired の TTL cron 実行で削除件数 > 0 時に audit_logs にエントリ作成 (action=screenshot_ttl_cleanup, actor=system:screenshot-cleanup-cron, GDPR Art.30)。repair-orphaned-backfill-records.ts が dry-run / confirm 両モードで audit_logs にエントリ作成 (action=backfill_orphaned_repaired)。Worker 二重稼働防止 (PR7d-2) の env var 3 件 (REFTRIX_ALLOW_MANUAL_WORKER / REFTRIX_WORKER_IS_CHILD / REFTRIX_WORKER_SUPERVISOR_BOOT_TOKEN) を Worker 設定表に正式記載。SEC M-1 対応として WorkerActiveLockService に discriminated union API (tryAcquireLock / probeExistingLock) を追加し fail-open vs fail-closed を明示的に区別。ADR-0011 参照。 / v0.4.0 PR7d-3: documentation sync + audit compliance. cleanupExpired's TTL cron now writes an audit_logs entry when deletedCount > 0 (action=screenshot_ttl_cleanup, actor=system:screenshot-cleanup-cron, GDPR Art.30). repair-orphaned-backfill-records.ts writes an audit_logs entry in both dry-run and confirm modes (action=backfill_orphaned_repaired). Dual-run prevention (PR7d-2) env vars (REFTRIX_ALLOW_MANUAL_WORKER / REFTRIX_WORKER_IS_CHILD / REFTRIX_WORKER_SUPERVISOR_BOOT_TOKEN) are formally documented in the Worker configuration table. Per SEC M-1, WorkerActiveLockService gained discriminated union APIs (tryAcquireLock / probeExistingLock) to distinguish fail-open vs. fail-closed explicitly. See ADR-0011.
2026-04-152.7.0v0.4.0 PR7d-1: PR7c の見落とし補修 — page-analyze-worker.ts の Phase 5 後処理 / finally に残っていた cleanupPhase5TempDir(path.dirname(state.screenshotPngPath)) 呼び出し 2 箇所を削除。永続化された screenshot (<REFTRIX_SCREENSHOT_ROOT>/phase5/) の retention-over-deletion bug を解消。併せて cleanupPhase5TempDir に 3 段 whitelist 防御(realpath + os.tmpdir() + reftrix-phase5-raw- prefix)を追加し、fork orchestrator の cleanupPhase5TmpDirOnly を削除して cleanupPhase5TempDir に統合(DRY)。既存破損レコード修復スクリプト repair-orphaned-backfill-records.ts を追加し、新 enum 値 skipped_screenshot_missing で遷移可能化。ADR-0010 参照。 / v0.4.0 PR7d-1: PR7c oversight fix — removed the two cleanupPhase5TempDir(path.dirname(state.screenshotPngPath)) call sites that remained in page-analyze-worker.ts (Phase 5 post-processing and finally), resolving the retention-over-deletion bug on the persisted screenshot directory. Added a three-stage whitelist defense to cleanupPhase5TempDir (realpath + os.tmpdir() + reftrix-phase5-raw- prefix) and folded cleanupPhase5TmpDirOnly into it (DRY). Added a repair script repair-orphaned-backfill-records.ts and a new enum value skipped_screenshot_missing to transition pre-existing corrupted rows. See ADR-0010.
2026-04-152.6.0v0.4.0 PR7c: Phase 5 Screenshot 即時削除を解除し、削除責務を (1) GDPR data.delete (Art. 17 即時) + (2) PR6 TTL cron (7d) の 2 経路に統一。Phase 5 fork orchestrator の cleanupScreenshotAndTmp()cleanupPhase5TmpDirOnly() に改名して RAW decode tmp dir のみ best-effort 削除に変更。これにより Queue-based Backfill (part_visual / section_visual) が screenshot を参照可能になり visual embedding が実生成される。併せて Pre-Return Pause の resume 補完 (applyPreReturnPauseAndMemoryGate() helper) で RSS 軽量 Worker の永久 pause バグを解消。ADR-0009 参照。 / v0.4.0 PR7c: Removed Phase 5's immediate screenshot deletion. Deletion consolidated into two paths: (1) GDPR data.delete (Art. 17 immediate) + (2) PR6 TTL cron (7d). Phase 5 fork orchestrator's cleanupScreenshotAndTmp() is renamed to cleanupPhase5TmpDirOnly() and now only best-effort removes the RAW decode tmp dir. Queue-based Backfill (part_visual / section_visual) can now actually read the screenshot and generate visual embeddings. Also fixes the RSS-light worker permanent-pause bug via applyPreReturnPauseAndMemoryGate() helper. See ADR-0009.
2026-04-132.5.0v0.4.0 PR7a + PR7b: Phase 5 Skip Recovery 全活性化。embeddingBackfillRetryCount (INT, retry cap 5, 無限ループ防御) + embeddingBackfillSkippedAt (TIMESTAMPTZ, 7d TTL, GDPR Art. 5(1)(e)) 追加。7d TTL cron で skipped_*failed (reason=skip_recovery_expired) 遷移。新規 audit*logs action: backfill_retry_exhausted / skip_recovery_expired / embedding_backfill_queue_jobs_removed(全て truncateTargetId 適用)。ADR-0008 (Accepted) 参照。/ v0.4.0 PR7a + PR7b: Phase 5 Skip Recovery activation. Added embeddingBackfillRetryCount (INT, retry cap 5, infinite-loop defense) + embeddingBackfillSkippedAt (TIMESTAMPTZ, 7d TTL, GDPR Art. 5(1)(e)). 7d TTL cron transitions skipped*\*failed with reason=skip_recovery_expired. New audit_logs actions: backfill_retry_exhausted/skip_recovery_expired/embedding_backfill_queue_jobs_removed(all withtruncateTargetId). See ADR-0008 (Accepted).
2026-04-132.4.0v0.4.0 PR6: TTL cron 統合(Screenshot cleanup 24h + backfill reconciliation 1h、updateMany CAS で worker 競合回避、embeddingBackfillStartedAt 専用列で stale 判定、CLI production --confirm/--dry-run 必須 SEC LOW-2)。 / v0.4.0 PR6: TTL cron integration (screenshot cleanup 24h + backfill reconciliation 1h, CAS via updateMany avoids worker race, stale detection uses dedicated embeddingBackfillStartedAt column, CLI production requires --confirm/--dry-run — SEC LOW-2).
2026-04-122.3.0BullMQ embedding-backfill Queue (v0.4.0 PR4) セクション追加。Redis port 27379、24h/7d retention、Zod 投入/受信 2 段検証 (SEC M-1)、Path Traversal allowlist + realpath 再適用 (SEC H-1/L-1)、GDPR Art. 17/5(1)(c)/5(1)(e) 対応 / Added BullMQ embedding-backfill Queue (v0.4.0 PR4) section. Redis port 27379, 24h/7d retention, Zod validation at enqueue + receipt (SEC M-1), path traversal allowlist + realpath re-applied (SEC H-1/L-1), GDPR Art. 17/5(1)(c)/5(1)(e) compliance
2026-04-122.2.0Screenshot Persistence (v0.4.0 PR1) セクション追加。保存先 ${REFTRIX_SCREENSHOT_ROOT}/phase5/<webPageId>.pngweb_pages.screenshot_storage_path カラム、GDPR Art. 17 削除経路統合、Path Traversal 三重防御、Symlink 正規化、サイズ上限 (50MB default / SCREENSHOT_MAX_BYTES)、cleanupExpired batch size cap、7日 TTL / Added Screenshot Persistence (v0.4.0 PR1) section. Storage at ${REFTRIX_SCREENSHOT_ROOT}/phase5/<webPageId>.png, web_pages.screenshot_storage_path column, GDPR Art. 17 deletion path integration, triple Path Traversal defense, symlink canonicalization, size cap (50MB default / SCREENSHOT_MAX_BYTES), cleanupExpired batch size cap, 7-day TTL
2026-03-292.1.0Phase 5 一時ファイル(RAW decode最適化)の透明性記述を追加(ディスク書き出し、パーミッション、finallyライフサイクル、PII保護)/ Added Phase 5 temporary files (RAW decode optimization) transparency description (disk write, permissions, finally lifecycle, PII protection)
2026-03-272.0.0v0.3.0テーブル追記: audit_logs(365日保持、GDPR Art.30)、search_logs(90日推奨、profileId NULL化連動)、design_snapshots + design_snapshot_sections(CASCADE DELETE連動)/ Added v0.3.0 tables: audit_logs (365-day retention, GDPR Art.30), search_logs (90-day recommended, profileId anonymization), design_snapshots + design_snapshot_sections (CASCADE DELETE)
2026-03-141.5.0Blank Image Detection + Dynamic Fallback (v0.1.9) のデータフロー・PII保護・上限記述を追加 / Added data flow, PII protection, and cap description for Blank Image Detection + Dynamic Fallback (v0.1.9)
2026-03-141.4.0Section Screenshot Fallback (v0.1.6) のフォールバック機構透明性記述を追加(メモリ上一時保持、DB非保存) / Added fallback mechanism transparency for Section Screenshot Fallback (v0.1.6) (in-memory only, not persisted to DB)
2026-03-131.3.0Section Visual Embedding データ保持セクション追加(section_embeddings.vision_embedding、PII保護、CASCADE削除) / Added Section Visual Embedding data retention section (section_embeddings.vision_embedding, PII protection, CASCADE deletion)
2026-03-131.2.0Part-Level Analysis データ保持セクション追加(component_parts / component_part_embeddings テーブル、PII保護、CASCADE削除) / Added Part-Level Analysis data retention section (component_parts / component_part_embeddings tables, PII protection, CASCADE deletion)
2026-03-111.1.0クロールデータ保持セクション追加 / Added crawl data retention section
2026-03-081.0.0初版作成 / Initial version