@michelj/context-guard 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +74 -6
- package/README.zh-CN.md +74 -6
- package/{skills/context-guard/SKILL.md → SKILL.md} +216 -77
- package/bin/context-guard-skill.js +88 -3
- package/hooks.json +25 -1
- package/package.json +7 -3
- package/{skills/context-guard/references → references}/context-template.md +52 -1
- package/references/feature-chain-methodology.md +228 -0
- package/{skills/context-guard/scripts → scripts}/context_guard.py +3119 -121
- package/{skills/context-guard/scripts → scripts}/context_guard_hook.py +344 -12
- package/{skills/context-guard/tests → tests}/BC-20260701-090.sh +8 -7
- package/{skills/context-guard/tests → tests}/BC-20260702-096.sh +6 -3
- package/tests/BC-20260706-098.sh +66 -0
- package/tests/BC-20260707-099.sh +47 -0
- package/tests/BC-20260707-100.sh +46 -0
- package/tests/BC-20260707-101.sh +47 -0
- package/tests/BC-20260707-102.sh +68 -0
- package/tests/BC-20260707-103.sh +59 -0
- package/tests/BC-20260707-104.sh +103 -0
- package/tests/BC-20260707-105.sh +109 -0
- package/tests/BC-20260707-106.sh +80 -0
- package/tests/BC-20260707-107.sh +74 -0
- package/tests/BC-20260707-108.sh +48 -0
- package/tests/BC-20260707-109.sh +56 -0
- package/tests/BC-20260707-110.sh +71 -0
- package/tests/BC-20260707-111.sh +70 -0
- package/tests/BC-20260707-112.sh +45 -0
- package/tests/BC-20260707-113.sh +73 -0
- package/tests/BC-20260707-115.sh +77 -0
- package/tests/BC-20260707-116.sh +77 -0
- package/tests/BC-20260707-118.sh +115 -0
- package/tests/BC-20260707-119.sh +47 -0
- package/tests/BC-20260707-120.sh +60 -0
- package/tests/BC-20260707-121.sh +66 -0
- package/tests/BC-20260707-122.sh +48 -0
- package/tests/BC-20260707-123.sh +43 -0
- package/tests/BC-20260707-124.sh +56 -0
- package/tests/BC-20260707-125.sh +64 -0
- package/tests/BC-20260707-126.sh +80 -0
- package/tests/BC-20260707-127.sh +88 -0
- package/tests/BC-20260707-129.sh +59 -0
- package/tests/BC-20260707-130.sh +69 -0
- package/tests/BC-20260707-131.sh +140 -0
- package/tests/BC-20260707-132.sh +150 -0
- package/tests/BC-20260707-133.sh +70 -0
- package/tests/BC-20260708-136.sh +210 -0
- package/tests/BC-20260708-137.sh +106 -0
- package/tests/BC-20260708-138.sh +168 -0
- package/tests/BC-20260708-139.sh +79 -0
- package/tests/BC-20260709-002.sh +63 -0
- package/tests/BC-20260709-003.sh +239 -0
- package/tests/BC-20260709-006.sh +76 -0
- package/tests/BC-20260709-008.sh +168 -0
- package/tests/BC-20260710-001.sh +61 -0
- package/tests/BC-20260710-002.sh +111 -0
- package/tests/npm-install-smoke.sh +53 -0
- package/skills/context-guard/README.md +0 -234
- package/skills/context-guard/README.zh-CN.md +0 -234
- /package/{skills/context-guard/agents → agents}/openai.yaml +0 -0
- /package/{skills/context-guard/references → references}/register-template.md +0 -0
- /package/{skills/context-guard/references → references}/task-case-template.md +0 -0
- /package/{skills/context-guard/tests → tests}/BC-20260618-063.sh +0 -0
- /package/{skills/context-guard/tests → tests}/BC-20260618-065.sh +0 -0
- /package/{skills/context-guard/tests → tests}/BC-20260626-080.sh +0 -0
- /package/{skills/context-guard/tests → tests}/BC-20260626-081.sh +0 -0
- /package/{skills/context-guard/tests → tests}/BC-20260626-082.sh +0 -0
- /package/{skills/context-guard/tests → tests}/BC-20260626-083.sh +0 -0
- /package/{skills/context-guard/tests → tests}/BC-20260627-084.sh +0 -0
- /package/{skills/context-guard/tests → tests}/BC-20260630-086.sh +0 -0
- /package/{skills/context-guard/tests → tests}/BC-20260630-087.sh +0 -0
- /package/{skills/context-guard/tests → tests}/BC-20260630-088.sh +0 -0
- /package/{skills/context-guard/tests → tests}/BC-20260630-089.sh +0 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/scripts/context_guard.py"
|
|
5
|
+
ROOT="$(mktemp -d "${TMPDIR:-/tmp}/context-guard-feature-chain-lifecycle-XXXXXX")"
|
|
6
|
+
trap 'rm -rf "$ROOT"' EXIT
|
|
7
|
+
|
|
8
|
+
python3 "$SCRIPT" init --root "$ROOT" >/dev/null
|
|
9
|
+
mkdir -p "$ROOT/.codex/context/test-hub/fixtures"
|
|
10
|
+
|
|
11
|
+
PROPOSE_OUT="$ROOT/propose.out"
|
|
12
|
+
PLAN_OUT="$ROOT/plan.out"
|
|
13
|
+
ATTACH_OUT="$ROOT/attach.out"
|
|
14
|
+
SUMMARY_OUT="$ROOT/summary.out"
|
|
15
|
+
OVERLAP_OUT="$ROOT/overlap.out"
|
|
16
|
+
APPROVE_OUT="$ROOT/approve.out"
|
|
17
|
+
COMPLETE_OUT="$ROOT/dev-complete.out"
|
|
18
|
+
|
|
19
|
+
python3 "$SCRIPT" feature-chain-plan \
|
|
20
|
+
--root "$ROOT" \
|
|
21
|
+
--query "创建一个测试任务:从编辑器输入 Markdown 到预览正确渲染,主要验证公式渲染回归" >"$PLAN_OUT"
|
|
22
|
+
|
|
23
|
+
grep -q "after-confirmation command: .*feature-chain-propose" "$PLAN_OUT"
|
|
24
|
+
grep -q -- "--coverage-pending-reason" "$PLAN_OUT"
|
|
25
|
+
|
|
26
|
+
python3 "$SCRIPT" feature-chain-propose \
|
|
27
|
+
--root "$ROOT" \
|
|
28
|
+
--title "Markdown 公式预览" \
|
|
29
|
+
--entry "编辑器输入 Markdown" \
|
|
30
|
+
--exit-check "预览正确渲染" \
|
|
31
|
+
--node-title "公式渲染回归" \
|
|
32
|
+
--check "单行、多行和矩阵公式都被 MathJax 正常排版" \
|
|
33
|
+
--coverage-pending-reason "用户先确认功能测试,后续遇到 MathJax 公式渲染 bad case 再挂到 checkpoint" >"$PROPOSE_OUT"
|
|
34
|
+
|
|
35
|
+
CHAIN_ID="$(grep 'feature chain proposed:' "$PROPOSE_OUT" | awk '{print $NF}')"
|
|
36
|
+
test -n "$CHAIN_ID"
|
|
37
|
+
|
|
38
|
+
cat >"$ROOT/.codex/context/bad-cases.md" <<'MD'
|
|
39
|
+
### BC-20260707-940: Markdown 公式预览没有渲染
|
|
40
|
+
|
|
41
|
+
- Status: resolved
|
|
42
|
+
- First observed: 2026-07-07
|
|
43
|
+
- Last checked: 2026-07-07
|
|
44
|
+
- Scope: Markdown editor preview
|
|
45
|
+
- Tags: #feature-chain #mathjax #rendering
|
|
46
|
+
- Display summary: Markdown 编辑器里的公式没有被 MathJax 正常渲染。
|
|
47
|
+
- Phenomenon: 单行、多行或矩阵公式在预览里显示为原始文本。
|
|
48
|
+
- Trigger / reproduction: 在编辑器输入 Markdown 公式并打开预览。
|
|
49
|
+
- Root cause: 预览渲染链路没有触发 MathJax 排版。
|
|
50
|
+
- Fix method: 确保预览更新后执行 MathJax typeset。
|
|
51
|
+
- Guard / verification: 功能链 checkpoint 应验证公式预览已渲染。
|
|
52
|
+
- Red condition: 预览仍显示原始公式文本。
|
|
53
|
+
- Green condition: 预览显示排版后的公式。
|
|
54
|
+
- Expected failure reason: 如果公式渲染回归,功能链 checkpoint 会失败。
|
|
55
|
+
MD
|
|
56
|
+
|
|
57
|
+
python3 "$SCRIPT" feature-chain-plan --root "$ROOT" --query "BC-20260707-940" >"$PLAN_OUT"
|
|
58
|
+
grep -q "action: review-existing-chain" "$PLAN_OUT"
|
|
59
|
+
grep -q "$CHAIN_ID" "$PLAN_OUT"
|
|
60
|
+
grep -q "checkpoint: 公式渲染回归" "$PLAN_OUT"
|
|
61
|
+
grep -q "after-confirmation command: .*feature-chain-attach-bc" "$PLAN_OUT"
|
|
62
|
+
|
|
63
|
+
python3 "$SCRIPT" feature-chain-attach-bc \
|
|
64
|
+
--root "$ROOT" \
|
|
65
|
+
--chain-id "$CHAIN_ID" \
|
|
66
|
+
--node-title "公式渲染回归" \
|
|
67
|
+
--bad-case "BC-20260707-940" \
|
|
68
|
+
--check "公式预览必须触发 MathJax 排版" >"$ATTACH_OUT"
|
|
69
|
+
|
|
70
|
+
python3 "$SCRIPT" feature-chain-summary --root "$ROOT" >"$SUMMARY_OUT"
|
|
71
|
+
grep -q "chains: 1" "$SUMMARY_OUT"
|
|
72
|
+
grep -q "covered bad cases: 1" "$SUMMARY_OUT"
|
|
73
|
+
grep -q "Markdown 公式预览没有渲染" "$SUMMARY_OUT"
|
|
74
|
+
grep -q "coverage density:" "$SUMMARY_OUT"
|
|
75
|
+
if grep -q "coverage pending" "$SUMMARY_OUT"; then
|
|
76
|
+
cat "$SUMMARY_OUT"
|
|
77
|
+
exit 1
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
python3 "$SCRIPT" feature-chain-propose \
|
|
81
|
+
--root "$ROOT" \
|
|
82
|
+
--title "Markdown 数学公式渲染" \
|
|
83
|
+
--entry "编辑器输入 Markdown" \
|
|
84
|
+
--exit-check "预览正确渲染" \
|
|
85
|
+
--node-title "数学公式预览稳定" \
|
|
86
|
+
--bad-cases "BC-20260707-941" \
|
|
87
|
+
--check "公式预览不会显示原始文本" >/dev/null
|
|
88
|
+
|
|
89
|
+
python3 "$SCRIPT" feature-chain-overlap --root "$ROOT" --min-score 4 >"$OVERLAP_OUT"
|
|
90
|
+
grep -q "overlapping candidates: 1" "$OVERLAP_OUT"
|
|
91
|
+
grep -q "Markdown 公式预览" "$OVERLAP_OUT"
|
|
92
|
+
grep -q "Markdown 数学公式渲染" "$OVERLAP_OUT"
|
|
93
|
+
|
|
94
|
+
cat >"$ROOT/.codex/context/test-hub/fixtures/markdown_preview_chain.sh" <<'SH'
|
|
95
|
+
#!/usr/bin/env bash
|
|
96
|
+
set -euo pipefail
|
|
97
|
+
echo "CG_CHECKPOINT:公式渲染回归:PASS"
|
|
98
|
+
SH
|
|
99
|
+
chmod +x "$ROOT/.codex/context/test-hub/fixtures/markdown_preview_chain.sh"
|
|
100
|
+
|
|
101
|
+
python3 "$SCRIPT" feature-chain-approve \
|
|
102
|
+
--root "$ROOT" \
|
|
103
|
+
--chain-id "$CHAIN_ID" \
|
|
104
|
+
--command-text "bash .codex/context/test-hub/fixtures/markdown_preview_chain.sh" >"$APPROVE_OUT"
|
|
105
|
+
|
|
106
|
+
grep -q "feature chain approved: $CHAIN_ID" "$APPROVE_OUT"
|
|
107
|
+
grep -q "approval dry-run: passed" "$APPROVE_OUT"
|
|
108
|
+
|
|
109
|
+
python3 "$SCRIPT" dev-complete --root "$ROOT" >"$COMPLETE_OUT"
|
|
110
|
+
grep -q "test hub: 1 passed, 0 failed, 0 blocked" "$COMPLETE_OUT"
|
|
111
|
+
grep -q "passed: Markdown 公式预览" "$COMPLETE_OUT"
|
|
112
|
+
|
|
113
|
+
python3 - "$ROOT/.codex/context/test-hub/feature-chains.json" "$ROOT/.codex/context/test-hub/last-run.json" "$CHAIN_ID" <<'PY'
|
|
114
|
+
from pathlib import Path
|
|
115
|
+
import json
|
|
116
|
+
import sys
|
|
117
|
+
|
|
118
|
+
chains_path = Path(sys.argv[1])
|
|
119
|
+
run_path = Path(sys.argv[2])
|
|
120
|
+
chain_id = sys.argv[3]
|
|
121
|
+
|
|
122
|
+
chains = json.loads(chains_path.read_text(encoding="utf-8"))["chains"]
|
|
123
|
+
chain = next(item for item in chains if item["id"] == chain_id)
|
|
124
|
+
assert chain["status"] == "approved", chain
|
|
125
|
+
assert chain["run_policy"] == "every-dev-completion", chain
|
|
126
|
+
node = chain["nodes"][0]
|
|
127
|
+
assert node["title"] == "公式渲染回归", node
|
|
128
|
+
assert "BC-20260707-940" in node["bad_cases"], node
|
|
129
|
+
assert "coverage_pending_reason" not in node, node
|
|
130
|
+
|
|
131
|
+
last_run = json.loads(run_path.read_text(encoding="utf-8"))
|
|
132
|
+
results = last_run.get("results", [])
|
|
133
|
+
assert len(results) == 1, last_run
|
|
134
|
+
result = results[0]
|
|
135
|
+
assert result["status"] == "passed", result
|
|
136
|
+
assert any(item.get("label") == "公式渲染回归" and item.get("status") == "pass" for item in result.get("checkpoints", [])), result
|
|
137
|
+
PY
|
|
138
|
+
|
|
139
|
+
python3 "$SCRIPT" validate-feature-chains --root "$ROOT" >"$ROOT/validate.out"
|
|
140
|
+
grep -q "feature-chain validation passed: 2 chain(s), 0 warning(s)" "$ROOT/validate.out"
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/scripts/context_guard.py}"
|
|
5
|
+
ROOT="$(mktemp -d "${TMPDIR:-/tmp}/context-guard-feature-chain-rerun-XXXXXX")"
|
|
6
|
+
trap 'rm -rf "$ROOT"' EXIT
|
|
7
|
+
|
|
8
|
+
python3 "$SCRIPT" init --root "$ROOT" >/dev/null
|
|
9
|
+
mkdir -p "$ROOT/.codex/context/test-hub/fixtures"
|
|
10
|
+
|
|
11
|
+
cat >"$ROOT/.codex/context/bad-cases.md" <<'MD'
|
|
12
|
+
### BC-20260707-950: Markdown 公式输入没有进入预览流程
|
|
13
|
+
|
|
14
|
+
- Status: resolved
|
|
15
|
+
- First observed: 2026-07-07
|
|
16
|
+
- Last checked: 2026-07-07
|
|
17
|
+
- Scope: Markdown editor preview
|
|
18
|
+
- Tags: #feature-chain #mathjax #input
|
|
19
|
+
- Display summary: 公式输入后预览流程没有被触发。
|
|
20
|
+
- Phenomenon: 用户输入公式后,预览仍停在旧内容。
|
|
21
|
+
- Trigger / reproduction: 在编辑器输入包含公式的 Markdown。
|
|
22
|
+
- Root cause: 输入事件没有触发预览更新。
|
|
23
|
+
- Fix method: 预览链路监听输入并进入渲染流程。
|
|
24
|
+
- Guard / verification: 功能链 checkpoint 检查公式输入进入预览流程。
|
|
25
|
+
- Red condition: 输入公式后预览流程没有执行。
|
|
26
|
+
- Green condition: 输入公式后预览流程开始并进入渲染阶段。
|
|
27
|
+
- Expected failure reason: 输入阶段回归时,功能链第一 checkpoint 会失败。
|
|
28
|
+
|
|
29
|
+
### BC-20260707-951: Markdown 公式预览仍显示原始文本
|
|
30
|
+
|
|
31
|
+
- Status: resolved
|
|
32
|
+
- First observed: 2026-07-07
|
|
33
|
+
- Last checked: 2026-07-07
|
|
34
|
+
- Scope: Markdown editor preview
|
|
35
|
+
- Tags: #feature-chain #mathjax #rendering
|
|
36
|
+
- Display summary: 公式预览没有完成 MathJax 渲染。
|
|
37
|
+
- Phenomenon: 预览区显示原始 LaTeX 文本,而不是排版后的公式。
|
|
38
|
+
- Trigger / reproduction: 输入单行、多行或矩阵公式并打开预览。
|
|
39
|
+
- Root cause: 渲染阶段没有执行 MathJax 排版。
|
|
40
|
+
- Fix method: 预览更新后执行 MathJax typeset。
|
|
41
|
+
- Guard / verification: 功能链 checkpoint 检查公式预览已渲染。
|
|
42
|
+
- Red condition: 预览仍显示原始公式文本。
|
|
43
|
+
- Green condition: 预览显示排版后的公式。
|
|
44
|
+
- Expected failure reason: 渲染阶段回归时,功能链第二 checkpoint 会失败。
|
|
45
|
+
MD
|
|
46
|
+
|
|
47
|
+
PROPOSE_OUT="$ROOT/propose.out"
|
|
48
|
+
python3 "$SCRIPT" feature-chain-propose \
|
|
49
|
+
--root "$ROOT" \
|
|
50
|
+
--title "Markdown 公式预览" \
|
|
51
|
+
--entry "编辑器输入 Markdown 公式" \
|
|
52
|
+
--exit-check "预览区显示排版后的公式" \
|
|
53
|
+
--node-title "公式输入进入预览流程" \
|
|
54
|
+
--bad-cases "BC-20260707-950" \
|
|
55
|
+
--check "输入公式后预览流程必须开始" >"$PROPOSE_OUT"
|
|
56
|
+
|
|
57
|
+
CHAIN_ID="$(grep 'feature chain proposed:' "$PROPOSE_OUT" | awk '{print $NF}')"
|
|
58
|
+
test -n "$CHAIN_ID"
|
|
59
|
+
|
|
60
|
+
python3 "$SCRIPT" feature-chain-attach-bc \
|
|
61
|
+
--root "$ROOT" \
|
|
62
|
+
--chain-id "$CHAIN_ID" \
|
|
63
|
+
--node-title "公式预览完成渲染" \
|
|
64
|
+
--bad-case "BC-20260707-951" \
|
|
65
|
+
--check "单行、多行和矩阵公式都必须完成 MathJax 排版" >/dev/null
|
|
66
|
+
|
|
67
|
+
RUNNER="$ROOT/.codex/context/test-hub/fixtures/markdown_formula_flow.sh"
|
|
68
|
+
cat >"$RUNNER" <<'SH'
|
|
69
|
+
#!/usr/bin/env bash
|
|
70
|
+
set -euo pipefail
|
|
71
|
+
echo "CG_CHECKPOINT:公式输入进入预览流程:PASS"
|
|
72
|
+
echo "CG_CHECKPOINT:公式预览完成渲染:PASS"
|
|
73
|
+
SH
|
|
74
|
+
chmod +x "$RUNNER"
|
|
75
|
+
|
|
76
|
+
python3 "$SCRIPT" feature-chain-approve \
|
|
77
|
+
--root "$ROOT" \
|
|
78
|
+
--chain-id "$CHAIN_ID" \
|
|
79
|
+
--command-text "bash .codex/context/test-hub/fixtures/markdown_formula_flow.sh" >"$ROOT/approve.out"
|
|
80
|
+
grep -q "approval dry-run: passed" "$ROOT/approve.out"
|
|
81
|
+
|
|
82
|
+
cat >"$RUNNER" <<'SH'
|
|
83
|
+
#!/usr/bin/env bash
|
|
84
|
+
set -euo pipefail
|
|
85
|
+
echo "CG_CHECKPOINT:公式输入进入预览流程:PASS"
|
|
86
|
+
echo "CG_CHECKPOINT:公式预览完成渲染:FAIL:预览仍显示原始公式文本"
|
|
87
|
+
printf "raw formula still visible\n" > "$CONTEXT_GUARD_TEST_RUN_DIR/render-evidence.txt"
|
|
88
|
+
SH
|
|
89
|
+
chmod +x "$RUNNER"
|
|
90
|
+
|
|
91
|
+
if python3 "$SCRIPT" dev-complete --root "$ROOT" >"$ROOT/fail.out" 2>&1; then
|
|
92
|
+
cat "$ROOT/fail.out"
|
|
93
|
+
exit 1
|
|
94
|
+
fi
|
|
95
|
+
grep -q "test hub: 0 passed, 1 failed, 0 blocked" "$ROOT/fail.out"
|
|
96
|
+
grep -q "checkpoint failed: 公式预览完成渲染 - 预览仍显示原始公式文本" "$ROOT/fail.out"
|
|
97
|
+
grep -q "evidence preserved" "$ROOT/fail.out"
|
|
98
|
+
|
|
99
|
+
python3 - "$ROOT/.codex/context/test-hub/last-run.json" <<'PY'
|
|
100
|
+
from pathlib import Path
|
|
101
|
+
import json
|
|
102
|
+
import sys
|
|
103
|
+
|
|
104
|
+
run = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
|
105
|
+
result = run["results"][0]
|
|
106
|
+
assert result["status"] == "failed", result
|
|
107
|
+
assert result["reason"] == "checkpoint failed: 公式预览完成渲染 - 预览仍显示原始公式文本", result
|
|
108
|
+
labels = [item["label"] for item in result["checkpoints"]]
|
|
109
|
+
assert labels == ["公式输入进入预览流程", "公式预览完成渲染"], labels
|
|
110
|
+
log_path = Path(result["log"])
|
|
111
|
+
assert log_path.exists(), log_path
|
|
112
|
+
evidence = log_path.parent / "render-evidence.txt"
|
|
113
|
+
assert evidence.exists(), evidence
|
|
114
|
+
assert "raw formula" in evidence.read_text(encoding="utf-8")
|
|
115
|
+
PY
|
|
116
|
+
|
|
117
|
+
cat >"$RUNNER" <<'SH'
|
|
118
|
+
#!/usr/bin/env bash
|
|
119
|
+
set -euo pipefail
|
|
120
|
+
echo "CG_CHECKPOINT:公式输入进入预览流程:PASS"
|
|
121
|
+
echo "CG_CHECKPOINT:公式预览完成渲染:PASS"
|
|
122
|
+
printf "temporary success artifact\n" > "$CONTEXT_GUARD_TEST_RUN_DIR/success.tmp"
|
|
123
|
+
SH
|
|
124
|
+
chmod +x "$RUNNER"
|
|
125
|
+
|
|
126
|
+
python3 "$SCRIPT" dev-complete --root "$ROOT" >"$ROOT/pass.out"
|
|
127
|
+
grep -q "test hub: 1 passed, 0 failed, 0 blocked" "$ROOT/pass.out"
|
|
128
|
+
grep -q "success artifacts cleaned" "$ROOT/pass.out"
|
|
129
|
+
|
|
130
|
+
python3 - "$ROOT/.codex/context/test-hub/feature-chains.json" "$ROOT/.codex/context/test-hub/last-run.json" "$CHAIN_ID" <<'PY'
|
|
131
|
+
from pathlib import Path
|
|
132
|
+
import json
|
|
133
|
+
import sys
|
|
134
|
+
|
|
135
|
+
chains = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))["chains"]
|
|
136
|
+
chain = next(item for item in chains if item["id"] == sys.argv[3])
|
|
137
|
+
assert chain["status"] == "approved", chain
|
|
138
|
+
assert chain["run_policy"] == "every-dev-completion", chain
|
|
139
|
+
covered = sorted(bc for node in chain["nodes"] for bc in node["bad_cases"])
|
|
140
|
+
assert covered == ["BC-20260707-950", "BC-20260707-951"], covered
|
|
141
|
+
|
|
142
|
+
last_run = json.loads(Path(sys.argv[2]).read_text(encoding="utf-8"))
|
|
143
|
+
result = last_run["results"][0]
|
|
144
|
+
assert result["status"] == "passed", result
|
|
145
|
+
assert result["missing_checkpoints"] == [], result
|
|
146
|
+
assert all(item["status"] == "pass" for item in result["checkpoints"]), result
|
|
147
|
+
PY
|
|
148
|
+
|
|
149
|
+
python3 "$SCRIPT" validate-feature-chains --root "$ROOT" >"$ROOT/validate.out"
|
|
150
|
+
grep -q "feature-chain validation passed: 1 chain(s), 0 warning(s)" "$ROOT/validate.out"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/scripts/context_guard.py}"
|
|
5
|
+
ROOT="$(mktemp -d "${TMPDIR:-/tmp}/context-guard-dry-run-id-XXXXXX")"
|
|
6
|
+
trap 'rm -rf "$ROOT"' EXIT
|
|
7
|
+
|
|
8
|
+
python3 "$SCRIPT" init --root "$ROOT" >/dev/null
|
|
9
|
+
|
|
10
|
+
cat >"$ROOT/.codex/context/bad-cases.md" <<'MD'
|
|
11
|
+
### BC-20260707-960: Dry-run 证据目录可能撞名
|
|
12
|
+
|
|
13
|
+
- Status: resolved
|
|
14
|
+
- First observed: 2026-07-07
|
|
15
|
+
- Last checked: 2026-07-07
|
|
16
|
+
- Scope: Context Guard Test Hub dry-run evidence
|
|
17
|
+
- Tags: #feature-chain #test-hub #artifact-policy
|
|
18
|
+
- Display summary: 快速连续 dry-run 不应把失败证据写到同一个目录。
|
|
19
|
+
- Phenomenon: 秒级 run id 可能让同一条功能链的多次 dry-run 复用同一个 evidence path。
|
|
20
|
+
- Trigger / reproduction: 快速连续运行两次失败的 `feature-chain-dry-run`。
|
|
21
|
+
- Root cause: dry-run 目录名只使用秒级时间戳。
|
|
22
|
+
- Fix method: dry-run/run 目录名改用微秒级唯一 run id。
|
|
23
|
+
- Guard / verification: 快速连续失败 dry-run 应保留两个不同 evidence 目录。
|
|
24
|
+
- Red condition: 两次失败 dry-run 输出相同 evidence preserved 路径。
|
|
25
|
+
- Green condition: 两次失败 dry-run 输出不同 evidence preserved 路径,且目录都存在。
|
|
26
|
+
- Expected failure reason: 证据路径撞名会覆盖或混杂失败日志,让 Codex 误判坏在哪个 checkpoint。
|
|
27
|
+
MD
|
|
28
|
+
|
|
29
|
+
PROPOSE_OUT="$ROOT/propose.out"
|
|
30
|
+
python3 "$SCRIPT" feature-chain-propose \
|
|
31
|
+
--root "$ROOT" \
|
|
32
|
+
--title "Markdown 公式预览" \
|
|
33
|
+
--entry "输入 Markdown 公式" \
|
|
34
|
+
--exit-check "预览渲染公式" \
|
|
35
|
+
--node-title "公式预览完成渲染" \
|
|
36
|
+
--bad-cases "BC-20260707-960" \
|
|
37
|
+
--check "公式预览必须完成渲染" >"$PROPOSE_OUT"
|
|
38
|
+
|
|
39
|
+
CHAIN_ID="$(grep 'feature chain proposed:' "$PROPOSE_OUT" | awk '{print $NF}')"
|
|
40
|
+
test -n "$CHAIN_ID"
|
|
41
|
+
|
|
42
|
+
set +e
|
|
43
|
+
python3 "$SCRIPT" feature-chain-dry-run \
|
|
44
|
+
--root "$ROOT" \
|
|
45
|
+
--chain-id "$CHAIN_ID" \
|
|
46
|
+
--command-text "printf 'CG_CHECKPOINT:公式预览完成渲染:FAIL:第一次失败\\n'" >"$ROOT/fail-1.out" 2>&1
|
|
47
|
+
STATUS_ONE=$?
|
|
48
|
+
python3 "$SCRIPT" feature-chain-dry-run \
|
|
49
|
+
--root "$ROOT" \
|
|
50
|
+
--chain-id "$CHAIN_ID" \
|
|
51
|
+
--command-text "printf 'CG_CHECKPOINT:公式预览完成渲染:FAIL:第二次失败\\n'" >"$ROOT/fail-2.out" 2>&1
|
|
52
|
+
STATUS_TWO=$?
|
|
53
|
+
set -e
|
|
54
|
+
|
|
55
|
+
test "$STATUS_ONE" -ne 0
|
|
56
|
+
test "$STATUS_TWO" -ne 0
|
|
57
|
+
|
|
58
|
+
DIR_ONE="$(sed -n 's/.*dry-run evidence preserved: //p' "$ROOT/fail-1.out" | tail -n 1)"
|
|
59
|
+
DIR_TWO="$(sed -n 's/.*dry-run evidence preserved: //p' "$ROOT/fail-2.out" | tail -n 1)"
|
|
60
|
+
test -n "$DIR_ONE"
|
|
61
|
+
test -n "$DIR_TWO"
|
|
62
|
+
test "$DIR_ONE" != "$DIR_TWO"
|
|
63
|
+
test -d "$DIR_ONE"
|
|
64
|
+
test -d "$DIR_TWO"
|
|
65
|
+
|
|
66
|
+
grep -q "第一次失败" "$DIR_ONE/$CHAIN_ID.log"
|
|
67
|
+
grep -q "第二次失败" "$DIR_TWO/$CHAIN_ID.log"
|
|
68
|
+
|
|
69
|
+
python3 "$SCRIPT" validate-feature-chains --root "$ROOT" >"$ROOT/validate.out"
|
|
70
|
+
grep -q "feature-chain validation passed: 1 chain(s), 0 warning(s)" "$ROOT/validate.out"
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/scripts/context_guard.py}"
|
|
5
|
+
ROOT="$(mktemp -d "${TMPDIR:-/tmp}/context-guard-multi-chain-XXXXXX")"
|
|
6
|
+
trap 'rm -rf "$ROOT"' EXIT
|
|
7
|
+
|
|
8
|
+
python3 "$SCRIPT" init --root "$ROOT" >/dev/null
|
|
9
|
+
mkdir -p "$ROOT/.codex/context/test-hub/fixtures"
|
|
10
|
+
|
|
11
|
+
cat >"$ROOT/.codex/context/bad-cases.md" <<'MD'
|
|
12
|
+
### BC-20260708-960: 晚间清单漏勾仍显示完成
|
|
13
|
+
|
|
14
|
+
- Status: resolved
|
|
15
|
+
- First observed: 2026-07-08
|
|
16
|
+
- Last checked: 2026-07-08
|
|
17
|
+
- Scope: Evening reset checklist
|
|
18
|
+
- Tags: #feature-chain #life
|
|
19
|
+
- Display summary: 晚间收纳清单不能在漏勾时显示完成。
|
|
20
|
+
- Phenomenon: 用户只勾选部分事项,页面仍显示今晚重置完成。
|
|
21
|
+
- Trigger / reproduction: 打开晚间重置清单,少勾一项后点击完成。
|
|
22
|
+
- Root cause: 完成判断只看点击事件,没有检查全部事项。
|
|
23
|
+
- Fix method: 完成前检查所有事项。
|
|
24
|
+
- Guard / verification: 功能链 checkpoint 检查提前完成被阻止。
|
|
25
|
+
- Red condition: 漏勾仍显示完成。
|
|
26
|
+
- Green condition: 漏勾时提示未完成。
|
|
27
|
+
- Expected failure reason: 完成判断回归时,生活清单功能链会失败。
|
|
28
|
+
|
|
29
|
+
### BC-20260708-961: 晚间清单完成后没有保存
|
|
30
|
+
|
|
31
|
+
- Status: resolved
|
|
32
|
+
- First observed: 2026-07-08
|
|
33
|
+
- Last checked: 2026-07-08
|
|
34
|
+
- Scope: Evening reset checklist
|
|
35
|
+
- Tags: #feature-chain #life #persistence
|
|
36
|
+
- Display summary: 晚间收纳清单完成后必须保存结果。
|
|
37
|
+
- Phenomenon: 页面显示完成,但刷新或读取状态时没有保存完成记录。
|
|
38
|
+
- Trigger / reproduction: 勾选全部事项并点击完成。
|
|
39
|
+
- Root cause: UI 状态更新后没有写入 localStorage。
|
|
40
|
+
- Fix method: 完成时写入 completed/count/total。
|
|
41
|
+
- Guard / verification: 功能链 checkpoint 检查完成结果已保存。
|
|
42
|
+
- Red condition: 完成后没有保存状态。
|
|
43
|
+
- Green condition: 完成后保存 completed=true 且数量正确。
|
|
44
|
+
- Expected failure reason: 保存回归时,生活清单功能链会失败。
|
|
45
|
+
|
|
46
|
+
### BC-20260708-962: 角色卡空白名字生成空主角
|
|
47
|
+
|
|
48
|
+
- Status: resolved
|
|
49
|
+
- First observed: 2026-07-08
|
|
50
|
+
- Last checked: 2026-07-08
|
|
51
|
+
- Scope: Story character card generator
|
|
52
|
+
- Tags: #feature-chain #creative
|
|
53
|
+
- Display summary: 角色卡生成器不能把空白名字直接写成空主角。
|
|
54
|
+
- Phenomenon: 用户没填角色名时,生成文本出现空主角字段。
|
|
55
|
+
- Trigger / reproduction: 只填写少量线索并点击生成角色卡。
|
|
56
|
+
- Root cause: 输入规范化缺失。
|
|
57
|
+
- Fix method: 空白角色名使用可读默认名。
|
|
58
|
+
- Guard / verification: 功能链 checkpoint 检查输入被规范化。
|
|
59
|
+
- Red condition: 空白角色名进入最终文本。
|
|
60
|
+
- Green condition: 空白角色名被规范化为可读默认名。
|
|
61
|
+
- Expected failure reason: 输入规范化回归时,创作工具功能链会失败。
|
|
62
|
+
|
|
63
|
+
### BC-20260708-963: 角色卡重复生成不稳定
|
|
64
|
+
|
|
65
|
+
- Status: resolved
|
|
66
|
+
- First observed: 2026-07-08
|
|
67
|
+
- Last checked: 2026-07-08
|
|
68
|
+
- Scope: Story character card generator
|
|
69
|
+
- Tags: #feature-chain #creative #determinism
|
|
70
|
+
- Display summary: 相同线索重复生成角色卡时输出应稳定。
|
|
71
|
+
- Phenomenon: 用户用相同线索重复点击生成,角色卡文本不断变化。
|
|
72
|
+
- Trigger / reproduction: 相同输入连续点击生成角色卡。
|
|
73
|
+
- Root cause: 输出生成缺少稳定规则。
|
|
74
|
+
- Fix method: 对同一输入使用确定性结构。
|
|
75
|
+
- Guard / verification: 功能链 checkpoint 检查最终文本稳定可复制。
|
|
76
|
+
- Red condition: 同一输入重复生成不同结果。
|
|
77
|
+
- Green condition: 同一输入重复生成完全一致。
|
|
78
|
+
- Expected failure reason: 稳定性回归时,创作工具功能链会失败。
|
|
79
|
+
MD
|
|
80
|
+
|
|
81
|
+
propose_life="$ROOT/propose-life.out"
|
|
82
|
+
python3 "$SCRIPT" feature-chain-propose \
|
|
83
|
+
--root "$ROOT" \
|
|
84
|
+
--title "晚间收纳重置" \
|
|
85
|
+
--entry "勾选晚间收纳事项并点击完成" \
|
|
86
|
+
--exit-check "页面显示 3/3 完成并保存状态" \
|
|
87
|
+
--node-title "阻止提前完成" \
|
|
88
|
+
--bad-cases "BC-20260708-960" \
|
|
89
|
+
--check "漏勾任一事项时不能显示完成" >"$propose_life"
|
|
90
|
+
life_id="$(grep 'feature chain proposed:' "$propose_life" | awk '{print $NF}')"
|
|
91
|
+
test -n "$life_id"
|
|
92
|
+
|
|
93
|
+
python3 "$SCRIPT" feature-chain-attach-bc \
|
|
94
|
+
--root "$ROOT" \
|
|
95
|
+
--chain-id "$life_id" \
|
|
96
|
+
--node-title "保存完成结果" \
|
|
97
|
+
--bad-case "BC-20260708-961" \
|
|
98
|
+
--check "完成后必须保存 completed=true 且 count=3 total=3" >/dev/null
|
|
99
|
+
|
|
100
|
+
propose_story="$ROOT/propose-story.out"
|
|
101
|
+
python3 "$SCRIPT" feature-chain-propose \
|
|
102
|
+
--root "$ROOT" \
|
|
103
|
+
--title "故事角色卡生成" \
|
|
104
|
+
--entry "填写线索并点击生成角色卡" \
|
|
105
|
+
--exit-check "生成稳定、完整、可复制的角色卡" \
|
|
106
|
+
--node-title "输入被规范化" \
|
|
107
|
+
--bad-cases "BC-20260708-962" \
|
|
108
|
+
--check "空白角色名必须被规范化为可读默认名" >"$propose_story"
|
|
109
|
+
story_id="$(grep 'feature chain proposed:' "$propose_story" | awk '{print $NF}')"
|
|
110
|
+
test -n "$story_id"
|
|
111
|
+
|
|
112
|
+
python3 "$SCRIPT" feature-chain-attach-bc \
|
|
113
|
+
--root "$ROOT" \
|
|
114
|
+
--chain-id "$story_id" \
|
|
115
|
+
--node-title "最终文本稳定可复制" \
|
|
116
|
+
--bad-case "BC-20260708-963" \
|
|
117
|
+
--check "同一输入重复生成必须输出一致" >/dev/null
|
|
118
|
+
|
|
119
|
+
life_runner="$ROOT/.codex/context/test-hub/fixtures/life_chain.sh"
|
|
120
|
+
story_runner="$ROOT/.codex/context/test-hub/fixtures/story_chain.sh"
|
|
121
|
+
|
|
122
|
+
cat >"$life_runner" <<'SH'
|
|
123
|
+
#!/usr/bin/env bash
|
|
124
|
+
set -euo pipefail
|
|
125
|
+
echo "CG_CHECKPOINT:阻止提前完成:PASS"
|
|
126
|
+
echo "CG_CHECKPOINT:保存完成结果:PASS"
|
|
127
|
+
SH
|
|
128
|
+
chmod +x "$life_runner"
|
|
129
|
+
|
|
130
|
+
cat >"$story_runner" <<'SH'
|
|
131
|
+
#!/usr/bin/env bash
|
|
132
|
+
set -euo pipefail
|
|
133
|
+
echo "CG_CHECKPOINT:输入被规范化:PASS"
|
|
134
|
+
echo "CG_CHECKPOINT:最终文本稳定可复制:PASS"
|
|
135
|
+
SH
|
|
136
|
+
chmod +x "$story_runner"
|
|
137
|
+
|
|
138
|
+
python3 "$SCRIPT" feature-chain-approve \
|
|
139
|
+
--root "$ROOT" \
|
|
140
|
+
--chain-id "$life_id" \
|
|
141
|
+
--command-text "bash .codex/context/test-hub/fixtures/life_chain.sh" >"$ROOT/approve-life.out"
|
|
142
|
+
grep -q "approval dry-run: passed" "$ROOT/approve-life.out"
|
|
143
|
+
|
|
144
|
+
python3 "$SCRIPT" feature-chain-approve \
|
|
145
|
+
--root "$ROOT" \
|
|
146
|
+
--chain-id "$story_id" \
|
|
147
|
+
--command-text "bash .codex/context/test-hub/fixtures/story_chain.sh" >"$ROOT/approve-story.out"
|
|
148
|
+
grep -q "approval dry-run: passed" "$ROOT/approve-story.out"
|
|
149
|
+
|
|
150
|
+
python3 "$SCRIPT" dev-complete --root "$ROOT" >"$ROOT/pass-two.out"
|
|
151
|
+
grep -q "test hub: 2 passed, 0 failed, 0 blocked" "$ROOT/pass-two.out"
|
|
152
|
+
grep -q "passed: 晚间收纳重置" "$ROOT/pass-two.out"
|
|
153
|
+
grep -q "passed: 故事角色卡生成" "$ROOT/pass-two.out"
|
|
154
|
+
grep -q "success artifacts cleaned" "$ROOT/pass-two.out"
|
|
155
|
+
|
|
156
|
+
cat >"$story_runner" <<'SH'
|
|
157
|
+
#!/usr/bin/env bash
|
|
158
|
+
set -euo pipefail
|
|
159
|
+
echo "CG_CHECKPOINT:输入被规范化:PASS"
|
|
160
|
+
echo "CG_CHECKPOINT:最终文本稳定可复制:FAIL:相同线索重复生成了不同角色卡"
|
|
161
|
+
printf "story output drifted\n" > "$CONTEXT_GUARD_TEST_RUN_DIR/story-drift.txt"
|
|
162
|
+
SH
|
|
163
|
+
chmod +x "$story_runner"
|
|
164
|
+
|
|
165
|
+
if python3 "$SCRIPT" dev-complete --root "$ROOT" >"$ROOT/fail-one.out" 2>&1; then
|
|
166
|
+
cat "$ROOT/fail-one.out"
|
|
167
|
+
exit 1
|
|
168
|
+
fi
|
|
169
|
+
grep -q "test hub: 1 passed, 1 failed, 0 blocked" "$ROOT/fail-one.out"
|
|
170
|
+
grep -q "passed: 晚间收纳重置" "$ROOT/fail-one.out"
|
|
171
|
+
grep -q "failed: 故事角色卡生成 (checkpoint failed: 最终文本稳定可复制 - 相同线索重复生成了不同角色卡)" "$ROOT/fail-one.out"
|
|
172
|
+
grep -q "evidence preserved" "$ROOT/fail-one.out"
|
|
173
|
+
|
|
174
|
+
python3 - "$ROOT/.codex/context/test-hub/last-run.json" <<'PY'
|
|
175
|
+
from pathlib import Path
|
|
176
|
+
import json
|
|
177
|
+
import sys
|
|
178
|
+
|
|
179
|
+
run = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
|
180
|
+
results = {item["title"]: item for item in run["results"]}
|
|
181
|
+
assert results["晚间收纳重置"]["status"] == "passed", results
|
|
182
|
+
story = results["故事角色卡生成"]
|
|
183
|
+
assert story["status"] == "failed", story
|
|
184
|
+
assert story["reason"] == "checkpoint failed: 最终文本稳定可复制 - 相同线索重复生成了不同角色卡", story
|
|
185
|
+
assert [c["label"] for c in story["checkpoints"]] == ["输入被规范化", "最终文本稳定可复制"], story
|
|
186
|
+
evidence = Path(story["log"]).parent / "story-drift.txt"
|
|
187
|
+
assert evidence.exists(), evidence
|
|
188
|
+
assert "story output drifted" in evidence.read_text(encoding="utf-8")
|
|
189
|
+
PY
|
|
190
|
+
|
|
191
|
+
cat >"$story_runner" <<'SH'
|
|
192
|
+
#!/usr/bin/env bash
|
|
193
|
+
set -euo pipefail
|
|
194
|
+
echo "CG_CHECKPOINT:输入被规范化:PASS"
|
|
195
|
+
echo "CG_CHECKPOINT:最终文本稳定可复制:PASS"
|
|
196
|
+
SH
|
|
197
|
+
chmod +x "$story_runner"
|
|
198
|
+
|
|
199
|
+
python3 "$SCRIPT" dev-complete --root "$ROOT" >"$ROOT/final-pass.out"
|
|
200
|
+
grep -q "test hub: 2 passed, 0 failed, 0 blocked" "$ROOT/final-pass.out"
|
|
201
|
+
grep -q "success artifacts cleaned" "$ROOT/final-pass.out"
|
|
202
|
+
|
|
203
|
+
python3 "$SCRIPT" feature-chain-summary --root "$ROOT" >"$ROOT/summary.out"
|
|
204
|
+
grep -q "chains: 2" "$ROOT/summary.out"
|
|
205
|
+
grep -q "covered bad cases: 4" "$ROOT/summary.out"
|
|
206
|
+
grep -q "coverage density: 2.0 bad case(s) per covered chain" "$ROOT/summary.out"
|
|
207
|
+
grep -q "reuse signal: one workflow covers up to 2 bad case(s)" "$ROOT/summary.out"
|
|
208
|
+
|
|
209
|
+
python3 "$SCRIPT" validate-feature-chains --root "$ROOT" >"$ROOT/validate.out"
|
|
210
|
+
grep -q "feature-chain validation passed: 2 chain(s), 0 warning(s)" "$ROOT/validate.out"
|