Coding
PromptBeginner5 minmarkdown
Markdown Converter
Agent skill for markdown-converter
7
專責處理 CBF (Commanded Behavior Frame) 類型的需求。讀取規格目錄結構,生成/審查 Command Side 設計與實作。支援 Java、TypeScript、Go 多語言。
Sign in to like and favorite skills
本 Skill 讀取以下規格檔案:
docs/specs/{feature-name}/ ├── frame.yaml # 讀取 frame_concerns, cross_context_dependencies ├── requirements/ # 讀取業務規則 │ └── req-{n}-{feature}.yaml ├── machine/ # 讀取 Application 層規格 │ ├── controller.yaml │ └── use-case.yaml ├── controlled-domain/ # 讀取 Domain 層規格 │ └── aggregate.yaml └── cross-context/ # 讀取跨 BC 依賴 └── {context}.yaml
本 Skill 可作為
runSubagent 的任務目標:
saga-orchestrator → runSubagent → command-sub-agent ├── 讀取規格目錄 ├── 套用 coding-standards ├── 套用 enforce-contract └── 輸出 Command Side 代碼
task: type: "command" spec_dir: "docs/specs/create-workflow/" language: "typescript" # | java | go output_paths: application: "src/application/use-cases/" domain: "src/domain/"
# 從 frame.yaml 讀取 problem_frame: "CreateWorkflow" frame_type: CommandedBehaviorFrame # 識別 Frame Concerns 需要滿足 frame_concerns: - FC1: Structure Integrity → 對應 aggregate.yaml#invariants - FC2: Concurrency → 對應 use-case.yaml#transaction_boundary # 識別跨 BC 依賴 cross_context_dependencies: - XC1: Authorization → 需整合 ACL
從
machine/use-case.yaml 生成 Application 層代碼:
從
controlled-domain/aggregate.yaml 生成 Domain 層代碼:
從
cross-context/{context}.yaml 整合 ACL:
// src/application/use-cases/CreateWorkflowUseCase.ts // Generated from: docs/specs/create-workflow/machine/use-case.yaml import { AuthorizationService } from '@/domain/services/AuthorizationService'; import { WorkflowRepository } from '@/domain/repositories/WorkflowRepository'; import { EventPublisher } from '@/domain/events/EventPublisher'; import { Workflow } from '@/domain/aggregates/Workflow'; import { WorkflowCreatedEvent } from '@/domain/events/WorkflowCreatedEvent'; // ===== Input/Output (from use-case.yaml#input/output) ===== export interface CreateWorkflowInput { readonly boardId: string; readonly name: string; readonly operatorId: string; } export interface CreateWorkflowOutput { readonly workflowId: string; readonly status: WorkflowStatus; readonly createdAt: Date; } // ===== Use Case ===== export class CreateWorkflowUseCase { constructor( // XC1: Authorization dependency (from cross-context/authorization.yaml) private readonly authorizationService: AuthorizationService, private readonly workflowRepository: WorkflowRepository, private readonly eventPublisher: EventPublisher, ) {} async execute(input: CreateWorkflowInput): Promise<CreateWorkflowOutput> { // ===== Pre-conditions (from use-case.yaml#contracts.pre_conditions) ===== if (!input.boardId) { throw new ValidationError('boardId is required'); } if (!input.name) { throw new ValidationError('name is required'); } if (!input.operatorId) { throw new ValidationError('operatorId is required'); } // ===== XC1: Authorization Check (from cross-context/authorization.yaml) ===== const authResult = await this.authorizationService.canExecute( input.operatorId, 'create', 'Workflow', input.boardId, ); if (!authResult.authorized) { throw new UnauthorizedError(`Not authorized: ${authResult.reason}`); } // ===== Domain Logic (from controlled-domain/aggregate.yaml) ===== const workflow = Workflow.create({ boardId: new BoardId(input.boardId), name: new WorkflowName(input.name), createdBy: new UserId(input.operatorId), }); // ===== Persist ===== await this.workflowRepository.save(workflow); // ===== Publish Domain Event (from use-case.yaml#publishes_events) ===== await this.eventPublisher.publish(new WorkflowCreatedEvent({ workflowId: workflow.id.value, boardId: workflow.boardId.value, name: workflow.name.value, createdBy: workflow.createdBy.value, createdAt: workflow.createdAt, })); // ===== Post-conditions (from use-case.yaml#contracts.post_conditions) ===== // POST1: result.workflowId is not null - enforced by return type return { workflowId: workflow.id.value, status: workflow.status, createdAt: workflow.createdAt, }; } }
// src/domain/aggregates/Workflow.ts // Generated from: docs/specs/create-workflow/controlled-domain/aggregate.yaml import { WorkflowId } from '../value-objects/WorkflowId'; import { WorkflowName } from '../value-objects/WorkflowName'; import { BoardId } from '../value-objects/BoardId'; import { Stage } from '../entities/Stage'; export class Workflow { // ===== Identity (from aggregate.yaml#identity) ===== readonly id: WorkflowId; // ===== Properties (from aggregate.yaml#properties) ===== readonly boardId: BoardId; private _name: WorkflowName; private _stages: Stage[] = []; readonly createdBy: UserId; readonly createdAt: Date; private constructor(props: WorkflowProps) { this.id = props.id; this.boardId = props.boardId; this._name = props.name; this.createdBy = props.createdBy; this.createdAt = props.createdAt; // ===== INV1: Enforce invariants in constructor (from aggregate.yaml#invariants) ===== this.validateInvariants(); } static create(props: CreateWorkflowProps): Workflow { return new Workflow({ id: WorkflowId.generate(), boardId: props.boardId, name: props.name, createdBy: props.createdBy, createdAt: new Date(), }); } // ===== Mutating Methods ===== addStage(stage: Stage): void { // ===== FC1: Structure Integrity - Lane Hierarchy (from frame_concerns) ===== // Stage may be root or nested, validated in Stage entity this._stages.push(stage); // Re-validate invariants after mutation this.validateInvariants(); } // ===== Invariants Validation (from aggregate.yaml#invariants.shared) ===== private validateInvariants(): void { // INV1: Structure Integrity for (const stage of this._stages) { for (const lane of stage.swimLanes) { // SwimLane cannot exist at root; it must be under a Stage if (!lane.parentStage) { throw new InvariantViolationError( 'SwimLane must be under a Stage' ); } } } } // ===== Getters ===== get name(): WorkflowName { return this._name; } get stages(): readonly Stage[] { return [...this._stages]; } get status(): WorkflowStatus { return this._stages.length > 0 ? WorkflowStatus.Active : WorkflowStatus.Empty; } }
// src/application/usecase/create_workflow.go // Generated from: docs/specs/create-workflow/machine/use-case.yaml package usecase import ( "context" "time" "myapp/domain/aggregates" "myapp/domain/events" "myapp/domain/services" "myapp/domain/valueobjects" ) // ===== Input/Output (from use-case.yaml) ===== type CreateWorkflowInput struct { BoardID string `json:"board_id" validate:"required,uuid"` Name string `json:"name" validate:"required,min=1,max=100"` OperatorID string `json:"operator_id" validate:"required,uuid"` } type CreateWorkflowOutput struct { WorkflowID string `json:"workflow_id"` Status string `json:"status"` CreatedAt time.Time `json:"created_at"` } // ===== Use Case ===== type CreateWorkflowUseCase struct { authService services.AuthorizationService // XC1 workflowRepo aggregates.WorkflowRepository eventPub events.EventPublisher } func NewCreateWorkflowUseCase( authService services.AuthorizationService, workflowRepo aggregates.WorkflowRepository, eventPub events.EventPublisher, ) *CreateWorkflowUseCase { return &CreateWorkflowUseCase{ authService: authService, workflowRepo: workflowRepo, eventPub: eventPub, } } func (uc *CreateWorkflowUseCase) Execute( ctx context.Context, input CreateWorkflowInput, ) (*CreateWorkflowOutput, error) { // ===== Pre-conditions (from use-case.yaml#contracts.pre_conditions) ===== if err := ValidateInput(input); err != nil { return nil, err } // ===== XC1: Authorization Check ===== authResult, err := uc.authService.CanExecute( ctx, input.OperatorID, services.ActionCreate, services.ResourceWorkflow, input.BoardID, ) if err != nil { return nil, err } if !authResult.Authorized { return nil, domain.NewUnauthorizedError(authResult.Reason) } // ===== Domain Logic ===== workflow, err := aggregates.NewWorkflow( valueobjects.NewBoardID(input.BoardID), valueobjects.NewWorkflowName(input.Name), valueobjects.NewUserID(input.OperatorID), ) if err != nil { return nil, err } // ===== Persist ===== if err := uc.workflowRepo.Save(ctx, workflow); err != nil { return nil, err } // ===== Publish Domain Event ===== if err := uc.eventPub.Publish(ctx, events.NewWorkflowCreatedEvent(workflow)); err != nil { return nil, err } return &CreateWorkflowOutput{ WorkflowID: workflow.ID().String(), Status: string(workflow.Status()), CreatedAt: workflow.CreatedAt(), }, nil }
// src/domain/aggregates/workflow.go // Generated from: docs/specs/create-workflow/controlled-domain/aggregate.yaml package aggregates import ( "time" "myapp/domain/entities" "myapp/domain/valueobjects" ) type Workflow struct { id valueobjects.WorkflowID boardID valueobjects.BoardID name valueobjects.WorkflowName stages []*entities.Stage createdBy valueobjects.UserID createdAt time.Time } func NewWorkflow( boardID valueobjects.BoardID, name valueobjects.WorkflowName, createdBy valueobjects.UserID, ) (*Workflow, error) { w := &Workflow{ id: valueobjects.GenerateWorkflowID(), boardID: boardID, name: name, stages: make([]*entities.Stage, 0), createdBy: createdBy, createdAt: time.Now(), } // ===== INV1: Validate invariants in constructor ===== if err := w.validateInvariants(); err != nil { return nil, err } return w, nil } func (w *Workflow) AddStage(stage *entities.Stage) error { // ===== FC1: Structure Integrity ===== w.stages = append(w.stages, stage) // Re-validate after mutation return w.validateInvariants() } // ===== Invariants (from aggregate.yaml#invariants.shared) ===== func (w *Workflow) validateInvariants() error { // INV1: Structure Integrity - Lane hierarchy for _, stage := range w.stages { for _, lane := range stage.SwimLanes() { if lane.ParentStage() == nil { return domain.NewInvariantViolationError( "SwimLane must be under a Stage", ) } } } return nil } // ===== Getters ===== func (w *Workflow) ID() valueobjects.WorkflowID { return w.id } func (w *Workflow) BoardID() valueobjects.BoardID { return w.boardID } func (w *Workflow) Name() valueobjects.WorkflowName { return w.name } func (w *Workflow) Stages() []*entities.Stage { return w.stages } func (w *Workflow) CreatedBy() valueobjects.UserID { return w.createdBy } func (w *Workflow) CreatedAt() time.Time { return w.createdAt } func (w *Workflow) Status() WorkflowStatus { if len(w.stages) > 0 { return WorkflowStatusActive } return WorkflowStatusEmpty }
生成代碼時,確保每個 Frame Concern 都有對應的實作:
| Frame Concern | 規格位置 | 實作位置 |
|---|---|---|
| FC1: Structure Integrity | aggregate.yaml#invariants.shared | Aggregate.validateInvariants() |
| FC2: Concurrency | use-case.yaml#transaction_boundary | Use Case transaction handling |
| XC1: Authorization | cross-context/authorization.yaml | Use Case authorization check |