Files
ccdi/docs/plans/backend/2026-03-22-lsfx-rule-hit-mode-backend-implementation.md

10 KiB
Raw Blame History

LSFX Mock Rule Hit Mode Backend Implementation Plan

For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (- [ ]) syntax for tracking.

Goal:lsfx-mock-server 增加可通过命令行切换的规则命中模式,在默认保持稳定随机子集命中的前提下,支持切换为“全部兼容规则命中”。

Architecture: 保持现有 FileService -> StatementService -> FileRecord 缓存 主链路不变,只在启动层新增命令行参数解析,在配置层新增统一模式值,在规则计划层新增 subset/all 两种编排路径。all 模式不做字面全开,而是通过显式互斥组裁剪产出“全部兼容规则命中”计划,避免样本语义冲突。

Tech Stack: Python 3, FastAPI, uvicorn, pydantic-settings, pytest, Markdown


File Structure

  • lsfx-mock-server/config/settings.py: 新增 RULE_HIT_MODE 配置项,统一暴露规则命中模式。
  • lsfx-mock-server/main.py: 新增普通启动命令行参数解析,并在启动前校验模式值。
  • lsfx-mock-server/dev.py: 新增热重载启动入口,支持 --reload --rule-hit-mode ...
  • lsfx-mock-server/services/file_service.py: 为 subset/all 两种模式生成命中计划,并显式维护互斥组裁剪逻辑。
  • lsfx-mock-server/tests/test_file_service.py: 锁定默认随机子集模式、all 模式全集逻辑和互斥组行为。
  • lsfx-mock-server/tests/test_startup.py: 锁定命令行参数解析、非法参数报错和热重载启动参数透传。
  • lsfx-mock-server/README.md: 更新普通启动、热重载启动与“全部兼容规则命中”的准确说明。
  • docs/reports/implementation/2026-03-22-lsfx-rule-hit-mode-backend-record.md: 记录本次后端实施范围、命中模式语义和落地结果。
  • docs/tests/records/2026-03-22-lsfx-rule-hit-mode-backend-verification.md: 记录测试命令、启动验证和进程清理结果。

Task 1: 接入命令行启动参数并统一规则命中模式配置

Files:

  • Modify: lsfx-mock-server/config/settings.py

  • Modify: lsfx-mock-server/main.py

  • Create: lsfx-mock-server/dev.py

  • Create: lsfx-mock-server/tests/test_startup.py

  • Reference: docs/superpowers/specs/2026-03-22-lsfx-rule-hit-mode-design.md

  • Step 1: Write the failing test

先在 lsfx-mock-server/tests/test_startup.py 中补三条失败用例,锁定启动参数语义:

import pytest

from main import parse_args as parse_main_args
from dev import parse_args as parse_dev_args


def test_main_parse_args_should_default_to_subset():
    args = parse_main_args([])
    assert args.rule_hit_mode == "subset"


def test_main_parse_args_should_accept_all_mode():
    args = parse_main_args(["--rule-hit-mode", "all"])
    assert args.rule_hit_mode == "all"


def test_dev_parse_args_should_reject_invalid_mode():
    with pytest.raises(SystemExit):
        parse_dev_args(["--rule-hit-mode", "invalid"])
  • Step 2: Run test to verify it fails

Run:

cd lsfx-mock-server
python3 -m pytest tests/test_startup.py -v

Expected:

  • FAIL

  • 原因是 main.py 与热重载入口尚未提供可测试的参数解析函数

  • Step 3: Write minimal implementation

按最小路径实现:

  1. config/settings.py 中新增默认配置:
RULE_HIT_MODE: str = "subset"
  1. main.py 中新增参数解析函数,只允许 subset|all
def parse_args(argv=None):
    parser = argparse.ArgumentParser()
    parser.add_argument("--rule-hit-mode", choices=["subset", "all"], default="subset")
    return parser.parse_args(argv)
  1. main.py 启动前,将 rule_hit_mode 写入环境变量,再初始化/读取 settings
  2. 新增 dev.py,复用同一套参数解析,支持:
python dev.py --reload --rule-hit-mode all
  1. dev.py 内部调用 uvicorn.run("main:app", reload=True, ...) 或等价方式,不再要求用户直接运行裸 uvicorn main:app --reload ...
  • Step 4: Run test to verify it passes

Run:

cd lsfx-mock-server
python3 -m pytest tests/test_startup.py -v

Expected:

  • PASS

  • 默认模式为 subset

  • all 模式可被普通启动与热重载入口正确解析

  • Step 5: Commit

git add lsfx-mock-server/config/settings.py lsfx-mock-server/main.py lsfx-mock-server/dev.py lsfx-mock-server/tests/test_startup.py
git commit -m "补充Mock规则命中模式启动参数"

Task 2: 在 FileService 中实现全部兼容规则命中计划

Files:

  • Modify: lsfx-mock-server/services/file_service.py

  • Modify: lsfx-mock-server/tests/test_file_service.py

  • Reference: lsfx-mock-server/services/statement_rule_samples.py

  • Reference: docs/superpowers/specs/2026-03-22-lsfx-rule-hit-mode-design.md

  • Step 1: Write the failing test

lsfx-mock-server/tests/test_file_service.py 中先补失败用例,锁定 all 模式语义:

def test_build_rule_hit_plan_should_return_all_compatible_rules_in_all_mode(monkeypatch):
    monkeypatch.setattr("services.file_service.settings.RULE_HIT_MODE", "all")
    service = FileService(staff_identity_repository=FakeStaffIdentityRepository())

    plan = service._build_rule_hit_plan(10001)

    assert plan["large_transaction_hit_rules"] == LARGE_TRANSACTION_RULE_CODES
    assert plan["phase1_hit_rules"] == PHASE1_RULE_CODES
    assert plan["phase2_statement_hit_rules"] == PHASE2_STATEMENT_RULE_CODES
    assert plan["phase2_baseline_hit_rules"] == PHASE2_BASELINE_RULE_CODES


def test_build_rule_hit_plan_should_keep_subset_mode_as_default():
    service = FileService(staff_identity_repository=FakeStaffIdentityRepository())

    plan1 = service._build_rule_hit_plan(10001)
    plan2 = service._build_rule_hit_plan(10001)

    assert plan1 == plan2
    assert 2 <= len(plan1["large_transaction_hit_rules"]) <= 4


def test_build_rule_hit_plan_should_drop_conflicting_rules_from_all_mode(monkeypatch):
    monkeypatch.setattr("services.file_service.settings.RULE_HIT_MODE", "all")
    monkeypatch.setattr(
        "services.file_service.RULE_CONFLICT_GROUPS",
        [["SALARY_QUICK_TRANSFER", "SALARY_UNUSED"]],
    )
    service = FileService(staff_identity_repository=FakeStaffIdentityRepository())

    plan = service._build_rule_hit_plan(10001)

    assert not (
        "SALARY_QUICK_TRANSFER" in plan["phase2_statement_hit_rules"]
        and "SALARY_UNUSED" in plan["phase2_statement_hit_rules"]
    )
  • Step 2: Run test to verify it fails

Run:

cd lsfx-mock-server
python3 -m pytest tests/test_file_service.py -k "rule_hit_plan" -v

Expected:

  • FAIL

  • 原因是当前 _build_rule_hit_plan() 只有稳定随机子集逻辑,尚无模式切换和互斥裁剪

  • Step 3: Write minimal implementation

lsfx-mock-server/services/file_service.py 中按职责做最小拆分:

  1. 保留现有四类规则池常量。
  2. 新增互斥组常量,第一版允许为空列表:
RULE_CONFLICT_GROUPS = []
  1. 新增模式编排辅助函数:
def _build_subset_rule_hit_plan(self, log_id: int) -> dict:
    ...


def _build_all_compatible_rule_hit_plan(self) -> dict:
    ...


def _apply_conflict_groups(self, rule_plan: dict) -> dict:
    ...
  1. _build_rule_hit_plan() 只负责分发:
if settings.RULE_HIT_MODE == "all":
    return self._apply_conflict_groups(self._build_all_compatible_rule_hit_plan())
return self._build_subset_rule_hit_plan(log_id)
  1. 不修改 FileRecord 字段结构和后续消费链路,只改变计划生成方式。
  • Step 4: Run test to verify it passes

Run:

cd lsfx-mock-server
python3 -m pytest tests/test_file_service.py -k "rule_hit_plan" -v

Expected:

  • PASS

  • 默认仍为随机子集

  • all 模式返回全部兼容规则

  • 若未来配置互斥组,同组规则不会同时出现在结果里

  • Step 5: Commit

git add lsfx-mock-server/services/file_service.py lsfx-mock-server/tests/test_file_service.py
git commit -m "补充Mock全部兼容规则命中计划"

Task 3: 更新文档并完成后端验证记录

Files:

  • Modify: lsfx-mock-server/README.md

  • Create: docs/reports/implementation/2026-03-22-lsfx-rule-hit-mode-backend-record.md

  • Create: docs/tests/records/2026-03-22-lsfx-rule-hit-mode-backend-verification.md

  • Step 1: Update README with accurate startup instructions

更新 lsfx-mock-server/README.md

  • 普通启动示例改为:
python main.py --rule-hit-mode subset
python main.py --rule-hit-mode all
  • 热重载示例改为:
python dev.py --reload --rule-hit-mode subset
python dev.py --reload --rule-hit-mode all
  • 文案统一使用“全部兼容规则命中”,不使用“全部规则命中”。

  • Step 2: Run targeted verification

Run:

cd lsfx-mock-server
python3 -m pytest tests/test_startup.py tests/test_file_service.py -k "rule_hit_plan or parse_args" -v

Expected:

  • PASS

  • 启动参数与规则计划两条主链路均被锁定

  • Step 3: Run startup smoke tests and stop processes

分别执行并记录:

cd lsfx-mock-server
python3 main.py --rule-hit-mode all > /tmp/lsfx_main.log 2>&1 & echo $! > /tmp/lsfx_main.pid
sleep 3
kill "$(cat /tmp/lsfx_main.pid)"
rm -f /tmp/lsfx_main.pid

python3 dev.py --reload --rule-hit-mode all > /tmp/lsfx_dev.log 2>&1 & echo $! > /tmp/lsfx_dev.pid
sleep 5
kill "$(cat /tmp/lsfx_dev.pid)"
rm -f /tmp/lsfx_dev.pid

Expected:

  • 两种启动方式均成功拉起

  • 结束验证后无残留进程

  • Step 4: Write implementation and verification records

在实施记录中写清:

  • 默认模式保持不变
  • all 的准确语义是“全部兼容规则命中”
  • 当前互斥组为空或具体清单
  • 热重载改为项目脚本入口

在验证记录中写清:

  • 测试命令及结果

  • 启动验证命令及结果

  • 进程清理动作

  • Step 5: Commit

git add lsfx-mock-server/README.md docs/reports/implementation/2026-03-22-lsfx-rule-hit-mode-backend-record.md docs/tests/records/2026-03-22-lsfx-rule-hit-mode-backend-verification.md
git commit -m "补充Mock命中模式后端实施与验证记录"