ワンクリックで
query-sub-agent
專責處理 IDF (Information Display Frame) 類型的需求。讀取規格目錄結構,生成/審查 Query Side 設計與實作。支援 Java、TypeScript、Go 多語言。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
專責處理 IDF (Information Display Frame) 類型的需求。讀取規格目錄結構,生成/審查 Query Side 設計與實作。支援 Java、TypeScript、Go 多語言。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Automated code review that checks code quality, Clean Architecture compliance, DDD patterns, coding standards, and spec traceability.
從規格目錄的 acceptance.yaml 生成/維護 BDD/ezSpec 測試。使用類似 Gherkin 語法,AI 自動產生 step definition(開發人員不需要手寫),驗收測試規格即為 Executable Specification。
在單元測試通過後觸發。透過引入人工錯誤(Mutants)來「測試你的測試」,確保測試案例具有足夠的錯誤偵測能力,建立對驗證機制的信任(Trust the Verification)。
驗證規格檔案的完整性與一致性,確保所有必要的規格元素都已定義且符合標準格式。
Replace with description of the skill and when Claude should use it.
協調多個 AI 模型(ChatGPT、Gemini、Codex、QWEN、Claude)進行三角驗證,確保「Specification == Program == Test」一致性。過濾假警報後輸出報告,大幅減少人工介入時間。
| name | query-sub-agent |
| description | 專責處理 IDF (Information Display Frame) 類型的需求。讀取規格目錄結構,生成/審查 Query Side 設計與實作。支援 Java、TypeScript、Go 多語言。 |
本 Skill 讀取以下規格檔案:
docs/specs/{feature-name}/
├── frame.yaml # 讀取 frame_concerns
├── requirements/ # 讀取查詢需求
│ └── req-{n}-{feature}.yaml
├── machine/ # 讀取 Query 規格
│ ├── query.yaml # Query Handler 規格
│ └── read-model.yaml # Read Model 規格
└── cross-context/ # 若需跨 BC 查詢
└── {context}.yaml
# docs/specs/{feature-name}/machine/query.yaml
query:
name: "{FeatureName}Query"
type: "single" # | list | paginated | aggregated
# Input 定義
input:
name: "{FeatureName}QueryInput"
fields:
- name: "id"
type: "string"
required: true
# 分頁參數 (若 type=paginated)
- name: "page"
type: "number"
default: 1
- name: "pageSize"
type: "number"
default: 20
# Output 定義
output:
name: "{FeatureName}QueryOutput"
type: "single" # | list | paginated
fields:
- name: "id"
type: "string"
- name: "name"
type: "string"
# 分頁輸出 (若 type=paginated)
pagination:
total: "number"
page: "number"
pageSize: "number"
hasNext: "boolean"
# 快取策略
caching:
enabled: true
ttl: "5m"
key_pattern: "{feature}:{id}"
invalidation:
- on_event: "{AggregateUpdatedEvent}"
# 效能約束
performance:
max_response_time: "100ms"
max_items_per_page: 100
saga-orchestrator → runSubagent → query-sub-agent
├── 讀取規格目錄
├── 套用 coding-standards
└── 輸出 Query Side 代碼
task:
type: "query"
spec_dir: "docs/specs/get-workflow/"
language: "typescript"
output_paths:
queries: "src/application/queries/"
read_models: "src/infrastructure/read-models/"
// src/application/queries/GetWorkflowByIdQuery.ts
// Generated from: docs/specs/get-workflow/machine/query.yaml
import { WorkflowReadModel } from '@/infrastructure/read-models/WorkflowReadModel';
import { CacheService } from '@/infrastructure/cache/CacheService';
// ===== Input/Output (from query.yaml) =====
export interface GetWorkflowByIdInput {
readonly workflowId: string;
}
export interface GetWorkflowByIdOutput {
readonly id: string;
readonly boardId: string;
readonly name: string;
readonly stages: readonly StageView[];
readonly status: string;
readonly createdAt: Date;
}
// ===== Query Handler =====
export class GetWorkflowByIdQuery {
constructor(
private readonly readModel: WorkflowReadModel,
private readonly cache: CacheService,
) {}
async execute(input: GetWorkflowByIdInput): Promise<GetWorkflowByIdOutput | null> {
// ===== Pre-conditions =====
if (!input.workflowId) {
throw new ValidationError('workflowId is required');
}
// ===== Caching (from query.yaml#caching) =====
const cacheKey = `workflow:${input.workflowId}`;
const cached = await this.cache.get<GetWorkflowByIdOutput>(cacheKey);
if (cached) {
return cached;
}
// ===== Query Read Model =====
const result = await this.readModel.findById(input.workflowId);
if (result) {
// Cache for 5 minutes (from query.yaml#caching.ttl)
await this.cache.set(cacheKey, result, { ttl: 300 });
}
return result;
}
}
// src/application/queries/ListWorkflowsQuery.ts
export interface ListWorkflowsInput {
readonly boardId: string;
readonly page?: number;
readonly pageSize?: number;
}
export interface ListWorkflowsOutput {
readonly items: readonly WorkflowSummary[];
readonly pagination: {
readonly total: number;
readonly page: number;
readonly pageSize: number;
readonly hasNext: boolean;
};
}
export class ListWorkflowsQuery {
constructor(
private readonly readModel: WorkflowReadModel,
) {}
async execute(input: ListWorkflowsInput): Promise<ListWorkflowsOutput> {
const page = input.page ?? 1;
const pageSize = Math.min(input.pageSize ?? 20, 100); // Max 100 items
const offset = (page - 1) * pageSize;
const [items, total] = await Promise.all([
this.readModel.findByBoardId(input.boardId, { offset, limit: pageSize }),
this.readModel.countByBoardId(input.boardId),
]);
return {
items,
pagination: {
total,
page,
pageSize,
hasNext: offset + items.length < total,
},
};
}
}
// src/infrastructure/read-models/WorkflowReadModel.ts
export interface WorkflowReadModel {
findById(id: string): Promise<WorkflowView | null>;
findByBoardId(boardId: string, options: PaginationOptions): Promise<WorkflowSummary[]>;
countByBoardId(boardId: string): Promise<number>;
}
// Implementation with optimized queries
export class PostgresWorkflowReadModel implements WorkflowReadModel {
constructor(private readonly db: Database) {}
async findById(id: string): Promise<WorkflowView | null> {
// Optimized query with joins for stages
const result = await this.db.query(`
SELECT w.*,
json_agg(s.*) as stages
FROM workflows w
LEFT JOIN stages s ON s.workflow_id = w.id
WHERE w.id = $1
GROUP BY w.id
`, [id]);
return result.rows[0] ?? null;
}
async findByBoardId(
boardId: string,
options: PaginationOptions
): Promise<WorkflowSummary[]> {
// Summary query without heavy joins
const result = await this.db.query(`
SELECT id, name, status, created_at,
(SELECT COUNT(*) FROM stages WHERE workflow_id = w.id) as stage_count
FROM workflows w
WHERE board_id = $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3
`, [boardId, options.limit, options.offset]);
return result.rows;
}
}
// src/application/query/get_workflow_by_id.go
package query
import (
"context"
"time"
"myapp/infrastructure/cache"
"myapp/infrastructure/readmodel"
)
type GetWorkflowByIdInput struct {
WorkflowID string `json:"workflow_id" validate:"required,uuid"`
}
type GetWorkflowByIdOutput struct {
ID string `json:"id"`
BoardID string `json:"board_id"`
Name string `json:"name"`
Stages []StageView `json:"stages"`
Status string `json:"status"`
CreatedAt time.Time `json:"created_at"`
}
type GetWorkflowByIdQuery struct {
readModel readmodel.WorkflowReadModel
cache cache.CacheService
}
func NewGetWorkflowByIdQuery(
rm readmodel.WorkflowReadModel,
c cache.CacheService,
) *GetWorkflowByIdQuery {
return &GetWorkflowByIdQuery{readModel: rm, cache: c}
}
func (q *GetWorkflowByIdQuery) Execute(
ctx context.Context,
input GetWorkflowByIdInput,
) (*GetWorkflowByIdOutput, error) {
// ===== Pre-conditions =====
if err := validate.Struct(input); err != nil {
return nil, err
}
// ===== Caching =====
cacheKey := fmt.Sprintf("workflow:%s", input.WorkflowID)
if cached, err := q.cache.Get(ctx, cacheKey); err == nil && cached != nil {
return cached.(*GetWorkflowByIdOutput), nil
}
// ===== Query Read Model =====
result, err := q.readModel.FindByID(ctx, input.WorkflowID)
if err != nil {
return nil, err
}
if result != nil {
// Cache for 5 minutes
_ = q.cache.Set(ctx, cacheKey, result, 5*time.Minute)
}
return result, nil
}
當 Domain Event 發生時,自動失效相關快取:
// src/infrastructure/cache/WorkflowCacheInvalidator.ts
export class WorkflowCacheInvalidator {
constructor(private readonly cache: CacheService) {}
@OnEvent('WorkflowCreatedEvent')
@OnEvent('WorkflowUpdatedEvent')
async invalidate(event: WorkflowEvent): void {
// Invalidate single item cache
await this.cache.delete(`workflow:${event.workflowId}`);
// Invalidate list cache for the board
await this.cache.deletePattern(`workflows:board:${event.boardId}:*`);
}
}