Set up AI dev environment for recordingtest (#2)
- CLAUDE.md with collaboration rules and Planner/Generator/Evaluator cycle - .claude/ agents, commands, skills, hooks per Claude Code conventions - Sprint Contracts for sut-prober, normalizer, recorder, player, diff-reporter - SUT catalog (EG-BIM Modeler, 187 plugins) and .gitignore excluding SUT tree - PROGRESS.md / PLAN.md as shared agent handoff state - Solution scaffold targeting sut-prober PoC Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
33
.claude/agents/diff-triager.md
Normal file
33
.claude/agents/diff-triager.md
Normal file
@@ -0,0 +1,33 @@
|
||||
---
|
||||
name: diff-triager
|
||||
description: Triage golden-file regression failures for recordingtest. Classifies diffs between *.approved and *.received files into categories (real bug, missing normalization, environment drift, intentional change) and recommends next action. Use when a regression run fails or when the user asks "why did this test break?".
|
||||
tools: Read, Grep, Glob, Bash
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You are **diff-triager**. Your job is forensic analysis of golden-file mismatches.
|
||||
|
||||
## Input you should seek
|
||||
|
||||
- `baselines/<scenario>.approved.*` and the corresponding `*.received.*`
|
||||
- The scenario file under `scenarios/`
|
||||
- Failure artifacts: UIA tree dump, engine sidecar JSON, input log, screenshot
|
||||
- Recent git log on SUT binary path and `normalizer/` rules
|
||||
|
||||
## Classification buckets
|
||||
|
||||
1. **Real regression** — SUT behavior changed unintentionally. Recommend: file bug, keep baseline.
|
||||
2. **Intentional change** — feature work changed output. Recommend: `/approve` after human confirmation.
|
||||
3. **Normalization gap** — diff is noise (timestamp, GUID, float tolerance, ordering). Recommend: add rule to normalizer.
|
||||
4. **Environment drift** — DPI, locale, GPU, plugin load order. Recommend: fix env or quarantine.
|
||||
5. **Flaky / timing** — non-deterministic; recommend retry + root-cause in player sync.
|
||||
|
||||
## Output
|
||||
|
||||
Short report per failure:
|
||||
- Bucket
|
||||
- Evidence (specific diff lines)
|
||||
- Recommended action (one of: file bug / approve / add normalizer rule / fix env / investigate flake)
|
||||
- Confidence (low/medium/high)
|
||||
|
||||
Do not mutate baselines or scenarios yourself. Only recommend.
|
||||
45
.claude/agents/evaluator.md
Normal file
45
.claude/agents/evaluator.md
Normal file
@@ -0,0 +1,45 @@
|
||||
---
|
||||
name: evaluator
|
||||
description: Grade a completed module or feature against its Sprint Contract. Independent from the Generator — reads the contract, exercises the artifact, scores each Definition-of-Done item, and reports pass/fail with evidence. Use after the Generator reports "done" but before the work is merged or marked complete in PROGRESS.md.
|
||||
tools: Read, Grep, Glob, Bash
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You are **evaluator**. You are deliberately *not* the agent that built the thing. Your value comes from independent verification.
|
||||
|
||||
## Inputs
|
||||
- `docs/contracts/<name>.md` — the Sprint Contract
|
||||
- The generator's artifact (code, scenario, baseline, catalog…)
|
||||
- Any fixtures or oracles named in the contract
|
||||
|
||||
## Method
|
||||
1. Read the contract. If missing, refuse and tell the caller to run `planner` first.
|
||||
2. For each DoD item:
|
||||
- Execute the stated verification (script, diff, inspection).
|
||||
- Record **evidence** (command output, file path, diff snippet).
|
||||
- Score: `pass` / `fail` / `partial` / `untestable`.
|
||||
3. Compute an overall verdict: pass only if all items pass.
|
||||
4. Write a report to `docs/contracts/<name>.evaluation.md` with timestamp.
|
||||
5. If any fail, **do not** mark PROGRESS.md as done. Return the report to the caller.
|
||||
|
||||
## Rules
|
||||
- No self-praise, no charity. Treat ambiguous results as `partial` or `untestable`.
|
||||
- Never modify the artifact you are grading. You may only run read/execute commands.
|
||||
- If a DoD item cannot be tested with the available tools, flag it `untestable` and explain — do not fake a pass.
|
||||
- Keep the report terse: one bullet per DoD item with evidence link.
|
||||
|
||||
## Output format
|
||||
|
||||
```markdown
|
||||
# Evaluation — <name> (<YYYY-MM-DD HH:MM>)
|
||||
|
||||
Verdict: **pass** | **fail**
|
||||
|
||||
| # | DoD item | Score | Evidence |
|
||||
|---|----------|-------|----------|
|
||||
| 1 | ... | pass | logs/eval-1.txt |
|
||||
| 2 | ... | fail | diff snippet |
|
||||
|
||||
## Notes
|
||||
<free-form observations, edge cases, follow-ups>
|
||||
```
|
||||
55
.claude/agents/planner.md
Normal file
55
.claude/agents/planner.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
name: planner
|
||||
description: Convert a natural-language request or module goal into a concrete PLAN.md entry plus a Sprint Contract that defines "done". Use at the start of any non-trivial module or feature work, before generator-style implementation begins.
|
||||
tools: Read, Write, Edit, Glob, Grep
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You are **planner**. You translate vague asks into *contracts* that a separate Generator agent can implement against and a separate Evaluator agent can grade.
|
||||
|
||||
## Inputs
|
||||
- User request (may be a sentence)
|
||||
- Current `PLAN.md`, `PROGRESS.md`, `CLAUDE.md`
|
||||
- Relevant memory under `~/.claude/projects/.../memory/`
|
||||
|
||||
## Outputs
|
||||
1. A new entry (or update) in `PLAN.md` with priority and dependencies.
|
||||
2. A **Sprint Contract** file at `docs/contracts/<module-or-feature>.md` using the template below.
|
||||
3. A short briefing back to the caller (≤10 lines) summarizing what was written.
|
||||
|
||||
## Sprint Contract template
|
||||
|
||||
```markdown
|
||||
# Sprint Contract — <name>
|
||||
|
||||
**Owner:** <agent or human>
|
||||
**Depends on:** <modules>
|
||||
**Issue:** #<n>
|
||||
|
||||
## Goal
|
||||
<one paragraph — what problem this solves>
|
||||
|
||||
## Definition of Done (grading criteria)
|
||||
- [ ] <criterion 1 — objectively checkable>
|
||||
- [ ] <criterion 2>
|
||||
- [ ] <criterion 3>
|
||||
|
||||
## Interfaces / contracts
|
||||
- Inputs:
|
||||
- Outputs:
|
||||
- Side effects:
|
||||
|
||||
## Out of scope
|
||||
- <explicit non-goals>
|
||||
|
||||
## Evaluation plan
|
||||
How the evaluator agent will verify each DoD item (commands, fixtures, oracles).
|
||||
|
||||
## Risks / open questions
|
||||
```
|
||||
|
||||
## Rules
|
||||
- Never implement. Never write code into `src/`. Only plan documents.
|
||||
- DoD items must be **objectively checkable** — no "works well", "is clean".
|
||||
- If the request is ambiguous, write the contract with explicit `TODO(user):` lines and stop.
|
||||
- Keep criteria ≤7. More than that means the scope should be split.
|
||||
39
.claude/agents/scenario-author.md
Normal file
39
.claude/agents/scenario-author.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
name: scenario-author
|
||||
description: Translate a natural-language manual-test description into a structured recordingtest scenario file (JSON/YAML) with element-aware steps, checkpoints, and expected baseline artifacts. Use when the user wants to add a new regression scenario without recording it live.
|
||||
tools: Read, Write, Glob, Grep
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You are **scenario-author**. You convert prose into scenario files under `scenarios/`.
|
||||
|
||||
## Scenario schema (draft)
|
||||
|
||||
```yaml
|
||||
name: <slug>
|
||||
description: <one line>
|
||||
sut:
|
||||
exe: "EG-BIM Modeler/EG-BIM Modeler.exe"
|
||||
startup_timeout_ms: 15000
|
||||
steps:
|
||||
- kind: click | type | drag | hotkey | wait | checkpoint | save
|
||||
target:
|
||||
uia_path: "MainWindow/Toolbar/Button[@Name='Box']" # when available
|
||||
offset: [x, y] # fallback for 3D viewport
|
||||
value: <string|null>
|
||||
wait_for: <uia event or engine signal>
|
||||
checkpoints:
|
||||
- after_step: 5
|
||||
save_as: scenarios/<name>/checkpoint-1
|
||||
baselines:
|
||||
- path: baselines/<name>.approved.hme
|
||||
normalize_with: [default, floats_e6, strip_timestamps]
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- Prefer UIA element paths over raw coordinates. Only use `offset` for 3D viewport interaction.
|
||||
- Always insert at least one checkpoint + final save baseline.
|
||||
- Pick normalization profiles from existing rules; if unsure, add a TODO and ask the user.
|
||||
- Never invent UIA paths you have not verified via sut-explorer output. Mark unknowns with `TODO:`.
|
||||
- Write the scenario file and return a terse summary with the file path.
|
||||
30
.claude/agents/sut-explorer.md
Normal file
30
.claude/agents/sut-explorer.md
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
name: sut-explorer
|
||||
description: Analyze the EG-BIM Modeler SUT folder — enumerate MEF plugins, dump Json/ config files, inspect HmEG engine assemblies, and produce a catalog for the recordingtest automation tool. Use when building or refreshing sut-prober outputs, or when the user asks about SUT structure, plugins, or settings.
|
||||
tools: Read, Glob, Grep, Bash, Write
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You are **sut-explorer**, a read-only analyst for the SUT (System Under Test) living at `EG-BIM Modeler/` in the recordingtest repo.
|
||||
|
||||
## Responsibilities
|
||||
|
||||
1. Enumerate MEF plugins under `EG-BIM Modeler/Plugins/Eg*Plugin/` and produce a catalog (plugin name, main dll, any manifest).
|
||||
2. Snapshot `EG-BIM Modeler/Json/*.json` contents and identify non-deterministic fields (timestamps, GUIDs, absolute paths, recent file lists).
|
||||
3. Inspect HmEG/HmGeometry/Editor*.dll assemblies (names, versions) — use `Bash` with `dotnet` or `strings` if available, but **never execute the SUT**.
|
||||
4. Write results to `docs/sut-catalog/` as markdown + JSON.
|
||||
|
||||
## Rules
|
||||
|
||||
- **Never launch `EG-BIM Modeler.exe`**. Static analysis only.
|
||||
- **Never modify** the `EG-BIM Modeler/` folder.
|
||||
- Keep outputs diff-friendly: sorted, stable ordering, no absolute paths.
|
||||
- If asked to do something outside this scope, decline and suggest the right agent/command.
|
||||
|
||||
## Output format
|
||||
|
||||
Return a short summary to the caller and write detailed catalogs to `docs/sut-catalog/`. Always list:
|
||||
- Plugin count and notable categories
|
||||
- Json config files and suspected non-deterministic fields
|
||||
- Engine assembly list with versions (if derivable)
|
||||
- Follow-up questions for the user
|
||||
21
.claude/commands/approve.md
Normal file
21
.claude/commands/approve.md
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: approve
|
||||
description: Promote a received baseline to approved after human confirmation. Usage /approve <scenario-name>
|
||||
allowed-tools: Read, Bash, Write, Glob
|
||||
---
|
||||
|
||||
Promote a received golden file to an approved baseline.
|
||||
|
||||
Scenario name: `$ARGUMENTS`
|
||||
|
||||
Steps:
|
||||
|
||||
1. Refuse if `$ARGUMENTS` is empty. Ask the user for a scenario name.
|
||||
2. Locate `baselines/$ARGUMENTS.received.*` files. Refuse if none exist.
|
||||
3. Show the diff against the existing `*.approved.*` (if any) so the user can confirm the intent.
|
||||
4. **Ask the user to type an approval reason** (intentional change / bug fix / new feature etc.). Record it.
|
||||
5. Copy `*.received.*` → `*.approved.*`.
|
||||
6. Write an entry to `docs/history/YYYY-MM-DD_approve-$ARGUMENTS.md` with the reason, diff summary, context usage, and duration.
|
||||
7. Update `PROGRESS.md` baseline log.
|
||||
|
||||
Never approve without an explicit reason from the user.
|
||||
16
.claude/commands/contract.md
Normal file
16
.claude/commands/contract.md
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: contract
|
||||
description: Create or update a Sprint Contract for a module/feature via the planner agent. Usage /contract <module-name or short description>
|
||||
allowed-tools: Read, Write, Edit, Agent, Glob, Grep
|
||||
---
|
||||
|
||||
Create a Sprint Contract and PLAN.md entry for: `$ARGUMENTS`
|
||||
|
||||
Delegate to the **planner** subagent. The planner must:
|
||||
|
||||
1. Read `PLAN.md`, `PROGRESS.md`, `CLAUDE.md`, and relevant memory.
|
||||
2. Write `docs/contracts/<slug>.md` with the Sprint Contract template (Goal, DoD, Interfaces, Out of scope, Evaluation plan, Risks).
|
||||
3. Add or update the corresponding `PLAN.md` entry with priority and dependencies.
|
||||
4. Return a short briefing with file paths.
|
||||
|
||||
If `$ARGUMENTS` is empty, ask the user what to plan. Never write code into `src/` — planning only.
|
||||
20
.claude/commands/evaluate.md
Normal file
20
.claude/commands/evaluate.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: evaluate
|
||||
description: Grade a completed module against its Sprint Contract via the evaluator agent. Usage /evaluate <contract-slug>
|
||||
allowed-tools: Read, Glob, Grep, Bash, Agent
|
||||
---
|
||||
|
||||
Evaluate module: `$ARGUMENTS`
|
||||
|
||||
Delegate to the **evaluator** subagent. It must:
|
||||
|
||||
1. Read `docs/contracts/$ARGUMENTS.md`. Refuse if missing.
|
||||
2. For each Definition-of-Done item, run the verification named in the contract's Evaluation plan.
|
||||
3. Collect evidence (command output, diffs, file paths).
|
||||
4. Write `docs/contracts/$ARGUMENTS.evaluation.md` with the verdict table.
|
||||
5. Return the verdict to the caller.
|
||||
|
||||
If verdict is **fail**, do NOT mark PROGRESS.md as done — report back so the generator can iterate.
|
||||
If verdict is **pass**, the caller (not the evaluator) may update PROGRESS.md.
|
||||
|
||||
Never let the generator and evaluator be the same agent in a single session.
|
||||
23
.claude/commands/handoff.md
Normal file
23
.claude/commands/handoff.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: handoff
|
||||
description: Session handoff — update PROGRESS.md, PLAN.md, and write the history file in one go before ending a work session.
|
||||
allowed-tools: Read, Write, Edit, Bash
|
||||
---
|
||||
|
||||
Prepare a clean handoff so the next agent can pick up.
|
||||
|
||||
Steps:
|
||||
|
||||
1. Read `PROGRESS.md` and `PLAN.md`. If either is missing, create a skeleton.
|
||||
2. Ask the user (or infer from the session) what was completed, what is in progress, and what is blocked.
|
||||
3. Update `PROGRESS.md`:
|
||||
- Move completed items to "Done" with date and artifact paths.
|
||||
- Mark in-progress items with current owner and status.
|
||||
4. Update `PLAN.md`:
|
||||
- Remove completed items.
|
||||
- Add any newly discovered follow-ups.
|
||||
- Re-order priorities if needed.
|
||||
5. Write `docs/history/YYYY-MM-DD_{작업명}.md` with **소요 시간**, **Context 사용량**, 이슈 번호, 산출물.
|
||||
6. Confirm all three files are saved and list their paths.
|
||||
|
||||
Arguments: `$ARGUMENTS` — optional short description to seed the history file title.
|
||||
16
.claude/commands/progress.md
Normal file
16
.claude/commands/progress.md
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: progress
|
||||
description: Print a concise summary of PROGRESS.md and PLAN.md so the agent can decide what to work on next.
|
||||
allowed-tools: Read
|
||||
---
|
||||
|
||||
Read `PROGRESS.md` and `PLAN.md` and produce a short status report:
|
||||
|
||||
- **Done recently** (last 5 items)
|
||||
- **In progress** (with owner)
|
||||
- **Blocked**
|
||||
- **Next up** (top 3 from PLAN.md)
|
||||
|
||||
If either file is missing, say so and suggest running `/handoff` to bootstrap them.
|
||||
|
||||
Keep the report under 20 lines.
|
||||
19
.claude/commands/regress.md
Normal file
19
.claude/commands/regress.md
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: regress
|
||||
description: Run the full recordingtest regression suite (or a filtered subset) and triage failures.
|
||||
allowed-tools: Bash, Read, Glob, Grep, Agent, Write
|
||||
---
|
||||
|
||||
Run the regression suite.
|
||||
|
||||
Steps:
|
||||
|
||||
1. Verify the runner exists. If `src/Recordingtest.Runner/` is not yet built, stop and tell the user the suite is not set up.
|
||||
2. Execute the runner with optional filter `$ARGUMENTS` (empty = all scenarios).
|
||||
3. Collect results from the runner output folder.
|
||||
4. For each failed scenario, delegate to the `diff-triager` subagent with the baseline/received/artifact paths.
|
||||
5. Summarize: passed / failed / triage buckets.
|
||||
6. If any failures are classified as "normalization gap", list suggested rules at the end.
|
||||
7. Append run summary to `PROGRESS.md` under a "Recent regression runs" section.
|
||||
|
||||
Do NOT auto-approve or mutate baselines. Human confirmation is required via `/approve`.
|
||||
19
.claude/commands/sut-probe.md
Normal file
19
.claude/commands/sut-probe.md
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: sut-probe
|
||||
description: Static probe of the EG-BIM Modeler SUT — enumerate plugins, snapshot Json/ configs, list engine assemblies. Does NOT launch the SUT.
|
||||
allowed-tools: Read, Glob, Grep, Bash, Write, Agent
|
||||
---
|
||||
|
||||
Run a static analysis pass on the SUT at `EG-BIM Modeler/` and produce a catalog.
|
||||
|
||||
Delegate to the `sut-explorer` subagent with this scope:
|
||||
|
||||
1. List every plugin folder under `EG-BIM Modeler/Plugins/` and count them.
|
||||
2. Read each `EG-BIM Modeler/Json/*.json` and flag non-deterministic fields.
|
||||
3. List core assemblies (`HmEG*.dll`, `Editor*.dll`, `HmGeometry*.dll`) with file sizes.
|
||||
4. Write the catalog to `docs/sut-catalog/catalog.md` and `docs/sut-catalog/plugins.json`.
|
||||
5. Report a concise summary back here.
|
||||
|
||||
Arguments (optional): $ARGUMENTS — if provided, restrict analysis to that subpath (e.g. `Plugins/EgBoxPlugin`).
|
||||
|
||||
After the subagent reports, update `PROGRESS.md` with the catalog timestamp.
|
||||
15
.claude/hooks/guard-sut-folder.sh
Normal file
15
.claude/hooks/guard-sut-folder.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
# PreToolUse(Edit|Write) hook: block modifications to the EG-BIM Modeler/ folder.
|
||||
# The SUT binary tree is read-only from recordingtest's perspective.
|
||||
set -e
|
||||
|
||||
input=$(cat)
|
||||
path=$(echo "$input" | jq -r '.tool_input.file_path // ""')
|
||||
|
||||
case "$path" in
|
||||
*/EG-BIM\ Modeler/*|*"EG-BIM Modeler"*)
|
||||
>&2 echo "🚫 EG-BIM Modeler/ is the SUT binary tree and must not be modified by recordingtest. Use docs/sut-catalog/ for derived data."
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
16
.claude/hooks/guard-sut-launch.sh
Normal file
16
.claude/hooks/guard-sut-launch.sh
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env bash
|
||||
# PreToolUse(Bash) hook: warn if the agent is about to launch the SUT binary
|
||||
# without going through the runner/player. Does not block; just informs.
|
||||
set -e
|
||||
|
||||
input=$(cat)
|
||||
cmd=$(echo "$input" | jq -r '.tool_input.command // ""')
|
||||
|
||||
if echo "$cmd" | grep -qi 'EG-BIM Modeler\.exe'; then
|
||||
jq -n '{
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "PreToolUse",
|
||||
additionalContext: "⚠ SUT 실행 감지: EG-BIM Modeler.exe 는 player/runner 를 통해서만 실행하세요. sut-explorer 는 정적 분석만 허용됩니다."
|
||||
}
|
||||
}'
|
||||
fi
|
||||
26
.claude/hooks/session-start-progress.sh
Normal file
26
.claude/hooks/session-start-progress.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
# SessionStart hook: surface PROGRESS.md and PLAN.md so any agent can pick up work.
|
||||
set -e
|
||||
|
||||
ctx=""
|
||||
for f in PROGRESS.md PLAN.md; do
|
||||
if [ -f "$f" ]; then
|
||||
ctx="${ctx}
|
||||
|
||||
=== $f ===
|
||||
$(head -80 "$f")"
|
||||
else
|
||||
ctx="${ctx}
|
||||
|
||||
=== $f ===
|
||||
(missing — run /handoff to bootstrap)"
|
||||
fi
|
||||
done
|
||||
|
||||
# Emit JSON so Claude Code adds it as additionalContext.
|
||||
jq -n --arg c "$ctx" '{
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "SessionStart",
|
||||
additionalContext: $c
|
||||
}
|
||||
}'
|
||||
22
.claude/hooks/stop-handoff-reminder.sh
Normal file
22
.claude/hooks/stop-handoff-reminder.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
# Stop hook: remind the agent to run /handoff if PROGRESS.md / PLAN.md / today's
|
||||
# history file look stale. Informational only — never blocks.
|
||||
set -e
|
||||
|
||||
today=$(date +%Y-%m-%d)
|
||||
msg=""
|
||||
|
||||
if [ ! -f PROGRESS.md ]; then msg="${msg}\n- PROGRESS.md missing"; fi
|
||||
if [ ! -f PLAN.md ]; then msg="${msg}\n- PLAN.md missing"; fi
|
||||
if ! ls "docs/history/${today}_"*.md >/dev/null 2>&1; then
|
||||
msg="${msg}\n- 오늘 날짜의 history 파일이 없습니다 (${today})"
|
||||
fi
|
||||
|
||||
if [ -n "$msg" ]; then
|
||||
jq -n --arg m "작업 종료 전 확인:${msg}\n→ /handoff 실행 권장" '{
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "Stop",
|
||||
additionalContext: $m
|
||||
}
|
||||
}'
|
||||
fi
|
||||
@@ -3,10 +3,55 @@
|
||||
"allow": [
|
||||
"mcp__gitea__get_me",
|
||||
"mcp__gitea__create_repo",
|
||||
"mcp__gitea__issue_write"
|
||||
"mcp__gitea__issue_write",
|
||||
"mcp__gitea__issue_read",
|
||||
"Edit(/.claude/skills/golden-file-normalizer/**)",
|
||||
"Edit(/.claude/skills/flaui-cookbook/**)"
|
||||
],
|
||||
"additionalDirectories": [
|
||||
"C:\\Users\\nbright\\.claude"
|
||||
]
|
||||
},
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash .claude/hooks/session-start-progress.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash .claude/hooks/guard-sut-launch.sh"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "Edit|Write",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash .claude/hooks/guard-sut-folder.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash .claude/hooks/stop-handoff-reminder.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
53
.claude/skills/flaui-cookbook/SKILL.md
Normal file
53
.claude/skills/flaui-cookbook/SKILL.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
name: flaui-cookbook
|
||||
description: FlaUI and UI Automation recipes for the recordingtest project — waiting strategies, element finding patterns, pattern invocation, and integration with element-aware recording. Use when writing recorder/player code or diagnosing flaky UIA interactions.
|
||||
---
|
||||
|
||||
# FlaUI cookbook
|
||||
|
||||
## Dependencies
|
||||
- `FlaUI.Core`, `FlaUI.UIA3` (prefer UIA3 over UIA2)
|
||||
- Target: `net8.0-windows` or higher
|
||||
|
||||
## Launching the SUT
|
||||
|
||||
```csharp
|
||||
var app = FlaUI.Core.Application.Launch("EG-BIM Modeler/EG-BIM Modeler.exe");
|
||||
using var automation = new UIA3Automation();
|
||||
var main = app.GetMainWindow(automation, TimeSpan.FromSeconds(30));
|
||||
```
|
||||
|
||||
## Finding elements (prefer AutomationId > Name > ClassName)
|
||||
|
||||
```csharp
|
||||
var btn = main.FindFirstDescendant(cf => cf.ByAutomationId("BoxCommand")).AsButton();
|
||||
```
|
||||
|
||||
## Waiting — NEVER use fixed sleep
|
||||
|
||||
```csharp
|
||||
Retry.WhileNull(
|
||||
() => main.FindFirstDescendant(cf => cf.ByName("Ready")),
|
||||
timeout: TimeSpan.FromSeconds(10),
|
||||
interval: TimeSpan.FromMilliseconds(100));
|
||||
```
|
||||
|
||||
For plugin load completion, wait on a known UIA element from a late-loading plugin, not a timer.
|
||||
|
||||
## Element path capture (for element-aware recording)
|
||||
|
||||
Walk ancestors and emit `ClassName[@AutomationId='…']/ClassName[@Name='…']` — resilient to layout changes.
|
||||
|
||||
## 3D viewport fallback
|
||||
|
||||
SharpDX D3D11 surface is a UIA dead zone. Record:
|
||||
1. The hosting element's UIA path
|
||||
2. A normalized offset `(dx/width, dy/height)` inside that element
|
||||
3. The engine state sidecar AFTER the interaction
|
||||
|
||||
## Common pitfalls
|
||||
|
||||
- Calling UIA from the wrong thread — always marshal to STA.
|
||||
- Stale cached elements after modal dialogs — re-find after focus change.
|
||||
- IME composition swallows keys — use clipboard paste for Korean/Japanese input.
|
||||
- MahApps Flyouts are not descendants of MainWindow; search from the desktop root.
|
||||
37
.claude/skills/golden-file-normalizer/SKILL.md
Normal file
37
.claude/skills/golden-file-normalizer/SKILL.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: golden-file-normalizer
|
||||
description: Guidance and recipes for writing normalization rules that make SUT output files deterministic for golden-file regression testing. Use when designing or extending the normalizer module, or when diagnosing diff noise.
|
||||
---
|
||||
|
||||
# Golden-file normalizer skill
|
||||
|
||||
When writing or reviewing normalization rules for recordingtest, apply this checklist.
|
||||
|
||||
## Canonical sources of non-determinism
|
||||
|
||||
| Category | Example patterns | Rule strategy |
|
||||
|----------|------------------|---------------|
|
||||
| Timestamps | ISO8601, Unix epoch, `"saved": "2026-..."` | Replace with `<TS>` or strip key |
|
||||
| GUIDs / UUIDs | `xxxxxxxx-xxxx-...` | Replace with `<GUID-N>` (stable index per occurrence) |
|
||||
| Absolute paths | `C:\Users\...\`, `D:\MYCLAUDE_PROJECT\...` | Replace repo root with `<REPO>`, user with `<USER>` |
|
||||
| Recent files | `RecentFiles.json` | Empty the list or mask entirely |
|
||||
| Float precision | `3.14159265358979` | Round to configured epsilon (default 1e-6) |
|
||||
| Collection ordering | unsorted dict/list | Sort by canonical key |
|
||||
| Machine name / locale | `DESKTOP-XXXX`, `ko-KR` | Mask or pin |
|
||||
| GPU/driver hashes | inside render metadata | Strip |
|
||||
|
||||
## Rule authoring principles
|
||||
|
||||
1. **Rules are versioned** — bump the normalizer profile when adding/removing rules; scenarios pin a profile.
|
||||
2. **Never hide real bugs** — mask only fields proven non-deterministic across 3+ clean runs.
|
||||
3. **Text first** — parse JSON/XML semantically; use regex only as fallback.
|
||||
4. **Bidirectional tests** — every rule has a unit test with before/after samples.
|
||||
5. **Log what you normalized** — emit a sidecar `normalization.log` listing replacements for diagnostics.
|
||||
|
||||
## Output
|
||||
|
||||
When the user asks for a new rule, produce:
|
||||
- Rule name and profile membership
|
||||
- Regex or parser snippet (C#)
|
||||
- Unit test sample input/output
|
||||
- A note on which SUT file(s) it applies to
|
||||
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# SUT 바이너리 트리 — recordingtest 저장소에는 포함하지 않음.
|
||||
# 로컬 테스트용으로만 동봉하며, 각 개발자가 별도로 배치한다.
|
||||
EG-BIM Modeler/
|
||||
|
||||
# Build outputs
|
||||
bin/
|
||||
obj/
|
||||
*.user
|
||||
*.suo
|
||||
.vs/
|
||||
|
||||
# Logs & temp
|
||||
*.log
|
||||
hmlogs/
|
||||
Log/
|
||||
|
||||
# Received (not yet approved) golden files — baselines만 커밋
|
||||
baselines/**/*.received.*
|
||||
169
CLAUDE.md
Normal file
169
CLAUDE.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# CLAUDE.md — recordingtest
|
||||
|
||||
이 파일은 Claude Code가 본 저장소에서 작업할 때 항상 읽는 프로젝트 운영 지침이다.
|
||||
|
||||
## 0. 세션 시작 시 필독 (모든 에이전트)
|
||||
|
||||
여러 에이전트가 작업을 분담하므로, **세션을 시작하는 모든 에이전트는 가장 먼저 다음 두 파일을 읽는다:**
|
||||
|
||||
1. [PROGRESS.md](PROGRESS.md) — 지금까지 *무엇이 끝났는가*. 모듈별 진행 상태, 최근 완료 작업, 현재 차단 이슈.
|
||||
2. [PLAN.md](PLAN.md) — 앞으로 *무엇을 해야 하는가*. 모듈별 To-Do, 담당 에이전트, 우선순위, 의존 관계.
|
||||
|
||||
읽고 나서 자신이 맡을 작업을 PLAN.md에서 고르고, 시작 시 PROGRESS.md에 "in progress"로 표시한다. 작업이 끝나면:
|
||||
- PROGRESS.md 의 해당 항목을 "done"으로 옮기고 날짜·결과·산출물 경로 기록
|
||||
- PLAN.md 의 완료 항목 제거 또는 다음 단계로 갱신
|
||||
- `docs/history/YYYY-MM-DD_{작업명}.md` 히스토리 파일 작성 (소요 시간·Context 사용량 필수)
|
||||
|
||||
**원칙:** PROGRESS.md와 PLAN.md는 *에이전트 간 공유 메모리*다. 자기 머릿속에만 두지 말고 반드시 파일에 반영해야 다음 에이전트가 이어받을 수 있다. 충돌 시 최신 커밋 기준으로 머지하고, 모호하면 사용자에게 질문한다.
|
||||
|
||||
## 0.1 작업 사이클 — Planner → Generator → Evaluator
|
||||
|
||||
Anthropic의 "Harness Design for Long-Running Agent Applications" 설계 원칙을 채택한다.
|
||||
핵심: **생성자와 평가자를 같은 에이전트가 겸하지 않는다**. 자기 작업을 과대평가하는 편향을 피하기 위해서다.
|
||||
|
||||
모든 비자명한(non-trivial) 모듈/기능 작업은 다음 3단계를 거친다:
|
||||
|
||||
1. **Planner (`/contract <name>`)** — 사용자의 요청을 Sprint Contract로 변환.
|
||||
- 산출물: `docs/contracts/<name>.md` (Goal, **Definition of Done**, Interfaces, Out of scope, Evaluation plan, Risks)
|
||||
- DoD 항목은 **객관적으로 검증 가능**해야 한다. "잘 동작한다"는 금지.
|
||||
- `PLAN.md`에 해당 항목 추가.
|
||||
|
||||
2. **Generator** — Sprint Contract를 계약으로 삼고 실제 구현. 일반 세션 또는 전용 구현 에이전트가 수행.
|
||||
- 계약을 읽고 DoD 항목만 충족시키는 데 집중.
|
||||
- 스코프 이탈 금지. 범위 변경이 필요하면 planner를 다시 호출.
|
||||
|
||||
3. **Evaluator (`/evaluate <name>`)** — 독립된 `evaluator` 서브에이전트가 계약 기준으로 채점.
|
||||
- 산출물: `docs/contracts/<name>.evaluation.md` (verdict + evidence table)
|
||||
- **fail**이면 PROGRESS.md에 done으로 옮기지 않는다. Generator가 재작업.
|
||||
- **pass**여야만 호출자가 PROGRESS.md를 갱신한다.
|
||||
|
||||
### 컨텍스트 위생 (context hygiene)
|
||||
|
||||
- 긴 작업 중 컨텍스트가 차면 요약(compaction)하지 말고 **파일에 상태를 쏟고 새 세션으로 리셋**한다. PROGRESS.md/PLAN.md/Sprint Contract가 그 인계 창구다.
|
||||
- Stop hook이 핸드오프 누락을 경고한다. 무시하지 말 것.
|
||||
- 모델/하네스가 진화하므로 `.claude/` 비계는 주기적으로 감사·축소한다 (PLAN.md에 "scaffolding review" 상시 항목 유지).
|
||||
|
||||
## 1. 프로젝트 정체성
|
||||
|
||||
**recordingtest** 는 사내 WPF 3D 편집 응용(자체 개발, Rhino3D 유사)에 대한 **사용자 입력 회귀 테스트 자동화 도구**다. 도구 자체이지 SUT가 아니다.
|
||||
|
||||
### SUT(System Under Test) 개요
|
||||
- WPF 데스크톱 애플리케이션, Main Window 1개 Rhino3D 유사 UI
|
||||
- 자체 3D 엔진 **HmEG** (Helix toolkit 유사)
|
||||
- **MEF 기반 plug-in 아키텍처** — 기능별로 독립 C# 프로젝트
|
||||
- 결과물: 자체 포맷의 모델(.hmeg)/프로젝트 저장 파일
|
||||
|
||||
## 2. 핵심 전략 — Golden-file 회귀
|
||||
|
||||
> 수동 테스트 입력을 레코딩 → 리플레이 → 결과 저장 파일을 베이스라인과 diff.
|
||||
|
||||
ApprovalTests 패턴과 동형. SUT 코드 변경 협조를 최소화하기 위한 의도적 선택.
|
||||
|
||||
```
|
||||
[수동 테스트] → 입력 레코드 + 결과 파일 A (baseline)
|
||||
[회귀 시점] → 입력 리플레이 → 결과 파일 B → normalize → diff(A, B)
|
||||
```
|
||||
|
||||
**우선순위:**
|
||||
- 1순위: recorder, player, 정규화(normalizer), diff-reporter
|
||||
- 2순위: engine-bridge (엔진 상태 sidecar JSON 덤프)
|
||||
- 후순위: viewport-verifier (픽셀/이미지 비교) — golden-file로 못 잡는 케이스 보강용
|
||||
|
||||
## 3. 아키텍처 구성요소(예정)
|
||||
|
||||
| 모듈 | 책임 |
|
||||
|------|------|
|
||||
| `sut-prober` | 대상 앱 실행, UIA 트리·MEF plugin 목록 덤프 |
|
||||
| `recorder` | 입력 캡처(키/마우스/포커스) + element path + offset 동시 저장 |
|
||||
| `player` | 시나리오 재생, 비동기 작업 동기화 |
|
||||
| `normalizer` | 저장 파일 정규화(타임스탬프/GUID/경로/부동소수점/순서) |
|
||||
| `diff-reporter` | baseline vs received diff, 갈라진 지점 시각화 |
|
||||
| `engine-bridge` | HmEG 내부 상태(카메라/선택/씬그래프) sidecar JSON 노출 |
|
||||
| `test-runner` | 시나리오 묶음 실행, 리포트 |
|
||||
| `viewport-verifier` | (후순위) 3D 스크린샷 SSIM 비교 |
|
||||
|
||||
각 모듈은 독립 PoC → 통합 순서로 진행한다.
|
||||
|
||||
## 4. 기술 스택 가이드
|
||||
|
||||
- **언어**: C# / .NET (SUT와 동일 생태계, in-process probe 가능)
|
||||
- **UI 자동화**: **FlaUI** 1순위 (UIA 기반, .NET 네이티브). WinAppDriver/Appium은 fallback.
|
||||
- **저수준 입력**: Win32 SetWindowsHookEx (low-level mouse/keyboard) — element 매칭과 hybrid
|
||||
- **시나리오 포맷**: JSON 또는 YAML (git diff 친화적). 바이너리 금지.
|
||||
- **베이스라인 파일**: `*.approved.{ext}` / `*.received.{ext}` 명명 규칙
|
||||
- **이미지/큰 베이스라인**: Git LFS
|
||||
|
||||
## 5. 반드시 지킬 설계 원칙
|
||||
|
||||
1. **결정성(Determinism) 우선** — 비결정적 요소(시각·랜덤·경로·GUID·부동소수점·컬렉션 순서)는 정규화 파이프라인을 통과해야 한다. 새 필드 추가 시 정규화 규칙 동시 등록.
|
||||
2. **Element-aware 입력 캡처** — 좌표만 저장 금지. 항상 UIA element path + 상대 offset을 같이 기록해 해상도/DPI/창크기 변화에 견디게 한다.
|
||||
3. **타이밍 동기화** — 고정 sleep 금지. UIA 이벤트, property 변경, plugin 로드 완료 신호를 대기한다.
|
||||
4. **Dispatcher marshaling** — SUT 내부 probe/hook 코드는 반드시 WPF UI thread 위에서 동작.
|
||||
5. **체크포인트** — 한 시나리오 안에서 여러 번 저장→비교 가능해야 한다(이분 탐색용).
|
||||
6. **실패 아티팩트 풀세트** — 실패 시 스크린샷, UIA 트리 덤프, 엔진 상태 sidecar, 입력 로그, diff를 한 폴더에 동시 저장.
|
||||
7. **SUT 침습 최소화** — AutomationPeer/probe 부착이 필요하면 별도 어셈블리로 격리하고 SUT 팀과 합의 후 진행.
|
||||
8. **민감정보 마스킹** — 레코딩에 비밀번호/토큰 포함 금지.
|
||||
|
||||
## 6. 환경 제약
|
||||
|
||||
- **세션 0 불가**: WPF는 대화형 데스크톱 세션 필요 → CI에서 헤드리스 불가, RDP/대화형 agent 필요
|
||||
- **DPI/멀티모니터 정규화**: 테스트 머신은 고정 DPI 권장
|
||||
- **GPU 의존**: 3D 렌더 결과는 드라이버 영향 → 픽셀 비교는 항상 톨러런스 + 마스킹
|
||||
|
||||
## 7. 작업 흐름 규칙
|
||||
|
||||
### 히스토리 기록 (필수)
|
||||
모든 작업 완료 시 `docs/history/YYYY-MM-DD_{작업명}.md` 작성. 필수 항목:
|
||||
- **소요 시간**
|
||||
- **Context 사용량**
|
||||
- 관련 이슈 (#N)
|
||||
|
||||
누락 시 저장이 차단된다.
|
||||
|
||||
### 저장소
|
||||
- Origin: https://gitea.hmac.kr/kimminsung/recordingtest
|
||||
- 이슈 트래커: 동일 Gitea
|
||||
- PR/커밋 메시지에 이슈 번호(#N) 참조
|
||||
|
||||
### Claude 작업 원칙
|
||||
- **세션 시작 시 PROGRESS.md / PLAN.md 먼저 읽기** (§0 참조)
|
||||
- 코드 변경 전 관련 파일 read 필수
|
||||
- 테스트 자동화 도구 자체의 회귀를 위해 본 저장소 코드도 단위 테스트 보유 권장
|
||||
- 의존성 추가는 사전에 사용자 확인
|
||||
- 메모리 시스템(`~/.claude/projects/.../memory/`)에 프로젝트 진행 상태/전략 결정 보존
|
||||
- 작업 종료 시 PROGRESS.md / PLAN.md 업데이트 + 히스토리 파일 작성 (3종 세트)
|
||||
|
||||
## 8. 디렉터리 구조 (예정 — 셋업 시 확정)
|
||||
|
||||
```
|
||||
recordingtest/
|
||||
├── src/
|
||||
│ ├── Recordingtest.Recorder/
|
||||
│ ├── Recordingtest.Player/
|
||||
│ ├── Recordingtest.Normalizer/
|
||||
│ ├── Recordingtest.DiffReporter/
|
||||
│ ├── Recordingtest.EngineBridge/
|
||||
│ ├── Recordingtest.SutProber/
|
||||
│ └── Recordingtest.Runner/
|
||||
├── tests/
|
||||
├── scenarios/ # 시나리오 JSON/YAML
|
||||
├── baselines/ # *.approved.* (LFS 후보)
|
||||
├── docs/
|
||||
│ ├── history/ # 작업 히스토리
|
||||
│ ├── contracts/ # Sprint Contracts + evaluations
|
||||
│ └── sut-catalog/ # sut-explorer 산출물
|
||||
├── PROGRESS.md
|
||||
├── PLAN.md
|
||||
└── CLAUDE.md
|
||||
```
|
||||
|
||||
## 9. 비목표 (Out of Scope)
|
||||
|
||||
- SUT 자체의 기능 변경/버그 수정
|
||||
- 일반 웹/모바일 자동화
|
||||
- 부하·성능 테스트
|
||||
- 단위 테스트 프레임워크 대체
|
||||
|
||||
## 10. 결정 로그 위치
|
||||
|
||||
주요 기술 결정과 그 근거는 `docs/history/`와 Claude 메모리(`project_recordingtest_*`)에 분산 저장된다. 새 결정 시 반드시 둘 다 갱신한다.
|
||||
10
Directory.Build.props
Normal file
10
Directory.Build.props
Normal file
@@ -0,0 +1,10 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<RootNamespace>Recordingtest.$(MSBuildProjectName.Replace('Recordingtest.',''))</RootNamespace>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
45
PLAN.md
Normal file
45
PLAN.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# PLAN.md — recordingtest
|
||||
|
||||
> 다음에 할 일의 *우선순위 큐*. 에이전트는 상단부터 집어든다.
|
||||
> 비자명한 항목은 `/contract <name>` → `docs/contracts/<name>.md` 로 Sprint Contract 먼저 작성.
|
||||
|
||||
## P0 — 지금 바로
|
||||
|
||||
1. **sut-prober PoC 구현** — Sprint Contract: [docs/contracts/sut-prober.md](docs/contracts/sut-prober.md)
|
||||
- Generator: 일반 세션
|
||||
- Evaluator: `/evaluate sut-prober`
|
||||
- 의존: 없음 (독립 실행)
|
||||
2. **PROGRESS.md / PLAN.md / CLAUDE.md 훅 동작 검증** — SessionStart/Stop/Guard 3개 shell 스크립트를 실제로 트리거시켜 확인
|
||||
- 의존: jq 설치 여부 확인
|
||||
|
||||
## P1 — PoC 1단계
|
||||
|
||||
3. **normalizer PoC** — Sprint Contract: [docs/contracts/normalizer.md](docs/contracts/normalizer.md)
|
||||
- 의존: sut-prober의 Json 카탈로그
|
||||
4. **recorder PoC (element-aware)** — Sprint Contract: [docs/contracts/recorder.md](docs/contracts/recorder.md)
|
||||
- 의존: FlaUI 패키지 승인
|
||||
5. **player PoC** — Sprint Contract: [docs/contracts/player.md](docs/contracts/player.md)
|
||||
- 의존: recorder 산출물 포맷 확정
|
||||
6. **diff-reporter PoC** — Sprint Contract: [docs/contracts/diff-reporter.md](docs/contracts/diff-reporter.md)
|
||||
- 의존: normalizer 규칙 1개 이상
|
||||
|
||||
## P2 — 통합
|
||||
|
||||
7. **test-runner** — 시나리오 일괄 실행 + 실패 triage
|
||||
- 의존: recorder + player + normalizer + diff-reporter 전부
|
||||
8. **engine-bridge 탐색** — HmEG PDB 리플렉션으로 카메라/선택 상태 접근 가능 여부 확인
|
||||
- Sprint Contract 필요
|
||||
9. **AutomationPeer PR 전략 PoC** — SUT fork에 AI로 AutomationPeer 자동 부착 샘플
|
||||
|
||||
## P3 — 상시
|
||||
|
||||
- **Scaffolding review** (PoC 3개마다)
|
||||
- **.claude/ 비계 감사** — 기사 원칙: 불필요한 비계 제거
|
||||
- **history 파일 누락 체크** — Stop hook이 경고
|
||||
|
||||
## 규칙
|
||||
|
||||
- 항목을 집을 때 PROGRESS.md의 "In progress"에 기록하고 본인(에이전트명/세션) 명시.
|
||||
- 비자명한 P0/P1/P2 항목은 반드시 Sprint Contract 먼저.
|
||||
- Generator와 Evaluator는 같은 세션이 겸하지 않는다.
|
||||
- 완료 시: evaluator pass → PROGRESS.md Done 이동 → PLAN.md 제거 → history 작성 → `/handoff`.
|
||||
47
PROGRESS.md
47
PROGRESS.md
@@ -1,17 +1,44 @@
|
||||
# PROGRESS.md — recordingtest
|
||||
|
||||
## 현재 상태 요약
|
||||
> 에이전트 간 공유 메모리. 세션 시작 시 PLAN.md와 함께 반드시 읽을 것.
|
||||
> 작업 상태만 기록한다. 결정의 근거는 `docs/history/`, 전략은 `CLAUDE.md` / 메모리 참조.
|
||||
|
||||
- 프로젝트: WPF Application User Input Regression Test
|
||||
- 저장소: https://gitea.hmac.kr/kimminsung/recordingtest
|
||||
- 히스토리 경로: docs/history
|
||||
**저장소**: https://gitea.hmac.kr/kimminsung/recordingtest
|
||||
**SUT**: `EG-BIM Modeler/` (git 제외, 로컬 동봉)
|
||||
**현재 이슈**: #2 컨텍스트 엔지니어링 & AI 개발환경 셋업
|
||||
|
||||
## 작업 이력
|
||||
---
|
||||
|
||||
| 날짜 | 작업 | 이슈 | 파일 |
|
||||
|------|------|------|------|
|
||||
## Done
|
||||
|
||||
## 참고
|
||||
| 날짜 | 항목 | 산출물 |
|
||||
|------|------|--------|
|
||||
| 2026-04-07 | 히스토리 훅 설정 | `docs/history/2026-04-07_히스토리-훅-설정.md` |
|
||||
| 2026-04-07 | 이슈 #2 리서치 & 에이전트 분해 | 메모리 `project_recordingtest_plan.md` |
|
||||
| 2026-04-07 | 구현 고려사항 정리 (8개 카테고리) | 히스토리 |
|
||||
| 2026-04-07 | Golden-file 회귀 전략 채택 | 메모리 `project_recordingtest_strategy.md` |
|
||||
| 2026-04-07 | CLAUDE.md 초안 + §0 협업 규칙 | `CLAUDE.md` |
|
||||
| 2026-04-07 | SUT(EG-BIM Modeler) 정적 분석 | 메모리 `project_sut_egbim_modeler.md` |
|
||||
| 2026-04-07 | `.claude/` agents·commands·skills·hooks 셋업 | `.claude/` |
|
||||
| 2026-04-07 | Harness design 채택 (Planner/Generator/Evaluator) | CLAUDE.md §0.1, `.claude/agents/planner.md`, `evaluator.md` |
|
||||
| 2026-04-07 | SUT 폴더 `.gitignore` | `.gitignore` |
|
||||
| 2026-04-07 | 초기 Sprint Contracts 5건 작성 | `docs/contracts/*.md` |
|
||||
| 2026-04-07 | SUT 카탈로그 v0 (정적) | `docs/sut-catalog/catalog.md`, `plugins.md` |
|
||||
| 2026-04-07 | 솔루션 스캐폴드(sut-prober PoC 타깃) | `recordingtest.sln`, `src/Recordingtest.SutProber/` |
|
||||
|
||||
- 히스토리 파일은 `docs/history/YYYY-MM-DD_{작업명}.md` 형식으로 저장
|
||||
- 필수 항목: **소요 시간**, **Context 사용량**, **이슈** (#N)
|
||||
## In progress
|
||||
|
||||
_(없음 — 다음 작업은 PLAN.md 상단에서 고른다)_
|
||||
|
||||
## Blocked
|
||||
|
||||
_(없음)_
|
||||
|
||||
## Recent regression runs
|
||||
|
||||
_(러너 미구현 — /regress 사용 불가)_
|
||||
|
||||
## Scaffolding review
|
||||
|
||||
- 마지막 감사: 2026-04-07 (초기 셋업)
|
||||
- 다음 감사 권장: PoC 3개 완료 후
|
||||
|
||||
44
docs/contracts/diff-reporter.md
Normal file
44
docs/contracts/diff-reporter.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Sprint Contract — diff-reporter
|
||||
|
||||
**Owner:** Generator
|
||||
**Depends on:** normalizer (정규화된 입력)
|
||||
**Issue:** #2
|
||||
|
||||
## Goal
|
||||
|
||||
`*.approved`와 `*.received` 쌍을 받아 의미 있는 diff를 생성하고, 실패 지점을 시각화한다. `diff-triager` 서브에이전트가 읽을 수 있는 구조화 출력도 제공.
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- [ ] `Recordingtest.DiffReporter` 라이브러리 + CLI
|
||||
- [ ] 입력: `--approved <path> --received <path> --out <dir>`
|
||||
- [ ] JSON/텍스트 파일은 라인·객체 단위 의미 diff, 바이너리는 hex 요약 diff
|
||||
- [ ] 출력: `diff.json`(구조화), `diff.md`(사람용), `diff.html`(옵션, side-by-side)
|
||||
- [ ] `diff.json` 스키마: `{ file, hunks[], summary: { added, removed, changed } }`
|
||||
- [ ] 동일 파일 비교 시 "identical"로 단정, exit code 0
|
||||
- [ ] 차이 존재 시 exit code 1 + 요약을 stdout 1줄
|
||||
- [ ] diff-triager 에이전트가 `diff.json`만 읽어 bucket 분류 가능 (통합 테스트 1개)
|
||||
|
||||
## Interfaces
|
||||
|
||||
- **Inputs:** 두 파일 경로
|
||||
- **Outputs:** `diff.json`, `diff.md`, (옵션) `diff.html`
|
||||
- **Side effects:** 없음
|
||||
|
||||
## Out of scope
|
||||
|
||||
- 정규화 자체 (normalizer)
|
||||
- triage 분류 로직 (diff-triager 에이전트)
|
||||
- 자동 approve (`/approve` 커맨드)
|
||||
|
||||
## Evaluation plan
|
||||
|
||||
1. 동일 파일 2개 → identical 판정
|
||||
2. 1필드만 바뀐 JSON → `diff.json.hunks.length == 1`
|
||||
3. 바이너리 파일 diff → hex summary 출력
|
||||
4. diff-triager에 통합: normalization gap 케이스에서 bucket=`normalization_gap` 분류 확인
|
||||
|
||||
## Risks
|
||||
|
||||
- 큰 파일(수십 MB 모델)에서 성능 — 스트리밍 diff 필요
|
||||
- JSON 스키마 변경 시 triager 하위 호환성
|
||||
43
docs/contracts/normalizer.md
Normal file
43
docs/contracts/normalizer.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Sprint Contract — normalizer
|
||||
|
||||
**Owner:** Generator
|
||||
**Depends on:** sut-prober (Json 카탈로그)
|
||||
**Issue:** #2
|
||||
|
||||
## Goal
|
||||
|
||||
SUT 저장 파일(Json 설정 + 모델 파일)의 비결정적 필드를 정규화해 golden-file 비교를 결정적으로 만든다. 규칙은 **프로파일 단위로 버전 관리**되고 단위 테스트를 동반한다.
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- [ ] `Recordingtest.Normalizer` 라이브러리가 `Normalize(input, profile)` API 제공
|
||||
- [ ] 기본 프로파일 `default` 포함 규칙 최소 5개: 타임스탬프 마스킹, GUID 치환, 절대 경로 → `<REPO>`/`<USER>`, 부동소수점 epsilon(1e-6), JSON 객체 키 정렬
|
||||
- [ ] 프로파일은 `profiles/*.yaml`로 선언, 코드 변경 없이 규칙 추가 가능
|
||||
- [ ] 각 규칙마다 `tests/Normalizer.Tests/` 아래 before/after 샘플 1쌍 이상
|
||||
- [ ] 동일 입력으로 두 번 정규화해도 같은 바이트 출력 (idempotent)
|
||||
- [ ] 정규화 로그(sidecar) `normalization.log` 생성 — 어떤 규칙이 몇 번 적용됐는지 기록
|
||||
- [ ] `json-configs.json` 카탈로그의 "suspected fields" 전수를 default 프로파일로 덮는다
|
||||
|
||||
## Interfaces / contracts
|
||||
|
||||
- **Inputs:** 파일 경로 + 프로파일 이름
|
||||
- **Outputs:** 정규화된 바이트 스트림 + sidecar 로그
|
||||
- **Side effects:** sidecar 로그 쓰기만
|
||||
|
||||
## Out of scope
|
||||
|
||||
- 바이너리 `.hme`/`.egm` 파서 (별도 contract)
|
||||
- SUT 내부 호출
|
||||
- 정규화 규칙 자동 학습
|
||||
|
||||
## Evaluation plan
|
||||
|
||||
1. `dotnet test tests/Normalizer.Tests` 전부 pass
|
||||
2. `json-configs.json`의 모든 suspected field가 default 프로파일 규칙에 커버됨 (매핑 테이블 검증)
|
||||
3. 동일 입력 2회 정규화 바이트 diff 없음
|
||||
4. 샘플 `Settings.json` 정규화 전/후 비교에서 타임스탬프·경로가 마스킹됨
|
||||
|
||||
## Risks
|
||||
|
||||
- epsilon 선택이 SUT 실제 정밀도와 충돌 가능 → 구성 가능해야 함.
|
||||
- 컬렉션 순서 정렬이 SUT 의미를 바꿀 수 있음 → 정렬 대상은 명시적 화이트리스트.
|
||||
44
docs/contracts/player.md
Normal file
44
docs/contracts/player.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# Sprint Contract — player
|
||||
|
||||
**Owner:** Generator
|
||||
**Depends on:** recorder 스키마 확정
|
||||
**Issue:** #2
|
||||
|
||||
## Goal
|
||||
|
||||
recorder가 생성한 시나리오 yaml을 SUT에 재생해 결과 저장 파일을 생성한다. 타이밍은 고정 sleep 금지, UIA/이벤트 기반 동기화.
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- [ ] `Recordingtest.Player` 콘솔이 `--scenario <path> [--output-dir <path>]` 받음
|
||||
- [ ] 각 step 재생 전 `wait_for`(uia 이벤트/엘리먼트 property) 대기 구현
|
||||
- [ ] `target.uia_path`로 엘리먼트 재탐색 후 `offset_norm`으로 좌표 계산해 입력 전달
|
||||
- [ ] 재생 중 예외 발생 시 시나리오 스텝 index + 엘리먼트 탐색 실패 이유를 아티팩트로 기록
|
||||
- [ ] 체크포인트 step에서 현재 저장 파일을 `<output-dir>/checkpoint-<n>.*`로 복사
|
||||
- [ ] 재생 완료 시 exit code 0, 실패 시 non-zero + 아티팩트 경로 출력
|
||||
- [ ] 동일 시나리오 10회 재생 시 9회 이상 성공 (flaky 허용 상한)
|
||||
|
||||
## Interfaces
|
||||
|
||||
- **Inputs:** 시나리오 yaml, 아티팩트 출력 디렉터리
|
||||
- **Outputs:** checkpoint 저장 파일, 실패 아티팩트(스크린샷, UIA 덤프, 로그)
|
||||
- **Side effects:** SUT 프로세스 실행/종료
|
||||
|
||||
## Out of scope
|
||||
|
||||
- 정규화 (normalizer의 몫)
|
||||
- diff (diff-reporter의 몫)
|
||||
- 리포트 집계 (test-runner의 몫)
|
||||
|
||||
## Evaluation plan
|
||||
|
||||
1. recorder로 만든 간단 시나리오(Box 생성 저장)를 10회 재생 → 9회 이상 완료 파일 생성
|
||||
2. 의도적으로 잘못된 `uia_path`를 넣어 실패 → 아티팩트에 탐색 실패 이유 기록 확인
|
||||
3. 체크포인트 2개 시나리오 재생 → 각 checkpoint 파일 존재 확인
|
||||
4. 고정 sleep 호출 grep으로 0건 확인
|
||||
|
||||
## Risks
|
||||
|
||||
- plugin 로드 지연 → 첫 step 전 "plugin ready" 신호 필요
|
||||
- 시작 상태 초기화 (임시 프로젝트 파일 삭제) 미비 시 누적 오염
|
||||
- 재생 중 모달 대화상자 출현 시 대응 정책 필요
|
||||
47
docs/contracts/recorder.md
Normal file
47
docs/contracts/recorder.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Sprint Contract — recorder
|
||||
|
||||
**Owner:** Generator
|
||||
**Depends on:** FlaUI 승인, sut-prober(UIA 힌트용 카탈로그)
|
||||
**Issue:** #2
|
||||
|
||||
## Goal
|
||||
|
||||
사용자가 EG-BIM Modeler에서 수동 테스트하는 동안 입력(키·마우스·창 이벤트)을 캡처해 element-aware 시나리오 파일을 생성한다.
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- [ ] `Recordingtest.Recorder` 콘솔 실행 시 SUT에 attach하고 입력 캡처 시작
|
||||
- [ ] 캡처되는 이벤트: 키 다운/업, 마우스 클릭/드래그/휠, 포커스 변경
|
||||
- [ ] 각 이벤트는 `{ ts, kind, uia_path, offset_norm, raw_coord, value }` 형식으로 기록
|
||||
- [ ] 3D 뷰포트(SharpDX surface) 클릭 시 uia_path는 호스팅 엘리먼트, `offset_norm`은 `[0..1]` 정규화 좌표
|
||||
- [ ] 출력: `scenarios/<name>.yaml` 스키마 준수 (`scenario-author` 규격 일치)
|
||||
- [ ] 비밀번호/토큰 마스킹 규칙 1개 이상 (예: focus된 element가 `PasswordBox`이면 `value`를 `<MASKED>`)
|
||||
- [ ] 캡처 중 SUT 성능에 눈에 띄는 영향 없음 (60 FPS 유지; 경고만)
|
||||
- [ ] 캡처 종료 시 요약(이벤트 수, 소요 시간, uia_path 미결 건수)
|
||||
|
||||
## Interfaces
|
||||
|
||||
- **Inputs:** `--output scenarios/<name>.yaml`, `--attach <pid or window title>`
|
||||
- **Outputs:** 시나리오 yaml
|
||||
- **Side effects:** 전역 Win32 hook 등록/해제
|
||||
|
||||
## Out of scope
|
||||
|
||||
- 리플레이 (player의 몫)
|
||||
- 3D 엔진 상태 캡처 (engine-bridge 사용 예정)
|
||||
- 자동 이름 추정 / 자연어 변환 (scenario-author)
|
||||
|
||||
## Evaluation plan
|
||||
|
||||
1. SUT를 수동 실행 후 recorder attach → Box 명령 버튼 클릭 → Box 생성 드래그 → 저장
|
||||
2. 출력 yaml 파싱 + 스키마 검증
|
||||
3. 클릭 이벤트의 `uia_path`가 `Button[@Name*='Box']` 형태 포함 확인
|
||||
4. 드래그 이벤트의 `offset_norm`이 `[0..1]` 범위 확인
|
||||
5. `PasswordBox`에 더미 입력 → `<MASKED>` 기록 확인
|
||||
6. 종료 후 hook 해제 확인 (재실행 시 double-hook 안 남음)
|
||||
|
||||
## Risks
|
||||
|
||||
- IME(한글) 조합 키 이벤트 손실 → 클립보드 우회 필요 가능
|
||||
- 해상도/DPI 변경 시 `raw_coord`는 유효성 상실 → `offset_norm` 우선 사용
|
||||
- Win32 hook은 UI thread 블록 가능 → 별도 스레드 + 큐 필수
|
||||
48
docs/contracts/sut-prober.md
Normal file
48
docs/contracts/sut-prober.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# Sprint Contract — sut-prober
|
||||
|
||||
**Owner:** Generator (일반 세션)
|
||||
**Depends on:** 없음
|
||||
**Issue:** #2
|
||||
|
||||
## Goal
|
||||
|
||||
EG-BIM Modeler SUT의 **정적** 구조(플러그인·설정·어셈블리)를 덤프해 `docs/sut-catalog/`에 베이스라인 가능한 카탈로그를 생성한다. SUT는 실행하지 않는다.
|
||||
|
||||
## Definition of Done
|
||||
|
||||
- [ ] `dotnet run --project src/Recordingtest.SutProber` 실행으로 카탈로그 3종이 생성된다: `plugins.json`, `json-configs.json`, `assemblies.json`
|
||||
- [ ] `plugins.json`은 `EG-BIM Modeler/Plugins/Eg*Plugin/` 187개(±) 전부를 담고, 각 항목은 `{ name, path, dlls[], size_bytes }` 형식
|
||||
- [ ] `json-configs.json`은 `EG-BIM Modeler/Json/*.json` 각 파일별 `{ name, top_level_keys[], suspected_nondeterministic_fields[] }`
|
||||
- [ ] `assemblies.json`은 `HmEG*.dll`, `Editor*.dll`, `HmGeometry*.dll`의 `{ name, size, has_pdb }`
|
||||
- [ ] 출력은 **정렬되어 있어** 두 번 실행해도 동일 (decimal/텍스트 diff 없음)
|
||||
- [ ] `EG-BIM Modeler/` 폴더에 대한 쓰기 접근이 없다 (코드 리뷰로 확인)
|
||||
- [ ] 경로는 repo root 상대 경로로 기록(절대 경로 금지)
|
||||
|
||||
## Interfaces / contracts
|
||||
|
||||
- **Inputs:** `EG-BIM Modeler/` (read-only), CLI 인자 `--sut <path>` (기본 `EG-BIM Modeler`)
|
||||
- **Outputs:** `docs/sut-catalog/plugins.json`, `docs/sut-catalog/json-configs.json`, `docs/sut-catalog/assemblies.json`
|
||||
- **Side effects:** 없음 (로그 제외)
|
||||
|
||||
## Out of scope
|
||||
|
||||
- SUT 실행 / 리플렉션 / 동적 분석
|
||||
- PDB 파싱 (engine-bridge의 몫)
|
||||
- UIA 트리 덤프 (recorder 또는 별도 PoC의 몫)
|
||||
|
||||
## Evaluation plan
|
||||
|
||||
evaluator는 다음을 수행:
|
||||
|
||||
1. `dotnet build` 성공
|
||||
2. `dotnet run --project src/Recordingtest.SutProber -- --sut "EG-BIM Modeler"` 실행, exit code 0 확인
|
||||
3. 세 카탈로그 파일 존재 확인 + JSON 파싱 유효성
|
||||
4. `plugins.json` 엔트리 수 ≥ 180
|
||||
5. 두 번째 실행 후 세 파일 `git diff` 결과가 비어있음을 확인 (결정성)
|
||||
6. `grep -r "EG-BIM Modeler" src/Recordingtest.SutProber/` 결과에 `File.Write|File.Delete|File.Create` 호출 없음 확인
|
||||
|
||||
## Risks / open questions
|
||||
|
||||
- .NET 버전 합의 필요 (`net8.0` 제안). SUT와 다를 수 있으나 prober는 독립 프로세스이므로 무관.
|
||||
- 플러그인 폴더 구조가 일정한지 샘플 확인 필요.
|
||||
- 경로 구분자(Windows `\`) 정규화 정책 — 출력에서 `/`로 통일 권장.
|
||||
19
docs/history/2026-04-07_이슈2-AI-AutomationPeer-부착옵션.md
Normal file
19
docs/history/2026-04-07_이슈2-AI-AutomationPeer-부착옵션.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# 2026-04-07 이슈 #2 — AI 기반 AutomationPeer 부착 옵션 기록
|
||||
|
||||
- **이슈**: kimminsung/recordingtest#2
|
||||
- **소요 시간**: ~3분
|
||||
- **Context 사용량**: ~70k tokens
|
||||
|
||||
## 결정/기록
|
||||
|
||||
SUT 커스텀 컨트롤에 AutomationPeer를 부착하는 작업을 AI로 자동화하는 보조 전략을 메모리에 남김. Golden-file이 1차 전략이지만, element-aware 레코딩 보강이 필요해질 때 꺼낼 카드.
|
||||
|
||||
### 핵심
|
||||
- 패턴 정형화로 80% AI 자동 생성 가능
|
||||
- ControlType/패턴 매핑과 `AutomationProperties.Name`은 사람 리뷰 필요
|
||||
- SUT 저장소엔 별도 브랜치/PR로 제안, 직접 커밋 금지
|
||||
- recordingtest 저장소에는 가이드/스크립트만, SUT 코드 변경은 fork/PR로 분리
|
||||
|
||||
## 산출물
|
||||
- 메모리: `project_recordingtest_automationpeer_ai.md`
|
||||
- MEMORY.md 인덱스 갱신
|
||||
21
docs/history/2026-04-07_이슈2-CLAUDE-md-progress-plan-규칙.md
Normal file
21
docs/history/2026-04-07_이슈2-CLAUDE-md-progress-plan-규칙.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 2026-04-07 이슈 #2 — CLAUDE.md에 PROGRESS/PLAN 협업 규칙 추가
|
||||
|
||||
- **이슈**: kimminsung/recordingtest#2
|
||||
- **소요 시간**: ~3분
|
||||
- **Context 사용량**: ~76k tokens
|
||||
|
||||
## 변경 내용
|
||||
|
||||
`CLAUDE.md` 에 §0 "세션 시작 시 필독" 섹션 신설. 여러 에이전트 협업을 위한 공유 메모리로 `PROGRESS.md`(끝난 일)와 `PLAN.md`(할 일) 운영 규칙 명시.
|
||||
|
||||
### 핵심 규칙
|
||||
- 세션 시작: PROGRESS.md + PLAN.md 먼저 read
|
||||
- 시작 시: PROGRESS.md에 in progress 표시
|
||||
- 종료 시: PROGRESS.md(done 이동) + PLAN.md(완료 제거/다음 단계) + history 파일 (3종 세트)
|
||||
- 충돌 시 최신 커밋 기준 머지, 모호하면 사용자에 질문
|
||||
|
||||
§7 Claude 작업 원칙에도 같은 내용 반영.
|
||||
|
||||
## 다음 단계
|
||||
|
||||
PROGRESS.md / PLAN.md 실제 파일은 셋업 단계에서 작성 예정 (현재는 CLAUDE.md 규칙만 정의).
|
||||
26
docs/history/2026-04-07_이슈2-CLAUDE-md-작성.md
Normal file
26
docs/history/2026-04-07_이슈2-CLAUDE-md-작성.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# 2026-04-07 이슈 #2 — CLAUDE.md 작성
|
||||
|
||||
- **이슈**: kimminsung/recordingtest#2
|
||||
- **소요 시간**: ~5분
|
||||
- **Context 사용량**: ~63k tokens
|
||||
|
||||
## 작업 내용
|
||||
|
||||
프로젝트 셋업 전, recordingtest(WPF 입력 회귀 자동화 도구)의 운영 지침인 `CLAUDE.md`를 루트에 작성. 셋업은 미실시.
|
||||
|
||||
## 포함 섹션
|
||||
|
||||
1. 프로젝트 정체성 / SUT 개요
|
||||
2. 핵심 전략 — Golden-file 회귀
|
||||
3. 아키텍처 구성요소(8개 모듈) 우선순위
|
||||
4. 기술 스택 가이드 (FlaUI, Win32 hook, JSON/YAML, LFS)
|
||||
5. 설계 원칙 8개 (결정성, element-aware, 타이밍, marshaling, 체크포인트, 아티팩트, 침습 최소화, 마스킹)
|
||||
6. 환경 제약 (세션0, DPI, GPU)
|
||||
7. 작업 흐름 규칙 (히스토리 필수)
|
||||
8. 디렉터리 구조(예정)
|
||||
9. 비목표
|
||||
10. 결정 로그 위치
|
||||
|
||||
## 다음 단계
|
||||
|
||||
사용자 승인 후 실제 솔루션/프로젝트 셋업 진행.
|
||||
28
docs/history/2026-04-07_이슈2-SUT-EGBIM-분석.md
Normal file
28
docs/history/2026-04-07_이슈2-SUT-EGBIM-분석.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# 2026-04-07 이슈 #2 — SUT (EG-BIM Modeler) 폴더 분석
|
||||
|
||||
- **이슈**: kimminsync/recordingtest#2
|
||||
- **소요 시간**: ~5분
|
||||
- **Context 사용량**: ~82k tokens
|
||||
|
||||
## 분석 대상
|
||||
|
||||
`d:/MYCLAUDE_PROJECT/recordingtest/EG-BIM Modeler/` — SUT 실행파일 일습.
|
||||
|
||||
## 핵심 발견
|
||||
|
||||
- WPF + .NET, 3D 엔진 `HmEG.dll`, 렌더 SharpDX D3D11
|
||||
- MEF plugin `Plugins/Eg*Plugin/` 수십~100개+
|
||||
- **PDB 동봉** → 리플렉션/엔진 침습 비용 ↓
|
||||
- `Json/` 폴더에 텍스트 설정 다수 — golden-file 1차 타깃으로 적합
|
||||
- 자체 저장 포맷 `.hme`/`.egm` — 파서·정규화 규칙 필요
|
||||
- 3D 뷰포트는 D3D surface로 UIA 사각지대 확정
|
||||
|
||||
## 전략 반영
|
||||
|
||||
- sut-prober 첫 PoC: `Plugins/` 스캔 + `Json/` 스냅샷
|
||||
- engine-bridge: HmEG 리플렉션으로 카메라/씬그래프 접근
|
||||
- 모델 포맷 파서 모듈 분리
|
||||
|
||||
## 산출물
|
||||
- 메모리: `project_sut_egbim_modeler.md`
|
||||
- MEMORY.md 인덱스 갱신
|
||||
@@ -0,0 +1,68 @@
|
||||
# 2026-04-07 이슈 #2 — .claude/ 에이전트·커맨드·스킬·훅 셋업
|
||||
|
||||
- **이슈**: kimminsung/recordingtest#2
|
||||
- **소요 시간**: ~15분
|
||||
- **Context 사용량**: ~95k tokens
|
||||
|
||||
## 작업 내용
|
||||
|
||||
Claude Code 공식 포맷 조사(claude-code-guide 에이전트) 후, recordingtest 운영에 필요한 커스터마이즈를 `.claude/` 하위에 생성.
|
||||
|
||||
### Subagents — `.claude/agents/*.md`
|
||||
- **sut-explorer** — EG-BIM Modeler 정적 분석(플러그인/Json/어셈블리). SUT 실행 금지.
|
||||
- **diff-triager** — 회귀 실패 분류(real bug / intentional / normalization gap / env drift / flaky).
|
||||
- **scenario-author** — 자연어 → 시나리오 YAML 생성.
|
||||
|
||||
### Slash commands — `.claude/commands/*.md`
|
||||
- **/sut-probe** — sut-explorer 위임, 카탈로그 생성.
|
||||
- **/regress** — 회귀 실행 + 실패 자동 triage.
|
||||
- **/approve** — received → approved 승격, 사유 강제.
|
||||
- **/handoff** — PROGRESS/PLAN/history 3종 세트 갱신.
|
||||
- **/progress** — PROGRESS.md/PLAN.md 요약 출력.
|
||||
|
||||
### Skills — `.claude/skills/<name>/SKILL.md`
|
||||
- **golden-file-normalizer** — 정규화 규칙 카테고리/원칙/저작 가이드.
|
||||
- **flaui-cookbook** — FlaUI 런칭/대기/엘리먼트 캡처/3D 뷰포트 폴백/함정.
|
||||
|
||||
### Hooks — `.claude/settings.json` + `.claude/hooks/*.sh`
|
||||
- **SessionStart** → `session-start-progress.sh`: PROGRESS.md/PLAN.md 자동 주입.
|
||||
- **PreToolUse(Bash)** → `guard-sut-launch.sh`: EG-BIM Modeler.exe 실행 경고.
|
||||
- **PreToolUse(Edit|Write)** → `guard-sut-folder.sh`: SUT 폴더 수정 차단(exit 2).
|
||||
- **Stop** → `stop-handoff-reminder.sh`: 오늘 history 파일/PROGRESS/PLAN 누락 시 /handoff 권장.
|
||||
|
||||
## 원칙
|
||||
|
||||
- 포맷은 claude-code-guide 조사 결과를 따름(frontmatter: name/description/allowed-tools/tools/model).
|
||||
- SUT 폴더는 **read-only** — 훅으로 강제.
|
||||
- 실제 모듈(src/*)은 아직 미생성 — /regress 등은 건드릴 파일이 없으면 early-exit.
|
||||
|
||||
## 산출물
|
||||
|
||||
```
|
||||
.claude/
|
||||
├── agents/
|
||||
│ ├── sut-explorer.md
|
||||
│ ├── diff-triager.md
|
||||
│ └── scenario-author.md
|
||||
├── commands/
|
||||
│ ├── sut-probe.md
|
||||
│ ├── regress.md
|
||||
│ ├── approve.md
|
||||
│ ├── handoff.md
|
||||
│ └── progress.md
|
||||
├── skills/
|
||||
│ ├── golden-file-normalizer/SKILL.md
|
||||
│ └── flaui-cookbook/SKILL.md
|
||||
├── hooks/
|
||||
│ ├── session-start-progress.sh
|
||||
│ ├── guard-sut-launch.sh
|
||||
│ ├── guard-sut-folder.sh
|
||||
│ └── stop-handoff-reminder.sh
|
||||
└── settings.json (hooks 섹션 추가)
|
||||
```
|
||||
|
||||
## 다음 단계
|
||||
|
||||
- PROGRESS.md / PLAN.md 실제 파일 작성 (셋업 단계에서)
|
||||
- src/ 모듈 scaffold
|
||||
- 훅 bash 스크립트 실제 동작 검증
|
||||
34
docs/history/2026-04-07_이슈2-golden-file-전략-채택.md
Normal file
34
docs/history/2026-04-07_이슈2-golden-file-전략-채택.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# 2026-04-07 이슈 #2 — Golden-file 회귀 전략 채택
|
||||
|
||||
- **이슈**: kimminsung/recordingtest#2
|
||||
- **소요 시간**: ~5분
|
||||
- **Context 사용량**: ~58k tokens
|
||||
|
||||
## 결정
|
||||
|
||||
사용자 제안 채택: **수동 테스트 입력 레코딩 → 리플레이 후 저장 파일을 베이스라인과 diff** 하는 golden-file 회귀 방식을 1차 검증 수단으로 삼는다. (ApprovalTests 패턴과 동형)
|
||||
|
||||
## 근거
|
||||
|
||||
- SUT(WPF+MEF+HmEG)에 AutomationPeer/probe 부착 협조 비용 회피
|
||||
- 결과물 파일 자체가 검증 기준 → 직관적, 사용자가 시나리오 작성 가능
|
||||
- 픽셀 비교 의존도 ↓ → viewport-verifier 우선순위 하향
|
||||
|
||||
## 성립 조건 / 함정
|
||||
|
||||
1. **결정성**: 저장 파일의 타임스탬프/GUID/경로/부동소수점/순서 → 정규화 파이프라인 필수
|
||||
2. **입력 재현 정확도**: 타이밍·포커스·IME·3D 드래그 픽셀 증폭
|
||||
3. **비교 범위**: 저장 파일만 vs +중간 체크포인트 vs +엔진 상태 sidecar
|
||||
4. **베이스라인 관리**: `*.approved`/`*.received` 워크플로, 큰 파일 LFS
|
||||
5. **진단**: 단계별 스냅샷으로 갈라진 지점 이분 탐색
|
||||
|
||||
## 모듈 우선순위 재조정
|
||||
|
||||
핵심: **recorder + player + 정규화 모듈 + diff-reporter**
|
||||
보조: engine-bridge(sidecar JSON 덤프 용도)
|
||||
후순위: viewport-verifier(픽셀 비교)
|
||||
|
||||
## 다음 단계
|
||||
|
||||
- 저장 파일 포맷 조사 (텍스트/바이너리, 정규화 가능 영역)
|
||||
- recorder PoC 범위 정의
|
||||
@@ -0,0 +1,41 @@
|
||||
# 2026-04-07 이슈 #2 — Planner/Evaluator 에이전트 & Sprint Contract 도입
|
||||
|
||||
- **이슈**: kimminsung/recordingtest#2
|
||||
- **소요 시간**: ~10분
|
||||
- **Context 사용량**: ~108k tokens
|
||||
|
||||
## 배경
|
||||
|
||||
Anthropic "Harness Design for Long-Running Agent Applications" 기사를 읽고, 핵심 원칙을 recordingtest에 반영.
|
||||
|
||||
## 추가된 것
|
||||
|
||||
### 에이전트
|
||||
- `.claude/agents/planner.md` — 요청 → Sprint Contract + PLAN.md 엔트리. 구현 금지.
|
||||
- `.claude/agents/evaluator.md` — 완료된 모듈을 Sprint Contract 기준으로 독립 채점. 자기 작업 평가 금지.
|
||||
|
||||
### 커맨드
|
||||
- `.claude/commands/contract.md` — `/contract <name>`
|
||||
- `.claude/commands/evaluate.md` — `/evaluate <name>`
|
||||
|
||||
### CLAUDE.md
|
||||
- §0.1 "작업 사이클 — Planner → Generator → Evaluator" 섹션 신설
|
||||
- Sprint Contract / DoD 객관성 / 독립 평가 원칙 명시
|
||||
- 컨텍스트 위생: compaction 대신 reset, Stop hook 경고 준수, scaffolding audit 상시 항목
|
||||
- 디렉터리 구조에 `docs/contracts/`, `PROGRESS.md`, `PLAN.md` 추가
|
||||
|
||||
### 메모리
|
||||
- `project_recordingtest_harness_design.md` 신규
|
||||
- MEMORY.md 인덱스 갱신
|
||||
|
||||
## 원칙 요약
|
||||
|
||||
1. 생성자와 평가자는 같은 에이전트가 겸하지 않는다.
|
||||
2. DoD는 객관적으로 검증 가능해야 한다.
|
||||
3. 컨텍스트가 차면 요약하지 말고 파일에 쏟고 리셋.
|
||||
4. evaluator fail → PROGRESS.md에 done으로 넘기지 않는다.
|
||||
5. `.claude/` 비계는 주기적으로 감사·축소.
|
||||
|
||||
## 다음 단계
|
||||
|
||||
PROGRESS.md / PLAN.md 실제 부트스트랩 (첫 번째 Sprint Contract 작성부터).
|
||||
26
docs/history/2026-04-07_이슈2-구현고려사항-정리.md
Normal file
26
docs/history/2026-04-07_이슈2-구현고려사항-정리.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# 2026-04-07 이슈 #2 — 구현 고려사항 정리
|
||||
|
||||
- **이슈**: kimminsung/recordingtest#2
|
||||
- **소요 시간**: ~5분
|
||||
- **Context 사용량**: ~52k tokens
|
||||
|
||||
## 정리한 고려사항 카테고리
|
||||
|
||||
1. **레코딩/리플레이 안정성** — 좌표 vs element, 타이밍, 포커스/Z-order, IME
|
||||
2. **WPF/MEF 특성** — Custom AutomationPeer, plugin 비동기 로드, Dispatcher marshaling
|
||||
3. **3D 뷰포트(HmEG) 검증** — 픽셀 비교 한계, 엔진 내부 상태 검증, 픽킹 안정성
|
||||
4. **환경/재현성** — DPI, 멀티모니터, 시계/랜덤 격리, CI 세션 0 제약
|
||||
5. **테스트 자산 관리** — JSON/YAML 시나리오, 베이스라인 이미지 LFS, 모듈화
|
||||
6. **진단/디버깅** — 실패 아티팩트(스샷·UIA 덤프·엔진 상태), 영상 녹화, diff 오버레이
|
||||
7. **보안/안전** — 입력 마스킹, 권한 일치
|
||||
8. **거버넌스** — SUT 코드 변경 협조(AutomationPeer/probe), 유지보수 비용
|
||||
|
||||
## 핵심 인사이트
|
||||
|
||||
- 픽셀 비교보다 **엔진 내부 상태(engine-bridge)** 가 훨씬 안정적 → 우선순위 상향
|
||||
- **SUT 팀 협조** 가 기술적 난제보다 큰 장애물 (AutomationPeer/probe 부착)
|
||||
- WPF는 세션 0에서 못 돌므로 CI는 대화형 세션 agent 필요
|
||||
|
||||
## 다음 단계
|
||||
|
||||
위 고려사항을 sut-prober PoC 설계에 반영.
|
||||
41
docs/history/2026-04-07_이슈2-리서치-에이전트분해.md
Normal file
41
docs/history/2026-04-07_이슈2-리서치-에이전트분해.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# 2026-04-07 이슈 #2 — 리서치 및 Agent 분해
|
||||
|
||||
- **이슈**: kimminsung/recordingtest#2 — 요구사항 컨텍스트 엔지니어링과 AI 개발환경 셋팅
|
||||
- **소요 시간**: ~15분
|
||||
- **Context 사용량**: ~45k tokens
|
||||
|
||||
## 작업 내용
|
||||
|
||||
WPF + MEF 플러그인 + 자체 HmEG 3D 엔진으로 구성된 SUT를 대상으로 한 입력 회귀 테스트 자동화 도구의 접근법을 웹 리서치하고, 단계별 agent로 분해하여 메모리에 저장.
|
||||
|
||||
### 리서치 결론
|
||||
|
||||
- **UIA 1순위**: FlaUI (.NET native, WPF 정밀 제어). WinAppDriver는 정체, Appium은 그 래퍼.
|
||||
- **3D 뷰포트**: UIA 불가 → 좌표 입력 + 픽셀/이미지 비교 + 엔진 in-process hook 하이브리드.
|
||||
- **MEF plug-in**: 각 plugin 프로젝트에 custom `AutomationPeer` 부착, plugin 상태를 probe로 노출.
|
||||
- **레코딩**: Win32 low-level hook + UIA element path + offset 동시 저장(해상도 내성).
|
||||
|
||||
### Agent 분해(예정)
|
||||
|
||||
1. sut-prober — UIA 트리 덤프, MEF plugin 목록
|
||||
2. recorder — element-aware 입력 캡처
|
||||
3. player — 재생/동기화
|
||||
4. viewport-verifier — 3D 스크린샷 비교
|
||||
5. engine-bridge — HmEG 상태 노출
|
||||
6. test-runner — 시나리오 실행/리포트
|
||||
7. diff-reporter — 실패 시각화
|
||||
|
||||
## 산출물
|
||||
|
||||
- 메모리: `project_recordingtest_goal.md`, `project_recordingtest_plan.md`
|
||||
- 본 히스토리 파일
|
||||
|
||||
## 다음 단계
|
||||
|
||||
`sut-prober` PoC부터 시작 — 사용자 결정 대기.
|
||||
|
||||
## 참고 링크
|
||||
|
||||
- [FlaUI vs WinAppDriver 2026](https://www.testsprite.com/use-cases/en/the-most-accurate-alternatives-to-winappdriver)
|
||||
- [WPF Custom AutomationPeer](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/ui-automation-of-a-wpf-custom-control)
|
||||
- [TestComplete WPF Record/Replay](https://support.smartbear.com/testcomplete/docs/app-testing/desktop/wpf/index.html)
|
||||
51
docs/history/2026-04-07_이슈2-병렬-스캐폴드-contracts.md
Normal file
51
docs/history/2026-04-07_이슈2-병렬-스캐폴드-contracts.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# 2026-04-07 이슈 #2 — 병렬 스캐폴드 + Sprint Contracts 일괄
|
||||
|
||||
- **이슈**: kimminsung/recordingtest#2
|
||||
- **소요 시간**: ~20분
|
||||
- **Context 사용량**: ~130k tokens
|
||||
|
||||
## 작업 내용
|
||||
|
||||
"병렬화 가능한 부분 병렬 구현" 요청에 따라, 독립 파일들을 한 세션에서 병렬 Write로 일괄 생성.
|
||||
|
||||
### 부트스트랩 문서
|
||||
- `PROGRESS.md` — 공유 상태 파일 확장 (Done/In progress/Blocked/Scaffolding review)
|
||||
- `PLAN.md` — P0~P3 우선순위 큐 + 작업 규칙
|
||||
|
||||
### Sprint Contracts (5건)
|
||||
`docs/contracts/` 하위:
|
||||
- `sut-prober.md` — 187개 플러그인 카탈로그 + json-configs + assemblies 덤프
|
||||
- `normalizer.md` — 기본 프로파일 5규칙, idempotent
|
||||
- `recorder.md` — element-aware 입력 캡처, IME·마스킹 포함
|
||||
- `player.md` — 10회 중 9회 성공, 고정 sleep 금지
|
||||
- `diff-reporter.md` — JSON/텍스트 의미 diff, triager 연동
|
||||
|
||||
각 contract는 planner 에이전트 규격(Goal / DoD / Interfaces / Out of scope / Evaluation plan / Risks)을 준수하며, DoD 항목은 모두 객관적으로 검증 가능.
|
||||
|
||||
### SUT 카탈로그 v0
|
||||
- `docs/sut-catalog/catalog.md` — 어셈블리·UI스택·Json 설정·저장 포맷·영향 요약
|
||||
- `docs/sut-catalog/plugins.md` — 187개 플러그인 전체 목록 (사전순)
|
||||
|
||||
### 솔루션 스캐폴드 (sut-prober PoC)
|
||||
- `recordingtest.sln`
|
||||
- `Directory.Build.props` (net8.0, nullable, warnings-as-errors)
|
||||
- `src/Recordingtest.SutProber/` 4개 파일:
|
||||
- `Recordingtest.SutProber.csproj`
|
||||
- `Program.cs` — CLI 진입, `--sut`/`--out` 인자
|
||||
- `PluginScanner.cs` — 정렬된 플러그인 엔트리 스캔
|
||||
- `JsonConfigScanner.cs` — Json/*.json top-level 키 + 의심 필드 휴리스틱
|
||||
- `AssemblyScanner.cs` — HmEG*/Editor*/HmGeometry* 어셈블리 + PDB 존재
|
||||
|
||||
**주의**: sut-prober 구현은 contract를 **선제적으로 맞추지는 않음** — 빌드·실행·evaluator 검증은 별도 Generator 세션의 몫. 현재는 컴파일 가능한 뼈대 수준.
|
||||
|
||||
## 병렬화 전략
|
||||
|
||||
- 파일 간 의존성 0인 것들만 병렬 Write
|
||||
- Edit는 기존 파일 의존 → 순차 처리
|
||||
- 에이전트 스폰 대신 단일 세션 다중 도구 호출로 context 오버헤드 최소화
|
||||
|
||||
## 다음 단계
|
||||
|
||||
1. **Generator 세션**에서 sut-prober 빌드/실행 → 카탈로그 산출물 생성
|
||||
2. **Evaluator 세션**에서 `/evaluate sut-prober` → contract DoD 채점
|
||||
3. pass 시 PROGRESS.md → PLAN.md 제거 → P1 항목 착수
|
||||
100
docs/sut-catalog/catalog.md
Normal file
100
docs/sut-catalog/catalog.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# SUT Catalog (v0, 정적 분석)
|
||||
|
||||
> 수동 관찰 기반 초기 카탈로그. `sut-prober` PoC 완료 시 기계 생성 파일로 교체된다.
|
||||
> 마지막 갱신: 2026-04-07
|
||||
|
||||
## 개요
|
||||
|
||||
- **이름**: EG-BIM Modeler
|
||||
- **위치**: `EG-BIM Modeler/` (git 제외)
|
||||
- **실행파일**: `EG-BIM Modeler.exe` (.NET / WPF)
|
||||
- **구성**: `.deps.json`, `.runtimeconfig.json`, `.pdb` 동봉
|
||||
|
||||
## 핵심 어셈블리
|
||||
|
||||
| 이름 | 역할 | PDB |
|
||||
|------|------|-----|
|
||||
| `HmEG.dll` | 자체 3D 엔진 | ✅ (`HmEG.pdb`, `HmEG.xml`) |
|
||||
| `HmGeometry.dll` / `HmGeometry.V2.dll` | 지오메트리 커널 | ✅ |
|
||||
| `HmTriangle.dll` | 삼각분할 | ✅ |
|
||||
| `HmPG.dll` | (추정) 페러미터 그래프/프로파일 | - |
|
||||
| `HmCommonUI.dll`, `HmCommonBridge.dll` | 공통 UI / 네이티브 브릿지 | - |
|
||||
| `Editor02.HmEGAppManager.dll` | 앱 생명주기 / plugin 매니저 (MEF 로드 추정) | ✅ |
|
||||
| `Editor03.PluginInterface.dll` | Plugin 기본 계약 | ✅ |
|
||||
| `Editor04.CommandControl.dll` | 명령 파이프라인 UI | ✅ |
|
||||
| `Editor05.CommandCore.dll` | 명령 실행 코어 | ✅ |
|
||||
| `Editor06.CommandCustom.dll` | 커스텀 명령 | ✅ |
|
||||
| `Editor07.WidgetPluginInterface.dll` | 위젯 플러그인 계약 | ✅ |
|
||||
| `EditorCore.dll` | Editor 코어 | ✅ |
|
||||
| `Editor01.Localization.dll` | 다국어 | ✅ |
|
||||
| `Editor.AI01.HttpConnector.dll` | AI 연동 | ✅ |
|
||||
|
||||
## 렌더링
|
||||
|
||||
- **SharpDX** (D3D9/D3D11/DXGI/Direct2D, Mathematics, D3DCompiler)
|
||||
- 3D 뷰포트는 D3D surface → **UIA 사각지대**
|
||||
- `assimp.dll` / `AssimpNet.dll` → 메시 임포트
|
||||
- `freetype6.dll`, `SharpFont.dll` → 폰트
|
||||
- `SharpVectors.*` → SVG 지원
|
||||
|
||||
## UI 스택
|
||||
|
||||
- `MahApps.Metro`, `ControlzEx`, `Dragablz`
|
||||
- `CommunityToolkit.Mvvm`
|
||||
- `Microsoft.Xaml.Behaviors`, `System.Windows.Interactivity`
|
||||
- `System.Windows.Controls.WpfPropertyGrid`
|
||||
- `Microsoft.Web.WebView2.*` (WebView2 패널)
|
||||
|
||||
## 유틸/공용
|
||||
|
||||
- `Serilog` + `Serilog.Sinks.File`, `log4net`
|
||||
- `Newtonsoft.Json`, `Google.Protobuf`, `MemoryPack.Core`
|
||||
- `UnitsNet`, `FluentScheduler`, `System.Reactive`, `Flurl.Http`
|
||||
- `BaronSoftware.Auth.dll` — 인증/라이선스 추정 (**레코딩 시 민감정보 주의**)
|
||||
|
||||
## Json 설정 (텍스트 golden-file 1차 타깃)
|
||||
|
||||
`EG-BIM Modeler/Json/`:
|
||||
|
||||
- `Settings.json`, `DefaultSettings.json`
|
||||
- `CategoryCommands.json`, `DefaultCategoryCommands.json`
|
||||
- `CommandAlias.json`, `DefaultCommandAlias.json`
|
||||
- `KeyShortCut.json`, `DefaultKeyShortCut.json`
|
||||
- `MouseSnap.json`, `DefaultMouseSnap.json`
|
||||
- `StartupCommand.json`, `DefaultStartupCommand.json`
|
||||
- `Units.json`, `DefaultUnits.json`
|
||||
- `Materials.json`
|
||||
- `RecentFiles.json` — **확실히 비결정적** (최근 사용 파일 경로)
|
||||
|
||||
### 비결정성 후보 필드 (정규화 대상)
|
||||
|
||||
- `RecentFiles.json` 전체
|
||||
- 모든 `Settings.json` 내부의 경로·창 크기·마지막 실행 시간
|
||||
- GUID·타임스탬프 (포맷 확인 필요)
|
||||
|
||||
## 저장 파일 포맷
|
||||
|
||||
- `lmd.hme` — 자체 모델 포맷 (`.hme`)
|
||||
- `jversion.egm` — 버전/메타 파일
|
||||
- **바이너리 추정** — 별도 포맷 분석 contract 필요
|
||||
|
||||
## 로컬라이제이션
|
||||
|
||||
- `ko-KR`, `en-US`, `ja-JP`, `es-ES` 리소스 폴더
|
||||
- `Editor01.Localization.dll` 경유
|
||||
|
||||
## MEF Plugin
|
||||
|
||||
- 위치: `EG-BIM Modeler/Plugins/Eg*Plugin/`
|
||||
- **총 187개** (2026-04-07 스냅샷)
|
||||
- 카테고리: 생성(Box, Circle, Arc, Curve, Cone, Cylinder …), Boolean(Union/Intersection/Difference), Array(Linear/Polar/Crv), Align, Check(Border/Disjointed/Duplicated/NonManifold/Self-Intersection/Overlap/ZeroArea/ZeroCurve), Audit, Block(Edit), Chamfer, Clash, Cap, Cut, DeleteFaces, Import/Export 등
|
||||
- 전체 목록: [plugins.md](plugins.md)
|
||||
|
||||
## recordingtest 영향 요약
|
||||
|
||||
1. **1차 검증 타깃**: `Json/` 텍스트 파일 (정규화 쉬움)
|
||||
2. **2차**: `.hme`/`.egm` 파서 필요
|
||||
3. **3D 뷰포트**: 좌표 입력 + engine-bridge
|
||||
4. **Plugin 카탈로그**: sut-prober 자동 덤프
|
||||
5. **PDB 풍부** → engine-bridge 리플렉션 비용 낮음
|
||||
6. **인증/라이선스**: 자동화 테스트 환경에서 로그인 상태 유지/마스킹 필요
|
||||
199
docs/sut-catalog/plugins.md
Normal file
199
docs/sut-catalog/plugins.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# EG-BIM Modeler Plugin List
|
||||
|
||||
- **스냅샷 일시**: 2026-04-07
|
||||
- **총 개수**: 187
|
||||
- **경로**: `EG-BIM Modeler/Plugins/`
|
||||
|
||||
> 자동 생성 전 수동 덤프. `sut-prober` PoC 완료 시 `plugins.json`으로 교체된다.
|
||||
|
||||
## 전체 목록 (사전순)
|
||||
|
||||
```
|
||||
Eg3DFacePlugin
|
||||
Eg3DMImportExporter
|
||||
EgAddToGroupPlugin
|
||||
EgAlignPlugin
|
||||
EgAlignSelectedVerticesPlugin
|
||||
EgAlignVerticesPlugin
|
||||
EgAnglePlugin
|
||||
EgArcPlugin
|
||||
EgAreaCentroidPlugin
|
||||
EgAreaPlugin
|
||||
EgArrayCrvPlugin
|
||||
EgArrayLinearPlugin
|
||||
EgArrayPlugin
|
||||
EgArrayPolarPlugin
|
||||
EgAuditPlugin
|
||||
EgBlockEditPlugin
|
||||
EgBlockPlugin
|
||||
EgBooleanDifferencePlugin
|
||||
EgBooleanIntersectionPlugin
|
||||
EgBooleanUnionPlugin
|
||||
EgBoundingBoxPlugin
|
||||
EgBoxPlugin
|
||||
EgBuildSectionPlugin
|
||||
EgCPlanePlugin
|
||||
EgCapPlugin
|
||||
EgChamferPlugin
|
||||
EgChangeColorPlugin
|
||||
EgCheckBorderEdgePlugin
|
||||
EgCheckDisjointedMeshPlugin
|
||||
EgCheckDuplicatedVertexPlugin
|
||||
EgCheckNonManifoldEdgePlugin
|
||||
EgCheckSelfIntersectionPlugin
|
||||
EgCheckSelfOverlapPlugin
|
||||
EgCheckZeroAreaPlugin
|
||||
EgCheckZeroCurvePlugin
|
||||
EgCirclePlugin
|
||||
EgClashPlugin
|
||||
EgCloseCrvPlugin
|
||||
EgClosestPtPlugin
|
||||
EgConePlugin
|
||||
EgConvertTextToBlockAttributePlugin
|
||||
EgCopyPlugin
|
||||
EgCopyToClipboardPlugin
|
||||
EgCrvEndPlugin
|
||||
EgCrvStartPlugin
|
||||
EgCurvePlugin
|
||||
EgCurveThroughPtPlugin
|
||||
EgCutPlugin
|
||||
EgCylinderPlugin
|
||||
EgDeleteFacesPlugin
|
||||
EgDeletePlugin
|
||||
EgDeleteSubCrvPlugin
|
||||
EgDeselByUidsPlugin
|
||||
EgDimAnglePlugin
|
||||
EgDirPlugin
|
||||
EgDistancePlugin
|
||||
EgDividePlugin
|
||||
EgDomainPlugin
|
||||
EgDotPlugin
|
||||
EgDupBorderPlugin
|
||||
EgDupEdgePlugin
|
||||
EgDupMeshHoleBoundaryPlugin
|
||||
EgEditTheGradingElevationPlugin
|
||||
EgEllipsePlugin
|
||||
EgEllipsoidPlugin
|
||||
EgEvaluatePtPlugin
|
||||
EgExplodeBlockPlugin
|
||||
EgExplodePlugin
|
||||
EgExportByPathPlugin
|
||||
EgExtendPlugin
|
||||
EgExtractMeshFacePlugin
|
||||
EgExtractNonManifoldMeshEdgesPlugin
|
||||
EgExtractPtPlugin
|
||||
EgExtrudeAlongCrvPlugin
|
||||
EgExtrudePlugin
|
||||
EgFillMeshHolePlugin
|
||||
EgFilletEdgePlugin
|
||||
EgFilletPlugin
|
||||
EgFilterByFaceCountPlugin
|
||||
EgFilterByLayerPlugin
|
||||
EgFilterByLengthPlugin
|
||||
EgFilterByModelTypePlugin
|
||||
EgFilterBySpatialBoxPlugin
|
||||
EgFilteringWithXYPlanePlugin
|
||||
EgFindTextPlugin
|
||||
EgFlipPlugin
|
||||
EgGroupPlugin
|
||||
EgHandleCurvePlugin
|
||||
EgHidePlugin
|
||||
EgHyperbolaPlugin
|
||||
EgImportByPathPlugin
|
||||
EgInsertPlugin
|
||||
EgInterpCrvPlugin
|
||||
EgIsolatePlugin
|
||||
EgJoinPlugin
|
||||
EgLengthPlugin
|
||||
EgLinePlugin
|
||||
EgLineSmoothPlugin
|
||||
EgLoftPlugin
|
||||
EgMarginLinePlugin
|
||||
EgMatchPropertiesPlugin
|
||||
EgMergeFacesPlugin
|
||||
EgMeshIntersectPlugin
|
||||
EgMeshPatchPlugin
|
||||
EgMeshPolylinePlugin
|
||||
EgMirrorPlugin
|
||||
EgModelClearPlugin
|
||||
EgMovePlugin
|
||||
EgObjectDescriptionPlugin
|
||||
EgOffsetMeshPlugin
|
||||
EgOffsetPlugin
|
||||
EgOneLayerOffPlugin
|
||||
EgOneLayerOnPlugin
|
||||
EgOpenURLPlugin
|
||||
EgOrientPlugin
|
||||
EgParabolaPlugin
|
||||
EgPastePlugin
|
||||
EgPatchPlugin
|
||||
EgPipePlugin
|
||||
EgPlanarDifferencePlugin
|
||||
EgPlanarIntersectionPlugin
|
||||
EgPlanarUnionPlugin
|
||||
EgPlanePlugin
|
||||
EgPlaneThroughPtPlugin
|
||||
EgPointPlugin
|
||||
EgPointsOffPlugin
|
||||
EgPointsOnPlugin
|
||||
EgPointsPlugin
|
||||
EgPolygonCountPlugin
|
||||
EgPolygonPlugin
|
||||
EgPolylinePlugin
|
||||
EgProfileSweepPlugin
|
||||
EgProjectPlugin
|
||||
EgProjectionPointPlugin
|
||||
EgPyramidPlugin
|
||||
EgRLeaderEditPlugin
|
||||
EgRebuildPlugin
|
||||
EgRectanglePlugin
|
||||
EgReduceMeshPlugin
|
||||
EgRemoveFromGroupPlugin
|
||||
EgRemoveSelfIntersectionPlugin
|
||||
EgRepairCrvTopologyPlugin
|
||||
EgRevolvePlugin
|
||||
EgRibbonPlugin
|
||||
EgRoadSectionPlugin
|
||||
EgRotate3DPlugin
|
||||
EgRotatePlugin
|
||||
EgScale1DPlugin
|
||||
EgScale2DPlugin
|
||||
EgScalePlugin
|
||||
EgScreenShotPlugin
|
||||
EgSelBoxPlugin
|
||||
EgSelByUidsPlugin
|
||||
EgSelColorPlugin
|
||||
EgSelLayerPlugin
|
||||
EgSelNamePlugin
|
||||
EgSelectedLayersOffPlugin
|
||||
EgSetDisplayModePlugin
|
||||
EgSetFocusedViewportPlugin
|
||||
EgSetGroupNamePlugin
|
||||
EgSetObjectNamePlugin
|
||||
EgSetViewmodePlugin
|
||||
EgShearPlugin
|
||||
EgShowPlugin
|
||||
EgSlabPlugin
|
||||
EgSlopePlugin
|
||||
EgSnipCrvPlugin
|
||||
EgSpherePlugin
|
||||
EgSplitDisjointMeshPlugin
|
||||
EgSplitPlugin
|
||||
EgSrfPtPlugin
|
||||
EgSubCrvPlugin
|
||||
EgSurfaceQuadRemeshPlugin
|
||||
EgSweep1Plugin
|
||||
EgSweep2Plugin
|
||||
EgTextPlugin
|
||||
EgTorusPlugin
|
||||
EgTrimPlugin
|
||||
EgTruncatedPyramidPlugin
|
||||
EgTubePlugin
|
||||
EgUnGroupPlugin
|
||||
EgUngroupAllPlugin
|
||||
EgUnifyNormalPlugin
|
||||
EgVerticalScalePlugin
|
||||
EgVolumeCentroidPlugin
|
||||
EgZoomPlugin
|
||||
HMEGImportExport
|
||||
```
|
||||
16
recordingtest.sln
Normal file
16
recordingtest.sln
Normal file
@@ -0,0 +1,16 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Recordingtest.SutProber", "src\Recordingtest.SutProber\Recordingtest.SutProber.csproj", "{1A0B2C3D-0001-0000-0000-000000000001}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{1A0B2C3D-0001-0000-0000-000000000001}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1A0B2C3D-0001-0000-0000-000000000001}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1A0B2C3D-0001-0000-0000-000000000001}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1A0B2C3D-0001-0000-0000-000000000001}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
25
src/Recordingtest.SutProber/AssemblyScanner.cs
Normal file
25
src/Recordingtest.SutProber/AssemblyScanner.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace Recordingtest.SutProber;
|
||||
|
||||
public sealed record AssemblyEntry(string Name, long SizeBytes, bool HasPdb);
|
||||
|
||||
public static class AssemblyScanner
|
||||
{
|
||||
private static readonly string[] Prefixes = { "HmEG", "HmGeometry", "HmTriangle", "HmPG", "HmCommon", "Editor", "EditorCore" };
|
||||
|
||||
public static List<AssemblyEntry> Scan(string sutRoot)
|
||||
{
|
||||
var entries = new List<AssemblyEntry>();
|
||||
if (!Directory.Exists(sutRoot)) return entries;
|
||||
|
||||
foreach (var file in Directory.EnumerateFiles(sutRoot, "*.dll", SearchOption.TopDirectoryOnly)
|
||||
.OrderBy(f => f, StringComparer.Ordinal))
|
||||
{
|
||||
var name = Path.GetFileName(file);
|
||||
if (!Prefixes.Any(p => name.StartsWith(p, StringComparison.Ordinal))) continue;
|
||||
|
||||
var pdb = Path.ChangeExtension(file, ".pdb");
|
||||
entries.Add(new AssemblyEntry(name, new FileInfo(file).Length, File.Exists(pdb)));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
51
src/Recordingtest.SutProber/JsonConfigScanner.cs
Normal file
51
src/Recordingtest.SutProber/JsonConfigScanner.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Recordingtest.SutProber;
|
||||
|
||||
public sealed record JsonConfigEntry(
|
||||
string Name,
|
||||
IReadOnlyList<string> TopLevelKeys,
|
||||
IReadOnlyList<string> SuspectedNondeterministicFields);
|
||||
|
||||
public static class JsonConfigScanner
|
||||
{
|
||||
private static readonly string[] SuspectPatterns =
|
||||
{ "time", "date", "path", "recent", "guid", "id", "machine", "user" };
|
||||
|
||||
public static List<JsonConfigEntry> Scan(string sutRoot)
|
||||
{
|
||||
var jsonRoot = Path.Combine(sutRoot, "Json");
|
||||
if (!Directory.Exists(jsonRoot)) return new();
|
||||
|
||||
var entries = new List<JsonConfigEntry>();
|
||||
foreach (var file in Directory.EnumerateFiles(jsonRoot, "*.json").OrderBy(f => f, StringComparer.Ordinal))
|
||||
{
|
||||
var name = Path.GetFileName(file);
|
||||
var keys = new List<string>();
|
||||
var suspects = new List<string>();
|
||||
try
|
||||
{
|
||||
using var doc = JsonDocument.Parse(File.ReadAllText(file));
|
||||
if (doc.RootElement.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
foreach (var p in doc.RootElement.EnumerateObject())
|
||||
{
|
||||
keys.Add(p.Name);
|
||||
var lower = p.Name.ToLowerInvariant();
|
||||
if (SuspectPatterns.Any(sp => lower.Contains(sp)))
|
||||
suspects.Add(p.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
keys.Add("<invalid-json>");
|
||||
}
|
||||
|
||||
keys.Sort(StringComparer.Ordinal);
|
||||
suspects.Sort(StringComparer.Ordinal);
|
||||
entries.Add(new JsonConfigEntry(name, keys, suspects));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
32
src/Recordingtest.SutProber/PluginScanner.cs
Normal file
32
src/Recordingtest.SutProber/PluginScanner.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
namespace Recordingtest.SutProber;
|
||||
|
||||
public sealed record PluginEntry(string Name, string Path, IReadOnlyList<string> Dlls, long SizeBytes);
|
||||
|
||||
public static class PluginScanner
|
||||
{
|
||||
public static List<PluginEntry> Scan(string sutRoot)
|
||||
{
|
||||
var pluginRoot = System.IO.Path.Combine(sutRoot, "Plugins");
|
||||
if (!Directory.Exists(pluginRoot)) return new();
|
||||
|
||||
var entries = new List<PluginEntry>();
|
||||
foreach (var dir in Directory.EnumerateDirectories(pluginRoot).OrderBy(d => d, StringComparer.Ordinal))
|
||||
{
|
||||
var dlls = Directory.EnumerateFiles(dir, "*.dll", SearchOption.TopDirectoryOnly)
|
||||
.Select(System.IO.Path.GetFileName)
|
||||
.Where(n => n is not null)
|
||||
.Select(n => n!)
|
||||
.OrderBy(n => n, StringComparer.Ordinal)
|
||||
.ToList();
|
||||
|
||||
long size = 0;
|
||||
foreach (var f in Directory.EnumerateFiles(dir, "*", SearchOption.AllDirectories))
|
||||
size += new FileInfo(f).Length;
|
||||
|
||||
var name = System.IO.Path.GetFileName(dir);
|
||||
var relPath = System.IO.Path.GetRelativePath(".", dir).Replace('\\', '/');
|
||||
entries.Add(new PluginEntry(name, relPath, dlls, size));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
42
src/Recordingtest.SutProber/Program.cs
Normal file
42
src/Recordingtest.SutProber/Program.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System.Text.Json;
|
||||
using Recordingtest.SutProber;
|
||||
|
||||
// Static-only probe of the EG-BIM Modeler SUT folder. NEVER launches the SUT.
|
||||
// See docs/contracts/sut-prober.md for the sprint contract.
|
||||
|
||||
string sutPath = "EG-BIM Modeler";
|
||||
string outDir = Path.Combine("docs", "sut-catalog");
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (args[i] == "--sut" && i + 1 < args.Length) sutPath = args[++i];
|
||||
else if (args[i] == "--out" && i + 1 < args.Length) outDir = args[++i];
|
||||
}
|
||||
|
||||
if (!Directory.Exists(sutPath))
|
||||
{
|
||||
Console.Error.WriteLine($"SUT path not found: {sutPath}");
|
||||
return 2;
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(outDir);
|
||||
|
||||
var plugins = PluginScanner.Scan(sutPath);
|
||||
var jsonConfigs = JsonConfigScanner.Scan(sutPath);
|
||||
var assemblies = AssemblyScanner.Scan(sutPath);
|
||||
|
||||
var opts = new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true,
|
||||
// deterministic property order follows POCO definition
|
||||
};
|
||||
|
||||
File.WriteAllText(Path.Combine(outDir, "plugins.json"),
|
||||
JsonSerializer.Serialize(plugins, opts));
|
||||
File.WriteAllText(Path.Combine(outDir, "json-configs.json"),
|
||||
JsonSerializer.Serialize(jsonConfigs, opts));
|
||||
File.WriteAllText(Path.Combine(outDir, "assemblies.json"),
|
||||
JsonSerializer.Serialize(assemblies, opts));
|
||||
|
||||
Console.WriteLine($"Wrote catalog to {outDir}/ — plugins: {plugins.Count}, json: {jsonConfigs.Count}, assemblies: {assemblies.Count}");
|
||||
return 0;
|
||||
10
src/Recordingtest.SutProber/Recordingtest.SutProber.csproj
Normal file
10
src/Recordingtest.SutProber/Recordingtest.SutProber.csproj
Normal file
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AssemblyName>Recordingtest.SutProber</AssemblyName>
|
||||
<RootNamespace>Recordingtest.SutProber</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Text.Json" Version="8.0.5" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user