Task — engineering-spec@1

"REW memory structural exclusion CI gate — no comp fields appear in memory-ingest paths; static analysis + runtime check"

draftTASK-REW-010
module rew · class product · priority p0 · created 2026-05-17 · shipped null
depends on TASK-REW-001 · blocks none

§1 — Description (BCP-14 normative)

The REW service MUST ship memory exclusion gate at services/rew/src/exclusion/ with static CI grep + runtime payload check + sev-1 audit on violation, 4 memory audit kinds.

  1. MUST validate exclusion_check_kind against closed enum per DEC-2241.
  1. MUST run static grep CI at .github/workflows/rew-memory-exclusion.yml per DEC-2242:
  1. MUST check runtime at runtime_check.rs::validate_payload(json) per DEC-2243:
  1. MUST define field blocklist at field_blocklist.rs per DEC-2241 — exhaustive list of forbidden field names.
  1. MUST emit 4 memory audit kinds per DEC-2244. Audit body itself MUST NOT carry comp data.
  1. MUST thread trace_id from ingest attempt → check → audit.
  1. MUST NOT bypass static analysis per DEC-2242.
  1. MUST NOT skip runtime check per DEC-2243.

§2 — Why this design

Why CI + runtime (DEC-2240)? Defense in depth — static catches at compile-time; runtime catches anything that slipped through.

Why CI grep (DEC-2242)? Cheap + fast; catches 95% of accidental introductions at PR review.

Why runtime payload check (DEC-2243)? Final guarantee — even if CI somehow misses, runtime aborts the request.


§3 — API contract

Sample runtime violation response:

{
  "error": "memory_exclusion_violation",
  "field": "gross_vnd",
  "message": "Comp field detected in memory ingest payload; rejected per TASK-REW-010."
}

CI grep script:

#!/bin/bash
# scripts/check_rew_memory_exclusion.sh
FORBIDDEN_PATTERNS="decrypted_amount|gross_vnd|net_vnd|deductions_total|payslip_pdf_bytes"
SCOPES="services/rew/src/audit services/*/src/memory"
matches=$(grep -rE "$FORBIDDEN_PATTERNS" $SCOPES || true)
if [ -n "$matches" ]; then
  echo "memory exclusion violation:"
  echo "$matches"
  exit 1
fi

§4 — Acceptance criteria

  1. exclusion_check_kind enum cardinality 4. 2. CI script greps forbidden patterns. 3. CI fails PR on match. 4. Runtime check rejects comp fields. 5. sev-1 audit on runtime violation. 6. 4 memory audit kinds emitted (no comp data in them). 7. Field blocklist documented + tested. 8. Audit body excludes comp by structure. 9. CI runs on every PR. 10. Tests verify CI script logic. 11. Tests verify runtime check. 12. RLS denies cross-tenant. 13. Trace_id preserved. 14. Append-only audit (no UPDATE). 15. Pattern list maintained + reviewed. 16. Runtime check perf < 1ms per payload. 17. Static check perf < 5s in CI. 18. False-positive handling (allowlist). 19. CI gate cannot be skipped. 20. CHANGELOG.md entry on every pattern list update.

§5 — Verification

#[tokio::test]
async fn runtime_blocks_gross_vnd() {
    let payload = json!({"member_id": "uuid", "gross_vnd": 30000000});
    let r = runtime_check::validate_payload(&payload);
    assert!(r.is_err());
    let audits = ctx.fetch_memory_audits("rew.exclusion_runtime_violation").await;
    assert!(!audits.is_empty());
}

#[tokio::test]
async fn ci_script_catches_pattern() {
    let test_dir = create_test_dir_with("services/audit/test.rs", "let x = decrypted_amount;");
    let r = run_shell("scripts/check_rew_memory_exclusion.sh", test_dir);
    assert!(!r.success());
}

#[tokio::test]
async fn audit_body_excludes_comp() {
    let audit = audit::emit("rew.payroll_committed", json!({"run_id": "uuid", "members_count": 30}), trace).await;
    let serialized = serde_json::to_string(&audit).unwrap();
    assert!(!serialized.contains("vnd"));  // no amount fields
}

// 5.4..5.10

§7 — Dependencies

Upstream: TASK-REW-001. Cross-module: TASK-MEMORY-111 (PII enforcement), TASK-MEMORY-101 (memory ingest paths to gate).

§10 — Failure modes

FailureDetectionOutcomeRecovery
CI grep misses patternmanual reviewsev-1 if leakadd pattern
Runtime check failscatchsev-1 + rejectinherent
False positive patternallowlistinherentrefine
Pattern list outdatedquarterly reviewsev-3update
Audit body leaks comptests catchinherentbug fix
Cross-tenant payloadRLSinherentinherent
Performance degradationbenchmarktuneinherent
CI skipped (admin bypass)sev-1 alertinherentgovernance
Runtime check disabledconfig checksev-1re-enable
Pattern injection via user inputescapeinherentinherent

§11 — Implementation notes


End of TASK-REW-010 spec.