@dusky-bluehour/agent-service 0.6.6 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -36
- package/catalog/tool-catalog.ko.json +92 -57
- package/claude-code/README.md +75 -30
- package/claude-code/commands/native/cmd-dev-be-api.md +51 -0
- package/claude-code/commands/native/cmd-dev-fe-hook-separate.md +51 -0
- package/claude-code/commands/native/cmd-dev-fe-ui-componentize.md +51 -0
- package/claude-code/commands/native/cmd-dev-perf-optimize.md +51 -0
- package/claude-code/commands/native/cmd-dev-sequential-autorun.md +51 -0
- package/claude-code/commands/native/cmd-doc-handoff.md +52 -0
- package/claude-code/commands/native/cmd-improve-techdebt.md +51 -0
- package/claude-code/commands/native/cmd-incident-triage.md +51 -0
- package/claude-code/commands/native/cmd-ops-ci-cd-gate.md +51 -0
- package/claude-code/commands/native/cmd-ops-deploy.md +51 -0
- package/claude-code/commands/native/cmd-ops-monitoring.md +51 -0
- package/claude-code/commands/native/cmd-plan-arch-decision.md +51 -0
- package/claude-code/commands/native/cmd-plan-implementation-bootstrap.md +51 -0
- package/claude-code/commands/native/cmd-plan-prd-details.md +51 -0
- package/claude-code/commands/native/cmd-plan-prd-master.md +52 -0
- package/claude-code/commands/native/cmd-plan-req-lock.md +52 -0
- package/claude-code/commands/native/cmd-review-code.md +51 -0
- package/claude-code/commands/native/cmd-sec-dependency-audit.md +51 -0
- package/claude-code/commands/native/cmd-sec-threat-model.md +51 -0
- package/claude-code/commands/native/cmd-test-unit-integration.md +51 -0
- package/claude-code/instructions/CLAUDE.template.md +42 -0
- package/claude-code/settings/settings.json +183 -0
- package/claude-code/settings/settings.local.json +10 -0
- package/codex/README.md +58 -24
- package/codex/instructions/AGENTS.permissions.generated.md +121 -0
- package/codex/instructions/AGENTS.template.md +18 -3
- package/codex/settings/runtime-policy.json +188 -0
- package/codex/skills/cmd-dev-be-api/SKILL.md +43 -0
- package/codex/skills/cmd-dev-be-api/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-fe-hook-separate/SKILL.md +43 -0
- package/codex/skills/cmd-dev-fe-hook-separate/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-fe-ui-componentize/SKILL.md +43 -0
- package/codex/skills/cmd-dev-fe-ui-componentize/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-perf-optimize/SKILL.md +43 -0
- package/codex/skills/cmd-dev-perf-optimize/agents/openai.yaml +4 -0
- package/codex/skills/cmd-dev-sequential-autorun/SKILL.md +43 -0
- package/codex/skills/cmd-dev-sequential-autorun/agents/openai.yaml +4 -0
- package/codex/skills/cmd-doc-handoff/SKILL.md +43 -0
- package/codex/skills/cmd-doc-handoff/agents/openai.yaml +4 -0
- package/codex/skills/cmd-improve-techdebt/SKILL.md +43 -0
- package/codex/skills/cmd-improve-techdebt/agents/openai.yaml +4 -0
- package/codex/skills/cmd-incident-triage/SKILL.md +43 -0
- package/codex/skills/cmd-incident-triage/agents/openai.yaml +4 -0
- package/codex/skills/cmd-ops-ci-cd-gate/SKILL.md +43 -0
- package/codex/skills/cmd-ops-ci-cd-gate/agents/openai.yaml +4 -0
- package/codex/skills/cmd-ops-deploy/SKILL.md +43 -0
- package/codex/skills/cmd-ops-deploy/agents/openai.yaml +4 -0
- package/codex/skills/cmd-ops-monitoring/SKILL.md +43 -0
- package/codex/skills/cmd-ops-monitoring/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-arch-decision/SKILL.md +43 -0
- package/codex/skills/cmd-plan-arch-decision/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-implementation-bootstrap/SKILL.md +43 -0
- package/codex/skills/cmd-plan-implementation-bootstrap/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-prd-details/SKILL.md +43 -0
- package/codex/skills/cmd-plan-prd-details/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-prd-master/SKILL.md +44 -0
- package/codex/skills/cmd-plan-prd-master/agents/openai.yaml +4 -0
- package/codex/skills/cmd-plan-req-lock/SKILL.md +44 -0
- package/codex/skills/cmd-plan-req-lock/agents/openai.yaml +4 -0
- package/codex/skills/cmd-review-code/SKILL.md +43 -0
- package/codex/skills/cmd-review-code/agents/openai.yaml +4 -0
- package/codex/skills/cmd-sec-dependency-audit/SKILL.md +43 -0
- package/codex/skills/cmd-sec-dependency-audit/agents/openai.yaml +4 -0
- package/codex/skills/cmd-sec-threat-model/SKILL.md +43 -0
- package/codex/skills/cmd-sec-threat-model/agents/openai.yaml +4 -0
- package/codex/skills/cmd-test-unit-integration/SKILL.md +43 -0
- package/codex/skills/cmd-test-unit-integration/agents/openai.yaml +4 -0
- package/common/settings/security-policy.json +221 -0
- package/common/skills/skill-catalog.json +368 -136
- package/common/workflows/workflow-catalog.json +89 -1238
- package/gemini/README.md +104 -0
- package/gemini/commands/definitions/cmd-dev-be-api.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-fe-hook-separate.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-fe-ui-componentize.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-perf-optimize.toml +35 -0
- package/gemini/commands/definitions/cmd-dev-sequential-autorun.toml +35 -0
- package/gemini/commands/definitions/cmd-doc-handoff.toml +36 -0
- package/gemini/commands/definitions/cmd-improve-techdebt.toml +35 -0
- package/gemini/commands/definitions/cmd-incident-triage.toml +35 -0
- package/gemini/commands/definitions/cmd-ops-ci-cd-gate.toml +35 -0
- package/gemini/commands/definitions/cmd-ops-deploy.toml +35 -0
- package/gemini/commands/definitions/cmd-ops-monitoring.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-arch-decision.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-implementation-bootstrap.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-prd-details.toml +35 -0
- package/gemini/commands/definitions/cmd-plan-prd-master.toml +36 -0
- package/gemini/commands/definitions/cmd-plan-req-lock.toml +36 -0
- package/gemini/commands/definitions/cmd-review-code.toml +35 -0
- package/gemini/commands/definitions/cmd-sec-dependency-audit.toml +35 -0
- package/gemini/commands/definitions/cmd-sec-threat-model.toml +35 -0
- package/gemini/commands/definitions/cmd-test-unit-integration.toml +35 -0
- package/gemini/gemini-extension.json +6 -0
- package/gemini/instructions/GEMINI.template.md +34 -0
- package/gemini/settings/editor-policy.json +193 -0
- package/{antigravity → gemini}/skills/change-safety-review/SKILL.md +8 -6
- package/{antigravity → gemini}/skills/code-review-and-improvement/SKILL.md +8 -3
- package/gemini/skills/frontend-repetition-pack/SKILL.md +44 -0
- package/gemini/skills/incident-response/SKILL.md +44 -0
- package/{antigravity → gemini}/skills/prd-to-production-pipeline/SKILL.md +13 -4
- package/{antigravity → gemini}/skills/release-and-operations/SKILL.md +11 -3
- package/gemini/skills/security-hardening/SKILL.md +43 -0
- package/gemini/skills/service-lifecycle-orchestration/SKILL.md +46 -0
- package/{antigravity → gemini}/workflows/workflow-catalog.json +1 -1
- package/package.json +4 -4
- package/scripts/generate-from-common.mjs +537 -23
- package/scripts/init.mjs +310 -41
- package/scripts/validate.mjs +264 -32
- package/antigravity/README.md +0 -43
- package/antigravity/skills/frontend-repetition-pack/SKILL.md +0 -35
- package/antigravity/skills/incident-response/SKILL.md +0 -35
- package/antigravity/skills/security-hardening/SKILL.md +0 -35
- package/antigravity/skills/service-lifecycle-orchestration/SKILL.md +0 -36
- package/claude-code/workflows/workflow-catalog.json +0 -688
- package/codex/workflows/workflow-catalog.json +0 -450
- /package/{antigravity/agents → common/gemini}/agent-catalog.json +0 -0
- /package/{antigravity/artifacts → common/gemini}/artifact-catalog.json +0 -0
- /package/{common/antigravity → gemini/agents}/agent-catalog.json +0 -0
- /package/{common/antigravity → gemini/artifacts}/artifact-catalog.json +0 -0
- /package/{antigravity → gemini}/commands/command-catalog.json +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-FRONTEND-REFACTOR.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-INCIDENT-RESPONSE.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-PRD-TO-PRODUCTION.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-SECURITY-HARDENING.workflow.yaml +0 -0
- /package/{antigravity → gemini}/workflows/definitions/WF-SERVICE-E2E.workflow.yaml +0 -0
package/scripts/validate.mjs
CHANGED
|
@@ -12,7 +12,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
12
12
|
const __dirname = path.dirname(__filename);
|
|
13
13
|
const rootDir = path.resolve(__dirname, '..');
|
|
14
14
|
|
|
15
|
-
const toolDirs = ['claude-code', 'codex', '
|
|
15
|
+
const toolDirs = ['claude-code', 'codex', 'gemini'];
|
|
16
16
|
const errors = [];
|
|
17
17
|
|
|
18
18
|
function fail(message) {
|
|
@@ -51,6 +51,18 @@ function parseFrontmatter(content) {
|
|
|
51
51
|
return result;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
function normalizeCommandBasename(commandId) {
|
|
55
|
+
const normalized = String(commandId ?? '')
|
|
56
|
+
.trim()
|
|
57
|
+
.toLowerCase()
|
|
58
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
59
|
+
.replace(/^-+|-+$/g, '');
|
|
60
|
+
if (!normalized) {
|
|
61
|
+
throw new Error(`invalid command id: ${commandId}`);
|
|
62
|
+
}
|
|
63
|
+
return normalized;
|
|
64
|
+
}
|
|
65
|
+
|
|
54
66
|
async function validateCatalog() {
|
|
55
67
|
const catalogPath = path.join(rootDir, 'catalog', 'tool-catalog.ko.json');
|
|
56
68
|
if (!(await exists(catalogPath))) {
|
|
@@ -193,10 +205,11 @@ async function validateCommonSources() {
|
|
|
193
205
|
'common/commands/command-catalog.json',
|
|
194
206
|
'common/workflows/workflow-catalog.json',
|
|
195
207
|
'common/skills/skill-catalog.json',
|
|
208
|
+
'common/settings/security-policy.json',
|
|
196
209
|
'common/claude/subagent-catalog.json',
|
|
197
210
|
'common/claude/team-catalog.json',
|
|
198
|
-
'common/
|
|
199
|
-
'common/
|
|
211
|
+
'common/gemini/agent-catalog.json',
|
|
212
|
+
'common/gemini/artifact-catalog.json'
|
|
200
213
|
];
|
|
201
214
|
|
|
202
215
|
const commonData = {};
|
|
@@ -220,10 +233,8 @@ async function validateCommonSources() {
|
|
|
220
233
|
}
|
|
221
234
|
|
|
222
235
|
const toolSet = new Set(Array.isArray(workflowCatalog.tools) ? workflowCatalog.tools : []);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
fail(`[common/workflows] tools에 필수 tool 누락: ${toolId}`);
|
|
226
|
-
}
|
|
236
|
+
if (!toolSet.has('gemini')) {
|
|
237
|
+
fail('[common/workflows] tools에 필수 tool 누락: gemini');
|
|
227
238
|
}
|
|
228
239
|
|
|
229
240
|
if (!workflowCatalog.workflow_policy || typeof workflowCatalog.workflow_policy !== 'object') {
|
|
@@ -358,6 +369,21 @@ async function validateCommonSources() {
|
|
|
358
369
|
fail(`[common/skills] 중단 조건 누락: ${skill.id}`);
|
|
359
370
|
}
|
|
360
371
|
|
|
372
|
+
for (const toolId of toolSet) {
|
|
373
|
+
const procedures = skill.procedures?.[toolId] ?? [];
|
|
374
|
+
for (const step of procedures) {
|
|
375
|
+
if (/\bCMD-[A-Z]/.test(step)) {
|
|
376
|
+
fail(`[common/skills] CMD-* 참조 금지 위반: ${skill.id}/${toolId} → "${step.slice(0, 60)}…"`);
|
|
377
|
+
}
|
|
378
|
+
if (/\bWF-[A-Z]/.test(step)) {
|
|
379
|
+
fail(`[common/skills] WF-* 참조 금지 위반: ${skill.id}/${toolId} → "${step.slice(0, 60)}…"`);
|
|
380
|
+
}
|
|
381
|
+
if (/\bART-[A-Z]/.test(step)) {
|
|
382
|
+
fail(`[common/skills] ART-* 참조 금지 위반: ${skill.id}/${toolId} → "${step.slice(0, 60)}…"`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
361
387
|
if (!skill.codex_openai) {
|
|
362
388
|
fail(`[common/skills] codex_openai 누락: ${skill.id}`);
|
|
363
389
|
} else {
|
|
@@ -369,6 +395,30 @@ async function validateCommonSources() {
|
|
|
369
395
|
}
|
|
370
396
|
}
|
|
371
397
|
}
|
|
398
|
+
|
|
399
|
+
const securityPolicy = commonData['common/settings/security-policy.json'];
|
|
400
|
+
if (securityPolicy) {
|
|
401
|
+
if (!securityPolicy.policy_id || !securityPolicy.shared_permissions) {
|
|
402
|
+
fail('[common/settings] policy_id/shared_permissions 누락');
|
|
403
|
+
}
|
|
404
|
+
const shared = securityPolicy.shared_permissions ?? {};
|
|
405
|
+
for (const field of [
|
|
406
|
+
'allow_bash',
|
|
407
|
+
'ask_bash',
|
|
408
|
+
'deny_bash',
|
|
409
|
+
'deny_read',
|
|
410
|
+
'deny_edit'
|
|
411
|
+
]) {
|
|
412
|
+
if (!Array.isArray(shared[field])) {
|
|
413
|
+
fail(`[common/settings] 배열 누락: ${field}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const claude = securityPolicy.claude_code ?? {};
|
|
418
|
+
if (!claude.default_mode || !claude.disable_bypass_permissions_mode) {
|
|
419
|
+
fail('[common/settings] claude_code default_mode/disable_bypass_permissions_mode 누락');
|
|
420
|
+
}
|
|
421
|
+
}
|
|
372
422
|
}
|
|
373
423
|
|
|
374
424
|
async function validatePackageJson() {
|
|
@@ -407,7 +457,7 @@ async function validatePackageJson() {
|
|
|
407
457
|
const requiredFiles = [
|
|
408
458
|
'common',
|
|
409
459
|
'claude-code',
|
|
410
|
-
'
|
|
460
|
+
'gemini',
|
|
411
461
|
'codex',
|
|
412
462
|
'catalog/tool-catalog.ko.json',
|
|
413
463
|
'scripts/init.mjs',
|
|
@@ -506,7 +556,7 @@ async function validateCommandCatalog(toolName) {
|
|
|
506
556
|
const filePath = path.join(rootDir, toolName, 'commands', 'command-catalog.json');
|
|
507
557
|
if (!(await exists(filePath))) {
|
|
508
558
|
fail(`[${toolName}] command-catalog.json 누락`);
|
|
509
|
-
return { commandIds: new Set() };
|
|
559
|
+
return { commandIds: new Set(), commandFileBases: new Set() };
|
|
510
560
|
}
|
|
511
561
|
|
|
512
562
|
let data;
|
|
@@ -514,12 +564,12 @@ async function validateCommandCatalog(toolName) {
|
|
|
514
564
|
data = await readJson(filePath);
|
|
515
565
|
} catch (error) {
|
|
516
566
|
fail(`[${toolName}] command-catalog.json 파싱 실패: ${error.message}`);
|
|
517
|
-
return { commandIds: new Set() };
|
|
567
|
+
return { commandIds: new Set(), commandFileBases: new Set() };
|
|
518
568
|
}
|
|
519
569
|
|
|
520
570
|
if (!Array.isArray(data.commands) || data.commands.length === 0) {
|
|
521
571
|
fail(`[${toolName}] commands 배열 누락 또는 비어 있음`);
|
|
522
|
-
return { commandIds: new Set() };
|
|
572
|
+
return { commandIds: new Set(), commandFileBases: new Set() };
|
|
523
573
|
}
|
|
524
574
|
|
|
525
575
|
const requiredFields = [
|
|
@@ -536,6 +586,7 @@ async function validateCommandCatalog(toolName) {
|
|
|
536
586
|
];
|
|
537
587
|
|
|
538
588
|
const commandIds = new Set();
|
|
589
|
+
const commandFileBases = new Set();
|
|
539
590
|
for (const cmd of data.commands) {
|
|
540
591
|
for (const field of requiredFields) {
|
|
541
592
|
if (!(field in cmd)) {
|
|
@@ -552,6 +603,11 @@ async function validateCommandCatalog(toolName) {
|
|
|
552
603
|
fail(`[${toolName}] 중복 명령 ID: ${cmd.id}`);
|
|
553
604
|
}
|
|
554
605
|
commandIds.add(cmd.id);
|
|
606
|
+
try {
|
|
607
|
+
commandFileBases.add(normalizeCommandBasename(cmd.id));
|
|
608
|
+
} catch (error) {
|
|
609
|
+
fail(`[${toolName}] 명령 ID 형식 오류: ${cmd.id} (${error.message})`);
|
|
610
|
+
}
|
|
555
611
|
}
|
|
556
612
|
|
|
557
613
|
const repeatedIds = [
|
|
@@ -566,7 +622,7 @@ async function validateCommandCatalog(toolName) {
|
|
|
566
622
|
}
|
|
567
623
|
}
|
|
568
624
|
|
|
569
|
-
return { commandIds };
|
|
625
|
+
return { commandIds, commandFileBases };
|
|
570
626
|
}
|
|
571
627
|
|
|
572
628
|
async function validateWorkflowCatalog(toolName, commandIds) {
|
|
@@ -620,7 +676,7 @@ async function validateWorkflowCatalog(toolName, commandIds) {
|
|
|
620
676
|
}
|
|
621
677
|
}
|
|
622
678
|
|
|
623
|
-
async function validateClaudeExtras() {
|
|
679
|
+
async function validateClaudeExtras(commandFileBases) {
|
|
624
680
|
const subagentsPath = path.join(rootDir, 'claude-code', 'subagents');
|
|
625
681
|
if (!(await exists(subagentsPath))) {
|
|
626
682
|
fail('[claude-code] subagents 디렉터리 누락');
|
|
@@ -645,30 +701,58 @@ async function validateClaudeExtras() {
|
|
|
645
701
|
if (!(await exists(teamCatalog))) {
|
|
646
702
|
fail('[claude-code] team-catalog.json 누락');
|
|
647
703
|
}
|
|
704
|
+
|
|
705
|
+
const commandDir = path.join(rootDir, 'claude-code', 'commands', 'native');
|
|
706
|
+
if (!(await exists(commandDir))) {
|
|
707
|
+
fail('[claude-code] commands/native 디렉터리 누락');
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
const commandFiles = new Set((await fs.readdir(commandDir)).filter((file) => file.endsWith('.md')));
|
|
712
|
+
for (const base of commandFileBases) {
|
|
713
|
+
const expected = `${base}.md`;
|
|
714
|
+
if (!commandFiles.has(expected)) {
|
|
715
|
+
fail(`[claude-code] native command 누락: commands/native/${expected}`);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
async function validateCodexExtras(commandFileBases) {
|
|
721
|
+
const codexSkillDir = path.join(rootDir, 'codex', 'skills');
|
|
722
|
+
for (const base of commandFileBases) {
|
|
723
|
+
const skillFile = path.join(codexSkillDir, base, 'SKILL.md');
|
|
724
|
+
const openAiYaml = path.join(codexSkillDir, base, 'agents', 'openai.yaml');
|
|
725
|
+
if (!(await exists(skillFile))) {
|
|
726
|
+
fail(`[codex] 명령 스킬 누락: skills/${base}/SKILL.md`);
|
|
727
|
+
}
|
|
728
|
+
if (!(await exists(openAiYaml))) {
|
|
729
|
+
fail(`[codex] 명령 스킬 openai.yaml 누락: skills/${base}/agents/openai.yaml`);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
648
732
|
}
|
|
649
733
|
|
|
650
|
-
async function
|
|
651
|
-
const agentCatalog = path.join(rootDir, '
|
|
652
|
-
const artifactCatalog = path.join(rootDir, '
|
|
734
|
+
async function validateGeminiExtras(commandFileBases) {
|
|
735
|
+
const agentCatalog = path.join(rootDir, 'gemini', 'agents', 'agent-catalog.json');
|
|
736
|
+
const artifactCatalog = path.join(rootDir, 'gemini', 'artifacts', 'artifact-catalog.json');
|
|
653
737
|
|
|
654
738
|
for (const f of [agentCatalog, artifactCatalog]) {
|
|
655
739
|
if (!(await exists(f))) {
|
|
656
|
-
fail(`[
|
|
740
|
+
fail(`[gemini] 누락: ${path.relative(rootDir, f)}`);
|
|
657
741
|
continue;
|
|
658
742
|
}
|
|
659
743
|
|
|
660
744
|
try {
|
|
661
745
|
await readJson(f);
|
|
662
746
|
} catch (error) {
|
|
663
|
-
fail(`[
|
|
747
|
+
fail(`[gemini] JSON 파싱 실패: ${path.relative(rootDir, f)} (${error.message})`);
|
|
664
748
|
}
|
|
665
749
|
}
|
|
666
750
|
|
|
667
|
-
const workflowCatalogPath = path.join(rootDir, '
|
|
668
|
-
const workflowDefsDir = path.join(rootDir, '
|
|
751
|
+
const workflowCatalogPath = path.join(rootDir, 'gemini', 'workflows', 'workflow-catalog.json');
|
|
752
|
+
const workflowDefsDir = path.join(rootDir, 'gemini', 'workflows', 'definitions');
|
|
669
753
|
|
|
670
754
|
if (!(await exists(workflowDefsDir))) {
|
|
671
|
-
fail('[
|
|
755
|
+
fail('[gemini] workflow definitions 디렉터리 누락');
|
|
672
756
|
return;
|
|
673
757
|
}
|
|
674
758
|
|
|
@@ -676,7 +760,7 @@ async function validateAntigravityExtras() {
|
|
|
676
760
|
try {
|
|
677
761
|
workflowCatalog = await readJson(workflowCatalogPath);
|
|
678
762
|
} catch (error) {
|
|
679
|
-
fail(`[
|
|
763
|
+
fail(`[gemini] workflow-catalog.json 파싱 실패: ${error.message}`);
|
|
680
764
|
return;
|
|
681
765
|
}
|
|
682
766
|
|
|
@@ -690,7 +774,39 @@ async function validateAntigravityExtras() {
|
|
|
690
774
|
for (const workflowId of workflowIds) {
|
|
691
775
|
const expected = `${workflowId}.workflow.yaml`;
|
|
692
776
|
if (!definitionFiles.includes(expected)) {
|
|
693
|
-
fail(`[
|
|
777
|
+
fail(`[gemini] workflow definition 누락: ${expected}`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
const commandDefinitionsDir = path.join(rootDir, 'gemini', 'commands', 'definitions');
|
|
782
|
+
if (!(await exists(commandDefinitionsDir))) {
|
|
783
|
+
fail('[gemini] commands/definitions 디렉터리 누락');
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const commandDefinitionFiles = new Set(
|
|
788
|
+
(await fs.readdir(commandDefinitionsDir)).filter((file) => file.endsWith('.toml'))
|
|
789
|
+
);
|
|
790
|
+
for (const base of commandFileBases) {
|
|
791
|
+
const expected = `${base}.toml`;
|
|
792
|
+
if (!commandDefinitionFiles.has(expected)) {
|
|
793
|
+
fail(`[gemini] command definition 누락: commands/definitions/${expected}`);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
const extensionJsonPath = path.join(rootDir, 'gemini', 'gemini-extension.json');
|
|
798
|
+
if (!(await exists(extensionJsonPath))) {
|
|
799
|
+
fail('[gemini] gemini-extension.json 누락');
|
|
800
|
+
} else {
|
|
801
|
+
try {
|
|
802
|
+
const extensionData = await readJson(extensionJsonPath);
|
|
803
|
+
for (const field of ['name', 'version', 'description']) {
|
|
804
|
+
if (!extensionData[field]) {
|
|
805
|
+
fail(`[gemini] gemini-extension.json 필수 필드 누락: ${field}`);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
} catch (error) {
|
|
809
|
+
fail(`[gemini] gemini-extension.json 파싱 실패: ${error.message}`);
|
|
694
810
|
}
|
|
695
811
|
}
|
|
696
812
|
}
|
|
@@ -704,6 +820,89 @@ async function validateReadmes() {
|
|
|
704
820
|
}
|
|
705
821
|
}
|
|
706
822
|
|
|
823
|
+
async function validateSettingsOutputs() {
|
|
824
|
+
const settingsFiles = [
|
|
825
|
+
'claude-code/settings/settings.json',
|
|
826
|
+
'claude-code/settings/settings.local.json',
|
|
827
|
+
'gemini/settings/editor-policy.json',
|
|
828
|
+
'codex/settings/runtime-policy.json'
|
|
829
|
+
];
|
|
830
|
+
|
|
831
|
+
for (const relPath of settingsFiles) {
|
|
832
|
+
const fullPath = path.join(rootDir, relPath);
|
|
833
|
+
if (!(await exists(fullPath))) {
|
|
834
|
+
fail(`[settings] 파일 누락: ${relPath}`);
|
|
835
|
+
continue;
|
|
836
|
+
}
|
|
837
|
+
try {
|
|
838
|
+
await readJson(fullPath);
|
|
839
|
+
} catch (error) {
|
|
840
|
+
fail(`[settings] JSON 파싱 실패: ${relPath} (${error.message})`);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const claudeProjectPath = path.join(
|
|
845
|
+
rootDir,
|
|
846
|
+
'claude-code',
|
|
847
|
+
'settings',
|
|
848
|
+
'settings.json'
|
|
849
|
+
);
|
|
850
|
+
if (await exists(claudeProjectPath)) {
|
|
851
|
+
try {
|
|
852
|
+
const claude = await readJson(claudeProjectPath);
|
|
853
|
+
const permissions = claude.permissions ?? {};
|
|
854
|
+
const validModes = new Set([
|
|
855
|
+
'default',
|
|
856
|
+
'acceptEdits',
|
|
857
|
+
'plan',
|
|
858
|
+
'dontAsk',
|
|
859
|
+
'bypassPermissions'
|
|
860
|
+
]);
|
|
861
|
+
if (!validModes.has(permissions.defaultMode)) {
|
|
862
|
+
fail(`[settings] Claude defaultMode 값 오류: ${permissions.defaultMode}`);
|
|
863
|
+
}
|
|
864
|
+
for (const key of ['allow', 'ask', 'deny']) {
|
|
865
|
+
if (!Array.isArray(permissions[key])) {
|
|
866
|
+
fail(`[settings] Claude permissions.${key} 누락`);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
} catch (error) {
|
|
870
|
+
fail(`[settings] Claude 설정 검증 실패: ${error.message}`);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const codexAgentsPermissionPath = path.join(
|
|
875
|
+
rootDir,
|
|
876
|
+
'codex',
|
|
877
|
+
'instructions',
|
|
878
|
+
'AGENTS.permissions.generated.md'
|
|
879
|
+
);
|
|
880
|
+
if (!(await exists(codexAgentsPermissionPath))) {
|
|
881
|
+
fail('[settings] Codex AGENTS.permissions.generated.md 누락');
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
async function validateProjectRuleTemplates() {
|
|
886
|
+
const requiredFiles = [
|
|
887
|
+
'claude-code/instructions/CLAUDE.template.md',
|
|
888
|
+
'codex/instructions/AGENTS.template.md',
|
|
889
|
+
'gemini/instructions/GEMINI.template.md'
|
|
890
|
+
];
|
|
891
|
+
|
|
892
|
+
for (const relPath of requiredFiles) {
|
|
893
|
+
const fullPath = path.join(rootDir, relPath);
|
|
894
|
+
if (!(await exists(fullPath))) {
|
|
895
|
+
fail(`[project-rules] 템플릿 누락: ${relPath}`);
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const content = await fs.readFile(fullPath, 'utf8');
|
|
900
|
+
if (!content.trim()) {
|
|
901
|
+
fail(`[project-rules] 템플릿 비어 있음: ${relPath}`);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
707
906
|
async function runCliSmokeTests() {
|
|
708
907
|
const nodeBin = process.execPath;
|
|
709
908
|
const cliPath = path.join(rootDir, 'scripts', 'init.mjs');
|
|
@@ -723,7 +922,7 @@ async function runCliSmokeTests() {
|
|
|
723
922
|
'--tool',
|
|
724
923
|
'codex',
|
|
725
924
|
'--components',
|
|
726
|
-
'skills,
|
|
925
|
+
'skills,commands',
|
|
727
926
|
'--target',
|
|
728
927
|
'/tmp/tri-agent-manager-validate',
|
|
729
928
|
'--dry-run',
|
|
@@ -765,7 +964,7 @@ async function runCliSmokeTests() {
|
|
|
765
964
|
'--tool',
|
|
766
965
|
'codex',
|
|
767
966
|
'--components',
|
|
768
|
-
'skills,
|
|
967
|
+
'skills,commands',
|
|
769
968
|
'--target',
|
|
770
969
|
'/tmp/tri-agent-manager-validate',
|
|
771
970
|
'--dry-run',
|
|
@@ -785,11 +984,11 @@ async function runCliSmokeTests() {
|
|
|
785
984
|
cliPath,
|
|
786
985
|
'install',
|
|
787
986
|
'--tool',
|
|
788
|
-
'
|
|
987
|
+
'gemini',
|
|
789
988
|
'--components',
|
|
790
989
|
'skills,workflows',
|
|
791
990
|
'--install-root',
|
|
792
|
-
'
|
|
991
|
+
'gemini=.gemini-custom',
|
|
793
992
|
'--target',
|
|
794
993
|
'/tmp/tri-agent-manager-validate',
|
|
795
994
|
'--dry-run',
|
|
@@ -802,6 +1001,30 @@ async function runCliSmokeTests() {
|
|
|
802
1001
|
fail(`[cli] install --install-root dry-run 실행 실패: ${error.message}`);
|
|
803
1002
|
}
|
|
804
1003
|
|
|
1004
|
+
try {
|
|
1005
|
+
await execFileAsync(
|
|
1006
|
+
nodeBin,
|
|
1007
|
+
[
|
|
1008
|
+
cliPath,
|
|
1009
|
+
'install',
|
|
1010
|
+
'--tool',
|
|
1011
|
+
'gemini',
|
|
1012
|
+
'--components',
|
|
1013
|
+
'skills',
|
|
1014
|
+
'--project-rules',
|
|
1015
|
+
'if-instructions',
|
|
1016
|
+
'--target',
|
|
1017
|
+
'/tmp/tri-agent-manager-validate',
|
|
1018
|
+
'--dry-run',
|
|
1019
|
+
'--yes',
|
|
1020
|
+
'--non-interactive'
|
|
1021
|
+
],
|
|
1022
|
+
{ cwd: rootDir }
|
|
1023
|
+
);
|
|
1024
|
+
} catch (error) {
|
|
1025
|
+
fail(`[cli] install --project-rules dry-run 실행 실패: ${error.message}`);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
805
1028
|
try {
|
|
806
1029
|
await execFileAsync(
|
|
807
1030
|
nodeBin,
|
|
@@ -851,7 +1074,7 @@ async function runCliSmokeTests() {
|
|
|
851
1074
|
'--tool',
|
|
852
1075
|
'codex',
|
|
853
1076
|
'--components',
|
|
854
|
-
'skills,
|
|
1077
|
+
'skills,commands',
|
|
855
1078
|
'--target',
|
|
856
1079
|
'/tmp/tri-agent-manager-validate',
|
|
857
1080
|
'--dry-run',
|
|
@@ -870,15 +1093,24 @@ async function main() {
|
|
|
870
1093
|
await validateCommonSources();
|
|
871
1094
|
await validateCatalog();
|
|
872
1095
|
await validateReadmes();
|
|
1096
|
+
await validateSettingsOutputs();
|
|
1097
|
+
await validateProjectRuleTemplates();
|
|
873
1098
|
|
|
1099
|
+
const commandFileBaseByTool = {};
|
|
1100
|
+
let geminiCommandIds = new Set();
|
|
874
1101
|
for (const toolName of toolDirs) {
|
|
875
1102
|
await validateSkillDirectory(toolName);
|
|
876
|
-
const { commandIds } = await validateCommandCatalog(toolName);
|
|
877
|
-
|
|
1103
|
+
const { commandIds, commandFileBases } = await validateCommandCatalog(toolName);
|
|
1104
|
+
commandFileBaseByTool[toolName] = commandFileBases;
|
|
1105
|
+
if (toolName === 'gemini') {
|
|
1106
|
+
geminiCommandIds = commandIds;
|
|
1107
|
+
}
|
|
878
1108
|
}
|
|
1109
|
+
await validateWorkflowCatalog('gemini', geminiCommandIds);
|
|
879
1110
|
|
|
880
|
-
await validateClaudeExtras();
|
|
881
|
-
await
|
|
1111
|
+
await validateClaudeExtras(commandFileBaseByTool['claude-code'] ?? new Set());
|
|
1112
|
+
await validateCodexExtras(commandFileBaseByTool.codex ?? new Set());
|
|
1113
|
+
await validateGeminiExtras(commandFileBaseByTool.gemini ?? new Set());
|
|
882
1114
|
await runCliSmokeTests();
|
|
883
1115
|
|
|
884
1116
|
if (errors.length > 0) {
|
package/antigravity/README.md
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# Antigravity 운영팩
|
|
2
|
-
|
|
3
|
-
이 폴더는 Antigravity의 Manager-에이전트-Artifact 흐름을 기준으로, 서비스 운영 전체를 실행 가능한 형태로 정리한 패키지입니다.
|
|
4
|
-
정의 원천은 저장소 `common/*`이며, 이 폴더의 파일은 생성 스크립트(`npm run generate`) 결과입니다.
|
|
5
|
-
직접 수정 대신 공통 원천을 수정한 뒤 재생성해 주세요.
|
|
6
|
-
|
|
7
|
-
## 폴더 구성
|
|
8
|
-
|
|
9
|
-
- `skills/`: 역할별 실행 스킬
|
|
10
|
-
- `agents/agent-catalog.json`: 에이전트 역할 카탈로그
|
|
11
|
-
- `artifacts/artifact-catalog.json`: 단계별 Artifact 계약
|
|
12
|
-
- `workflows/workflow-catalog.json`: 오케스트레이션 워크플로우
|
|
13
|
-
- `workflows/definitions/*.workflow.yaml`: Antigravity 실행용 워크플로우 정의 파일
|
|
14
|
-
- `commands/command-catalog.json`: 공통 명령 계약
|
|
15
|
-
|
|
16
|
-
## 빠른 시작
|
|
17
|
-
|
|
18
|
-
1. `WF-SERVICE-E2E`(기본 E2E), `WF-SECURITY-HARDENING`(보안), `WF-INCIDENT-RESPONSE`(장애 대응), `WF-PRD-TO-PRODUCTION`(PRD→구현) 등 목적별 워크플로우를 선택해 주세요.
|
|
19
|
-
2. 각 단계의 `input_artifact` 필드를 먼저 검증해 주세요.
|
|
20
|
-
3. 지정된 `agent`가 `commands`를 수행합니다.
|
|
21
|
-
4. `output_artifact` 검증 후 다음 단계로 진행해 주세요.
|
|
22
|
-
|
|
23
|
-
워크플로우 카탈로그에서 선택한 ID와 동일한 이름의 정의 파일을 `workflows/definitions/`에서 사용하시면 됩니다. 예: `WF-SERVICE-E2E.workflow.yaml`
|
|
24
|
-
|
|
25
|
-
## 반복 작업 역할 분리
|
|
26
|
-
|
|
27
|
-
- UI 컴포넌트화: `frontend-repetition-agent` + `CMD-DEV-FE-UI-COMPONENTIZE`
|
|
28
|
-
- Hook 분리: `frontend-repetition-agent` + `CMD-DEV-FE-HOOK-SEPARATE`
|
|
29
|
-
- 성능 최적화: `performance-agent` + `CMD-DEV-PERF-OPTIMIZE`
|
|
30
|
-
- 변경점 안전 검토: `change-safety-review` + `CMD-TEST-UNIT-INTEGRATION`, `CMD-REVIEW-CODE`
|
|
31
|
-
- PRD 작성: `prd-writer-agent`
|
|
32
|
-
|
|
33
|
-
## 운영 규칙
|
|
34
|
-
|
|
35
|
-
- Manager 에이전트가 단계 전환 권한을 가집니다.
|
|
36
|
-
- Artifact 검증 실패 시 단계 중단 후 원인 수정을 우선해 주세요.
|
|
37
|
-
- 보안/배포 단계는 우회 없이 게이트를 통과해야 합니다.
|
|
38
|
-
|
|
39
|
-
## 참고 문서 (검증일: 2026-02-17)
|
|
40
|
-
|
|
41
|
-
- [Google Antigravity 발표](https://developers.googleblog.com/build-with-google-antigravity-our-new-agentic-development-platform/)
|
|
42
|
-
- [Antigravity 공식 문서](https://antigravity.dev/docs)
|
|
43
|
-
- [Antigravity 워크플로우 문서](https://antigravity.im/documentation)
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: frontend-repetition-pack
|
|
3
|
-
description: UI 컴포넌트화, Hook 분리, 성능 최적화를 표준 단계로 실행한다. 반복 프론트엔드 개선 작업에서 사용한다.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 시작 전 체크리스트
|
|
7
|
-
|
|
8
|
-
- 중복 UI 후보 목록을 수집한다 (화면/컴포넌트/사용 횟수).
|
|
9
|
-
- 기준 지표를 기록한다 (번들 크기, 렌더 횟수, 핵심 UX 지연 시간).
|
|
10
|
-
- 디자인 토큰/컴포넌트 규칙을 확인한다.
|
|
11
|
-
- 상태 로직 분리 대상(부수효과/비동기/공유 상태)을 표시한다.
|
|
12
|
-
|
|
13
|
-
# 실행 절차
|
|
14
|
-
|
|
15
|
-
1. `CMD-DEV-FE-UI-COMPONENTIZE`
|
|
16
|
-
2. `CMD-DEV-FE-HOOK-SEPARATE`
|
|
17
|
-
3. `CMD-DEV-PERF-OPTIMIZE`
|
|
18
|
-
|
|
19
|
-
# 결과 보고 형식
|
|
20
|
-
|
|
21
|
-
- 컴포넌트화 결과: 기존->신규 매핑, 공통 Props 계약
|
|
22
|
-
- Hook 분리 결과: Hook 이름, 입력/출력, 부수효과 정책
|
|
23
|
-
- 성능 결과: 전/후 지표와 측정 조건
|
|
24
|
-
- 남은 리팩터 항목: 우선순위와 예상 영향
|
|
25
|
-
|
|
26
|
-
# 중단 조건
|
|
27
|
-
|
|
28
|
-
- 시각적 회귀가 차단 이슈 수준으로 발생한 경우
|
|
29
|
-
- 컴포넌트 계약이 확정되지 않아 호출부 정리가 불가능한 경우
|
|
30
|
-
- 전/후 성능 비교가 불가능해 개선 효과를 증명할 수 없는 경우
|
|
31
|
-
|
|
32
|
-
# 품질 규칙
|
|
33
|
-
|
|
34
|
-
- 단계별 전후 지표를 Artifact에 남긴다.
|
|
35
|
-
- 계약 없는 리팩터링 금지
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: incident-response
|
|
3
|
-
description: 장애 대응, 근본 원인 수정, 재발 방지까지 수행한다. 운영 장애 및 핫픽스 대응에서 사용한다.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 시작 전 체크리스트
|
|
7
|
-
|
|
8
|
-
- 장애 심각도(SEV), 영향 범위, 시작 시각을 확정한다.
|
|
9
|
-
- 즉시 복구 경로(롤백/우회/트래픽 차단)를 준비한다.
|
|
10
|
-
- 커뮤니케이션 채널과 의사결정 책임자를 지정한다.
|
|
11
|
-
- 로그/메트릭/알림 스냅샷을 보존한다.
|
|
12
|
-
|
|
13
|
-
# 실행 절차
|
|
14
|
-
|
|
15
|
-
1. `CMD-INCIDENT-TRIAGE`로 복구 우선
|
|
16
|
-
2. `CMD-IMPROVE-TECHDEBT`로 근본 원인 보완
|
|
17
|
-
3. `CMD-DOC-HANDOFF`로 재발 방지 문서화
|
|
18
|
-
|
|
19
|
-
# 결과 보고 형식
|
|
20
|
-
|
|
21
|
-
- 타임라인: 탐지->완화->복구 시각
|
|
22
|
-
- 복구 조치: 실행 명령/설정 변경/결과
|
|
23
|
-
- 근본 원인: 기술 원인, 프로세스 원인
|
|
24
|
-
- 재발 방지 액션: 담당자, 마감일, 검증 방법
|
|
25
|
-
|
|
26
|
-
# 중단 조건
|
|
27
|
-
|
|
28
|
-
- 고객 영향이 지속되는데 복구 경로가 없는 경우
|
|
29
|
-
- 데이터 손상 가능성이 있어 즉시 격리가 필요한 경우
|
|
30
|
-
- 재발 방지 액션의 책임자/기한이 확정되지 않은 경우
|
|
31
|
-
|
|
32
|
-
# 품질 규칙
|
|
33
|
-
|
|
34
|
-
- RCA와 재발 방지 액션 없는 종료 금지
|
|
35
|
-
- 복구와 개선을 분리 보고
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: security-hardening
|
|
3
|
-
description: 위협 모델링과 취약점 감사를 수행하고 보완 및 재검증까지 실행한다. 보안 강화 요청에서 사용한다.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 시작 전 체크리스트
|
|
7
|
-
|
|
8
|
-
- 신뢰 경계와 보호 대상 자산을 확정한다.
|
|
9
|
-
- 의존성 목록과 현재 취약점 현황을 수집한다.
|
|
10
|
-
- 위협 모델링 범위(인증/인가/데이터/운영)를 고정한다.
|
|
11
|
-
- 예외 승인 정책(책임자, 만료일)을 준비한다.
|
|
12
|
-
|
|
13
|
-
# 실행 절차
|
|
14
|
-
|
|
15
|
-
1. `WF-SECURITY-HARDENING` 실행
|
|
16
|
-
2. `CMD-SEC-THREAT-MODEL`, `CMD-SEC-DEPENDENCY-AUDIT` 수행
|
|
17
|
-
3. 보완 반영 후 테스트/리뷰 재실행
|
|
18
|
-
|
|
19
|
-
# 결과 보고 형식
|
|
20
|
-
|
|
21
|
-
- 위협/취약점 목록: 심각도, 영향, 우선순위
|
|
22
|
-
- 보완 적용 내역: 코드/설정 변경과 근거
|
|
23
|
-
- 재검증 결과: 스캔/테스트/리뷰 결과
|
|
24
|
-
- 잔여 위험 및 예외 승인: 책임자, 만료일
|
|
25
|
-
|
|
26
|
-
# 중단 조건
|
|
27
|
-
|
|
28
|
-
- 치명 취약점이 남아 있는데 배포를 진행하려는 경우
|
|
29
|
-
- 고위험 위협에 대한 완화 통제가 정의되지 않은 경우
|
|
30
|
-
- 예외 승인이 만료일 없이 요청된 경우
|
|
31
|
-
|
|
32
|
-
# 품질 규칙
|
|
33
|
-
|
|
34
|
-
- 치명 취약점 0건 전까지 종료 금지
|
|
35
|
-
- 예외 수용은 만료일 포함
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: service-lifecycle-orchestration
|
|
3
|
-
description: Plan-Implement-Test-Review-Release 흐름을 Manager 중심으로 실행한다. 신규 서비스 구축 및 대형 기능 출시에 사용한다.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# 시작 전 체크리스트
|
|
7
|
-
|
|
8
|
-
- 요구사항/ADR/범위/비범위를 확정한다.
|
|
9
|
-
- 각 단계 책임 역할과 승인자를 지정한다.
|
|
10
|
-
- 단계별 진입/종료 기준을 작업 시작 전에 확인한다.
|
|
11
|
-
- 리스크와 롤백 전략을 초기 단계에서 기록한다.
|
|
12
|
-
|
|
13
|
-
# 실행 절차
|
|
14
|
-
|
|
15
|
-
1. `WF-SERVICE-E2E`를 선택한다.
|
|
16
|
-
2. 단계별 입력 Artifact 스키마를 검증한다.
|
|
17
|
-
3. 해당 단계 에이전트가 명령 ID를 수행한다.
|
|
18
|
-
4. 출력 Artifact 검증 후 다음 단계로 진행한다.
|
|
19
|
-
|
|
20
|
-
# 결과 보고 형식
|
|
21
|
-
|
|
22
|
-
- 단계 상태: 완료/미완료/차단요인
|
|
23
|
-
- 실행 근거: 단계별 명령 ID와 결과
|
|
24
|
-
- 품질 상태: 테스트/보안/리뷰 게이트 결과
|
|
25
|
-
- 다음 행동: 담당자, 마감 조건, 선행 조건
|
|
26
|
-
|
|
27
|
-
# 중단 조건
|
|
28
|
-
|
|
29
|
-
- 진입 기준이 충족되지 않은 단계가 있는 경우
|
|
30
|
-
- 차단 이슈가 해결되지 않았는데 다음 단계로 이동하려는 경우
|
|
31
|
-
- 롤백 전략 없이 배포 단계 진입이 요청된 경우
|
|
32
|
-
|
|
33
|
-
# 품질 규칙
|
|
34
|
-
|
|
35
|
-
- Artifact 검증 실패 시 단계 진행 금지
|
|
36
|
-
- command ID 없는 임의 실행 금지
|