deuk-agent-flow 4.2.7 → 5.0.4
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/CHANGELOG.ko.md +277 -0
- package/CHANGELOG.md +794 -124
- package/LICENSE +0 -0
- package/README.ko.md +97 -17
- package/README.md +108 -25
- package/bin/deuk-agent-flow.js +11 -13
- package/bin/deuk-agent-rule.js +1 -1
- package/bundled/README.md +3 -0
- package/bundled/deuk-agent-flow.vsix +0 -0
- package/core-rules/AGENTS.md +30 -120
- package/docs/architecture.ko.md +155 -2
- package/docs/architecture.md +155 -2
- package/docs/assets/agentflow-panel-skills.png +0 -0
- package/docs/assets/agentflow-panel.png +0 -0
- package/docs/how-it-works.ko.md +109 -52
- package/docs/how-it-works.md +128 -71
- package/docs/principles.ko.md +68 -68
- package/docs/principles.md +68 -68
- package/docs/usage-guide.ko.md +251 -212
- package/package.json +60 -45
- package/scripts/bundle-vscode-vsix.ts +67 -0
- package/scripts/{cli-args.mjs → cli-args.ts} +49 -12
- package/scripts/cli-context-commands.ts +35 -0
- package/scripts/cli-init-claude.ts +179 -0
- package/scripts/cli-init-commands.ts +99 -0
- package/scripts/cli-init-core.ts +89 -0
- package/scripts/cli-init-instructions.ts +489 -0
- package/scripts/cli-init-logic.ts +46 -0
- package/scripts/cli-init-migrate.ts +504 -0
- package/scripts/cli-init-workspace.ts +63 -0
- package/scripts/cli-locales.ts +85 -0
- package/scripts/{cli-prompts.mjs → cli-prompts.ts} +19 -34
- package/scripts/{cli-rule-compiler.mjs → cli-rule-compiler.ts} +128 -112
- package/scripts/cli-skill-commands.ts +709 -0
- package/scripts/{cli-telemetry-commands.mjs → cli-telemetry-commands.ts} +25 -22
- package/scripts/cli-ticket-command-shared.ts +4 -0
- package/scripts/cli-ticket-commands.ts +3781 -0
- package/scripts/cli-ticket-docmeta.ts +103 -0
- package/scripts/cli-ticket-document.ts +153 -0
- package/scripts/cli-ticket-evidence.ts +312 -0
- package/scripts/cli-ticket-home.ts +449 -0
- package/scripts/cli-ticket-index.ts +283 -0
- package/scripts/{cli-ticket-migration.mjs → cli-ticket-migration.ts} +44 -68
- package/scripts/cli-ticket-parser.ts +100 -0
- package/scripts/cli-ticket-scan.ts +102 -0
- package/scripts/cli-ticket-surface.ts +56 -0
- package/scripts/{cli-usage-commands.mjs → cli-usage-commands.ts} +325 -326
- package/scripts/cli-utils.ts +1560 -0
- package/scripts/cli.ts +695 -0
- package/scripts/{lint-md.mjs → lint-md.ts} +32 -42
- package/scripts/lint-rules.ts +203 -0
- package/scripts/{merge-logic.mjs → merge-logic.ts} +44 -44
- package/scripts/oss-prepack.js +32 -0
- package/scripts/out/scripts/bundle-vscode-vsix.js +68 -0
- package/scripts/out/scripts/bundle-vscode-vsix.js.map +1 -0
- package/scripts/out/scripts/cli-args.js +374 -0
- package/scripts/out/scripts/cli-args.js.map +1 -0
- package/scripts/out/scripts/cli-context-commands.js +31 -0
- package/scripts/out/scripts/cli-context-commands.js.map +1 -0
- package/scripts/out/scripts/cli-init-claude.js +176 -0
- package/scripts/out/scripts/cli-init-claude.js.map +1 -0
- package/scripts/out/scripts/cli-init-commands.js +87 -0
- package/scripts/out/scripts/cli-init-commands.js.map +1 -0
- package/scripts/out/scripts/cli-init-core.js +89 -0
- package/scripts/out/scripts/cli-init-core.js.map +1 -0
- package/scripts/out/scripts/cli-init-instructions.js +460 -0
- package/scripts/out/scripts/cli-init-instructions.js.map +1 -0
- package/scripts/out/scripts/cli-init-logic.js +41 -0
- package/scripts/out/scripts/cli-init-logic.js.map +1 -0
- package/scripts/out/scripts/cli-init-migrate.js +497 -0
- package/scripts/out/scripts/cli-init-migrate.js.map +1 -0
- package/scripts/out/scripts/cli-init-workspace.js +57 -0
- package/scripts/out/scripts/cli-init-workspace.js.map +1 -0
- package/scripts/out/scripts/cli-locales.js +83 -0
- package/scripts/out/scripts/cli-locales.js.map +1 -0
- package/scripts/out/scripts/cli-prompts.js +101 -0
- package/scripts/out/scripts/cli-prompts.js.map +1 -0
- package/scripts/out/scripts/cli-rule-compiler.js +121 -0
- package/scripts/out/scripts/cli-rule-compiler.js.map +1 -0
- package/scripts/out/scripts/cli-skill-commands.js +708 -0
- package/scripts/out/scripts/cli-skill-commands.js.map +1 -0
- package/scripts/out/scripts/cli-telemetry-commands.js +604 -0
- package/scripts/out/scripts/cli-telemetry-commands.js.map +1 -0
- package/scripts/out/scripts/cli-ticket-command-shared.js +2 -0
- package/scripts/out/scripts/cli-ticket-command-shared.js.map +1 -0
- package/scripts/out/scripts/cli-ticket-commands.js +3530 -0
- package/scripts/out/scripts/cli-ticket-commands.js.map +1 -0
- package/scripts/out/scripts/cli-ticket-docmeta.js +90 -0
- package/scripts/out/scripts/cli-ticket-docmeta.js.map +1 -0
- package/scripts/out/scripts/cli-ticket-document.js +121 -0
- package/scripts/out/scripts/cli-ticket-document.js.map +1 -0
- package/scripts/out/scripts/cli-ticket-evidence.js +286 -0
- package/scripts/out/scripts/cli-ticket-evidence.js.map +1 -0
- package/scripts/out/scripts/cli-ticket-home.js +440 -0
- package/scripts/out/scripts/cli-ticket-home.js.map +1 -0
- package/scripts/out/scripts/cli-ticket-index.js +272 -0
- package/scripts/out/scripts/cli-ticket-index.js.map +1 -0
- package/scripts/out/scripts/cli-ticket-migration.js +261 -0
- package/scripts/out/scripts/cli-ticket-migration.js.map +1 -0
- package/scripts/out/scripts/cli-ticket-parser.js +90 -0
- package/scripts/out/scripts/cli-ticket-parser.js.map +1 -0
- package/scripts/out/scripts/cli-ticket-scan.js +93 -0
- package/scripts/out/scripts/cli-ticket-scan.js.map +1 -0
- package/scripts/out/scripts/cli-ticket-surface.js +53 -0
- package/scripts/out/scripts/cli-ticket-surface.js.map +1 -0
- package/scripts/out/scripts/cli-usage-commands.js +310 -0
- package/scripts/out/scripts/cli-usage-commands.js.map +1 -0
- package/scripts/out/scripts/cli-utils.js +1544 -0
- package/scripts/out/scripts/cli-utils.js.map +1 -0
- package/scripts/out/scripts/cli.js +696 -0
- package/scripts/out/scripts/cli.js.map +1 -0
- package/scripts/out/scripts/lint-md.js +238 -0
- package/scripts/out/scripts/lint-md.js.map +1 -0
- package/scripts/out/scripts/lint-rules.js +193 -0
- package/scripts/out/scripts/lint-rules.js.map +1 -0
- package/scripts/out/scripts/merge-logic.js +38 -0
- package/scripts/out/scripts/merge-logic.js.map +1 -0
- package/scripts/out/scripts/plan-parser.js +52 -0
- package/scripts/out/scripts/plan-parser.js.map +1 -0
- package/scripts/out/scripts/ticket-machine.js +94 -0
- package/scripts/out/scripts/ticket-machine.js.map +1 -0
- package/scripts/out/scripts/ticket-workflow.js +498 -0
- package/scripts/out/scripts/ticket-workflow.js.map +1 -0
- package/scripts/{plan-parser.mjs → plan-parser.ts} +53 -53
- package/scripts/ticket-machine.ts +99 -0
- package/scripts/ticket-workflow.ts +544 -0
- package/templates/MODULE_RULE_TEMPLATE.md +11 -11
- package/templates/PROJECT_RULE.md +46 -47
- package/templates/TICKET_TEMPLATE.ko.md +48 -44
- package/templates/TICKET_TEMPLATE.md +48 -44
- package/templates/project-memory.md +19 -0
- package/templates/project-pilot/CONFORMANCE_GATE_TEMPLATE.md +25 -25
- package/templates/project-pilot/DRIFT_CHECKLIST.md +27 -27
- package/templates/project-pilot/FLOW_CONTRACT_TEMPLATE.md +26 -26
- package/templates/project-pilot/IMPLEMENTATION_MATRIX_TEMPLATE.md +30 -30
- package/templates/project-pilot/INTEGRATION_CONTRACT_TEMPLATE.md +26 -26
- package/templates/project-pilot/OWNER_MAP_TEMPLATE.md +15 -15
- package/templates/project-pilot/PROJECT_PILOT_RULE_TEMPLATE.md +34 -34
- package/templates/project-pilot/REFACTOR_CONTRACT_TEMPLATE.md +32 -32
- package/templates/project-pilot/REMEDIATION_PLAN_TEMPLATE.md +33 -33
- package/templates/rules.d/deukcontext-mcp.md +31 -31
- package/templates/rules.d/platform-coexistence.md +29 -29
- package/templates/skills/context-recall/SKILL.md +3 -1
- package/templates/skills/doc-sync/SKILL.md +111 -0
- package/templates/skills/generated-file-guard/SKILL.md +3 -1
- package/templates/skills/persona-maid/SKILL.md +65 -0
- package/templates/skills/project-pilot/SKILL.md +14 -63
- package/templates/skills/role-closer/SKILL.md +27 -0
- package/templates/skills/role-implementer/SKILL.md +30 -0
- package/templates/skills/role-planner/SKILL.md +29 -0
- package/templates/skills/role-qa-verifier/SKILL.md +30 -0
- package/templates/skills/safe-refactor/SKILL.md +3 -1
- package/templates/skills/ticket-status-surface/SKILL.md +17 -0
- package/NOTICE.md +0 -19
- package/core-rules/GEMINI.md +0 -7
- package/docs/npm-publish-guide.ko.md +0 -70
- package/scripts/cli-init-commands.mjs +0 -1759
- package/scripts/cli-init-logic.mjs +0 -64
- package/scripts/cli-skill-commands.mjs +0 -212
- package/scripts/cli-ticket-command-shared.mjs +0 -60
- package/scripts/cli-ticket-commands.mjs +0 -2474
- package/scripts/cli-ticket-index.mjs +0 -322
- package/scripts/cli-ticket-parser.mjs +0 -210
- package/scripts/cli-utils.mjs +0 -602
- package/scripts/cli.mjs +0 -256
- package/scripts/lint-rules.mjs +0 -197
- package/scripts/publish-dual-npm.mjs +0 -141
- package/scripts/smoke-npm-docker.mjs +0 -102
- package/scripts/smoke-npm-local.mjs +0 -110
- package/scripts/update-download-badge.mjs +0 -103
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic Markdown Plan Parser
|
|
3
|
-
*
|
|
4
|
-
* Supports various AI Agent Plan formats (Antigravity, Copilot, etc.)
|
|
5
|
-
* Extracts basic ticket fields (title, summary, tasks) and body.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
export function parseGenericMarkdownPlan(content) {
|
|
9
|
-
const lines = content.split('\n');
|
|
10
|
-
|
|
11
|
-
// 1. Title: First H1
|
|
12
|
-
const h1Line = lines.find(l => /^# /.test(l));
|
|
13
|
-
const title = h1Line ? h1Line.replace(/^# /, '').trim() : '';
|
|
14
|
-
|
|
15
|
-
// 2. Summary: Text between H1 and the first --- or ##
|
|
16
|
-
const h1Idx = h1Line ? lines.indexOf(h1Line) : -1;
|
|
17
|
-
let summaryLines = [];
|
|
18
|
-
for (let i = h1Idx + 1; i < lines.length; i++) {
|
|
19
|
-
if (/^---$/.test(lines[i].trim()) || /^## /.test(lines[i])) break;
|
|
20
|
-
if (lines[i].trim()) summaryLines.push(lines[i].trim());
|
|
21
|
-
}
|
|
22
|
-
const summary = summaryLines.join(' ').slice(0, 200);
|
|
23
|
-
|
|
24
|
-
// 3. Tasks: Collect all checklist items
|
|
25
|
-
const tasks = lines
|
|
26
|
-
.filter(l => /^\s*- \[[ x/]\]/.test(l))
|
|
27
|
-
.map(l => l.replace(/^\s*- \[[ x/]\]\s*/, '').trim());
|
|
28
|
-
|
|
29
|
-
return { title, summary, tasks, body: content };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Detect agent type from plan path
|
|
34
|
-
*/
|
|
35
|
-
export function detectAgentFromPath(planPath) {
|
|
36
|
-
if (planPath.includes('.gemini')) return 'antigravity';
|
|
37
|
-
if (planPath.includes('copilot') || planPath.includes('.codex')) return 'copilot';
|
|
38
|
-
if (planPath.includes('.cursor')) return 'cursor';
|
|
39
|
-
if (planPath.includes('.claude')) return 'claude';
|
|
40
|
-
return 'generic';
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export const PLAN_PARSERS = {
|
|
44
|
-
'antigravity': parseGenericMarkdownPlan,
|
|
45
|
-
'copilot': parseGenericMarkdownPlan,
|
|
46
|
-
'generic': parseGenericMarkdownPlan
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export function parsePlan(planPath, content) {
|
|
50
|
-
const agent = detectAgentFromPath(planPath);
|
|
51
|
-
const parser = PLAN_PARSERS[agent] || parseGenericMarkdownPlan;
|
|
52
|
-
return parser(content);
|
|
53
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Generic Markdown Plan Parser
|
|
3
|
+
*
|
|
4
|
+
* Supports various AI Agent Plan formats (Antigravity, Copilot, etc.)
|
|
5
|
+
* Extracts basic ticket fields (title, summary, tasks) and body.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export function parseGenericMarkdownPlan(content) {
|
|
9
|
+
const lines = content.split('\n');
|
|
10
|
+
|
|
11
|
+
// 1. Title: First H1
|
|
12
|
+
const h1Line = lines.find(l => /^# /.test(l));
|
|
13
|
+
const title = h1Line ? h1Line.replace(/^# /, '').trim() : '';
|
|
14
|
+
|
|
15
|
+
// 2. Summary: Text between H1 and the first --- or ##
|
|
16
|
+
const h1Idx = h1Line ? lines.indexOf(h1Line) : -1;
|
|
17
|
+
let summaryLines = [];
|
|
18
|
+
for (let i = h1Idx + 1; i < lines.length; i++) {
|
|
19
|
+
if (/^---$/.test(lines[i].trim()) || /^## /.test(lines[i])) break;
|
|
20
|
+
if (lines[i].trim()) summaryLines.push(lines[i].trim());
|
|
21
|
+
}
|
|
22
|
+
const summary = summaryLines.join(' ').slice(0, 200);
|
|
23
|
+
|
|
24
|
+
// 3. Tasks: Collect all checklist items
|
|
25
|
+
const tasks = lines
|
|
26
|
+
.filter(l => /^\s*- \[[ x/]\]/.test(l))
|
|
27
|
+
.map(l => l.replace(/^\s*- \[[ x/]\]\s*/, '').trim());
|
|
28
|
+
|
|
29
|
+
return { title, summary, tasks, body: content };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Detect agent type from plan path
|
|
34
|
+
*/
|
|
35
|
+
export function detectAgentFromPath(planPath) {
|
|
36
|
+
if (planPath.includes('.gemini')) return 'antigravity';
|
|
37
|
+
if (planPath.includes('copilot') || planPath.includes('.codex')) return 'copilot';
|
|
38
|
+
if (planPath.includes('.cursor')) return 'cursor';
|
|
39
|
+
if (planPath.includes('.claude')) return 'claude';
|
|
40
|
+
return 'generic';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const PLAN_PARSERS = {
|
|
44
|
+
'antigravity': parseGenericMarkdownPlan,
|
|
45
|
+
'copilot': parseGenericMarkdownPlan,
|
|
46
|
+
'generic': parseGenericMarkdownPlan
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export function parsePlan(planPath, content) {
|
|
50
|
+
const agent = detectAgentFromPath(planPath);
|
|
51
|
+
const parser = PLAN_PARSERS[agent] || parseGenericMarkdownPlan;
|
|
52
|
+
return parser(content);
|
|
53
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// #691: XState 기반 티켓 워크플로 상태머신.
|
|
2
|
+
//
|
|
3
|
+
// LangGraph 형태 전환의 핵심 — 직접 구현한 전이 분기(미정의 전이 허용·guard 우회 같은
|
|
4
|
+
// 구멍)를 검증된 상태머신으로 대체한다. 머신 정의는 TICKET_WORKFLOW(SSOT)에서 동적으로
|
|
5
|
+
// 빌드하므로 노드/전이를 두 곳에 중복 선언하지 않는다.
|
|
6
|
+
//
|
|
7
|
+
// 모델: actor/런타임 없이 XState v5의 순수 transition()만 사용한다. 에이전트(Claude Code)가
|
|
8
|
+
// 곧 런타임이고, CLI는 stateless다 — 매 호출마다 .md에서 현재 state를 복원해 transition을
|
|
9
|
+
// 한 스텝 계산하고 결과를 .md에 기록한다. 따라서 actor 영속화가 필요 없다.
|
|
10
|
+
//
|
|
11
|
+
// 이벤트 모델: phase 전이는 GO_<target> 이벤트(예: GO_phase2, GO_end)로 표현한다.
|
|
12
|
+
// guard는 전이별 requiredEvidence를 evidence 입력으로 검증한다 — 미충족 시 XState가
|
|
13
|
+
// 전이를 거부하므로 증거 우회 구멍이 구조적으로 막힌다.
|
|
14
|
+
|
|
15
|
+
import { createMachine, transition, initialTransition } from "xstate";
|
|
16
|
+
import { TICKET_WORKFLOW, TICKET_WORKFLOW_EVIDENCE } from "./ticket-workflow.js";
|
|
17
|
+
|
|
18
|
+
export function eventForTarget(targetStateName) {
|
|
19
|
+
return `GO_${targetStateName}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// TICKET_WORKFLOW.states → XState states 정의. 각 전이는 GO_<target> 이벤트 + guard.
|
|
23
|
+
function buildMachineStates() {
|
|
24
|
+
const states: Record<string, any> = {};
|
|
25
|
+
for (const [name, node] of Object.entries(TICKET_WORKFLOW.states)) {
|
|
26
|
+
const n = node as Record<string, any>;
|
|
27
|
+
const on: Record<string, any> = {};
|
|
28
|
+
for (const target of n.transitions || []) {
|
|
29
|
+
const evidenceKeys = n.requiredEvidence?.[target] || [];
|
|
30
|
+
on[eventForTarget(target)] = {
|
|
31
|
+
target,
|
|
32
|
+
// guard: 이 전이가 요구하는 증거가 evidence 입력에 모두 있어야 통과.
|
|
33
|
+
guard: ({ event }) => {
|
|
34
|
+
const evidence = event?.evidence || {};
|
|
35
|
+
return evidenceKeys.every(key => Boolean(evidence[key]));
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
states[name] = (n.transitions || []).length === 0
|
|
40
|
+
? { type: "final" }
|
|
41
|
+
: { on };
|
|
42
|
+
}
|
|
43
|
+
return states;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 머신은 SSOT(TICKET_WORKFLOW)에서 빌드한다. ESM 순환 import 때문에 모듈 로드 시점엔
|
|
47
|
+
// TICKET_WORKFLOW가 아직 초기화 전일 수 있으므로 lazy로 첫 호출 때 빌드한다. initial은
|
|
48
|
+
// 의미 없으므로(매 호출마다 현재 state를 명시 복원) phase1로 둔다.
|
|
49
|
+
let _machine = null;
|
|
50
|
+
function getMachine() {
|
|
51
|
+
if (!_machine) {
|
|
52
|
+
_machine = createMachine({
|
|
53
|
+
id: "ticket-flow",
|
|
54
|
+
initial: "phase1",
|
|
55
|
+
states: buildMachineStates()
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return _machine;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 현재 state 이름으로 머신 스냅샷을 복원한다(.md phase → state name은 호출측에서 결정).
|
|
62
|
+
function snapshotForState(stateName) {
|
|
63
|
+
const [init] = initialTransition(getMachine());
|
|
64
|
+
// XState v5: 스냅샷의 value를 직접 지정해 임의 state에서 출발시킨다.
|
|
65
|
+
return { ...init, value: stateName };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 전이 결과: { allowed, blocked, reason, to } — 미정의 전이/guard 미충족이면 allowed=false.
|
|
69
|
+
// missingEvidence가 비어있지 않으면 guard 차단(증거 부족), to가 그대로면 미정의 전이.
|
|
70
|
+
export function machineTransition(fromStateName, targetStateName, evidence = {}) {
|
|
71
|
+
if (fromStateName === targetStateName) {
|
|
72
|
+
return { allowed: true, to: targetStateName, missingEvidence: [] };
|
|
73
|
+
}
|
|
74
|
+
const node = TICKET_WORKFLOW.states[fromStateName];
|
|
75
|
+
if (!node) {
|
|
76
|
+
return { allowed: false, reason: "unknown_state", to: fromStateName, missingEvidence: [] };
|
|
77
|
+
}
|
|
78
|
+
const allowedTargets = node.transitions || [];
|
|
79
|
+
if (!allowedTargets.includes(targetStateName)) {
|
|
80
|
+
// 미정의 전이 — 구멍 차단.
|
|
81
|
+
return { allowed: false, reason: "transition_not_declared", to: fromStateName, missingEvidence: [] };
|
|
82
|
+
}
|
|
83
|
+
// 증거 guard 검증 (XState transition으로).
|
|
84
|
+
const snapshot = snapshotForState(fromStateName);
|
|
85
|
+
const [next] = transition(getMachine(), snapshot, {
|
|
86
|
+
type: eventForTarget(targetStateName),
|
|
87
|
+
evidence
|
|
88
|
+
} as any);
|
|
89
|
+
const reached = String(next.value) === targetStateName;
|
|
90
|
+
if (reached) {
|
|
91
|
+
return { allowed: true, to: targetStateName, missingEvidence: [] };
|
|
92
|
+
}
|
|
93
|
+
// guard가 막음 — 어떤 증거가 비었는지 산출.
|
|
94
|
+
const evidenceKeys = node.requiredEvidence?.[targetStateName] || [];
|
|
95
|
+
const missingEvidence = evidenceKeys
|
|
96
|
+
.filter(key => !evidence[key])
|
|
97
|
+
.map(key => TICKET_WORKFLOW_EVIDENCE[key]?.missingReason || `${key}_missing`);
|
|
98
|
+
return { allowed: false, reason: "guard_blocked", to: fromStateName, missingEvidence };
|
|
99
|
+
}
|