feat(ai): F1-5b Wave A 中段 沙箱业务日全栈架构主体收口 (W1)
完成 F1-5b 任务:
- T1 RuntimeContext unit 测试基础(36 case PASS,本地不入仓走 .gitignore:71)
- A1 admin_service.py 4 处 CURRENT_DATE → business_date 改造
- _get_range_stats / _get_7d_trend / _get_app_distribution
- 上下界双全(下界 - 6 days + 上界 < + 1 day,Step 4b 暴露原 PR
上界缺失,sandbox=4-20 时 trend_7d 漏 4-21~5-01 数据 → 修补)
- 全局聚合 list_trigger_jobs / get_budget 保留 CURRENT_DATE
(Neo D 决策选 A: 多 site 时全局无单一业务日)
- A2 fdw_queries:113 / 2552 异常分支兜底 + 三层 fallback + warning
- conn=None 也尝试 get_runtime_context(自开 conn)
- RuntimeContext 不可用降级真实 today + logger.warning
- A3 _fdw_context docstring 显式登记唯一 ETL 入口架构契约
(D2 完整且统一: 所有 ETL 视图查询通过 _fdw_context 自动 SET 三个
GUC: site_id / business_date / runtime_mode)
- 防御 hook post_edit_business_date_check.py
Wave 2 后续 PR 引回 CURRENT_DATE / date.today() 即提醒
双口径验证(§3.1 4a + 4b):
- 4a live: dashboard trend_7d 2 条 4-30~5-01 (真实今天)
- 4b sandbox=2026-04-20: trend_7d 1 条仅 4-20 (业务日上界生效硬证据)
- pytest test_runtime_context 36/36 全过
未完(下一批 Wave A): T2 integration / UI-1/2/4 / MP-3/5 / MP-1 / BE-1
F1-5b-tasks.md 新增 + audit 记录已就位
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
72
.claude/hooks/post_edit_business_date_check.py
Normal file
72
.claude/hooks/post_edit_business_date_check.py
Normal file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
"""PostToolUse hook: 编辑 backend service 后检查是否引回 CURRENT_DATE / date.today()
|
||||
|
||||
F1-5b A1 决策(2026-05-05):
|
||||
apps/backend/app/services/ 下涉及业务时间的查询应走 RuntimeContext.business_date
|
||||
(sandbox 模式取虚拟日,live 取真实今天),不应直接用 SQL CURRENT_DATE 或
|
||||
Python date.today()。
|
||||
|
||||
例外:全局聚合(无 site_id 上下文)允许保留 CURRENT_DATE,需在该行附近加
|
||||
`# RUNTIME_CTX_BYPASS: <理由>` 注释明确标注。
|
||||
|
||||
本 hook 仅 soft warning(additionalContext),不强阻断,允许必要 fallback。
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
|
||||
try:
|
||||
data = json.load(sys.stdin)
|
||||
except Exception:
|
||||
sys.exit(0)
|
||||
|
||||
tool_input = data.get("tool_input") or {}
|
||||
fp = tool_input.get("file_path", "")
|
||||
if not fp:
|
||||
sys.exit(0)
|
||||
|
||||
rel = re.sub(r"^.*?NeoZQYY[/\\]", "", fp.replace("\\", "/"))
|
||||
|
||||
# 只关注 backend service 层
|
||||
if not re.search(r"^apps/backend/app/services/.*\.py$", rel):
|
||||
sys.exit(0)
|
||||
|
||||
# 提取本次写入/改动的内容
|
||||
content = tool_input.get("new_string") or tool_input.get("content") or ""
|
||||
if not content:
|
||||
sys.exit(0)
|
||||
|
||||
# grep CURRENT_DATE / date.today()
|
||||
lines = content.split("\n")
|
||||
hits = []
|
||||
for i, line in enumerate(lines, 1):
|
||||
if "RUNTIME_CTX_BYPASS" in line:
|
||||
continue
|
||||
# 排除注释行的纯文本提及
|
||||
stripped = line.lstrip()
|
||||
if stripped.startswith("#") or stripped.startswith('"""') or stripped.startswith('"'):
|
||||
continue
|
||||
if re.search(r"\bCURRENT_DATE\b", line) or re.search(r"\bdate\.today\(\)", line):
|
||||
hits.append((i, line.strip()[:80]))
|
||||
|
||||
if not hits:
|
||||
sys.exit(0)
|
||||
|
||||
hint_lines = [
|
||||
f"[business-date-check] 检测到 {rel} 含 {len(hits)} 处 CURRENT_DATE / date.today():",
|
||||
]
|
||||
for ln, code in hits[:5]:
|
||||
hint_lines.append(f" L{ln}: {code}")
|
||||
if len(hits) > 5:
|
||||
hint_lines.append(f" ... (+{len(hits) - 5} 处)")
|
||||
hint_lines.append(
|
||||
"F1-5b A1 决策:有 site_id 上下文应走 RuntimeContext.business_date。"
|
||||
"全局聚合(无 site_id)允许保留,但需加 `# RUNTIME_CTX_BYPASS: <理由>` 注释。"
|
||||
)
|
||||
|
||||
print(json.dumps({
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PostToolUse",
|
||||
"additionalContext": "\n".join(hint_lines),
|
||||
}
|
||||
}))
|
||||
@@ -95,6 +95,16 @@
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "Edit|Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "python \"$CLAUDE_PROJECT_DIR/.claude/hooks/post_edit_business_date_check.py\"",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
|
||||
Reference in New Issue
Block a user