### Tap and Install Open-SPDD via Homebrew Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md Alternative Homebrew installation by first tapping the repository and then installing the package. ```bash brew tap gszhangwei/tools brew install openspdd ``` -------------------------------- ### Install Open-SPDD using One-shot Script Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md Execute the install script to install the latest version or a specific tag of Open-SPDD. This script handles `go install` and PATH instructions. ```bash ./scripts/install.sh # installs @latest ./scripts/install.sh v1.2.3 # installs a specific tag ``` -------------------------------- ### Update README --tool Examples Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-004-202605081404-[Feat]-detector-opencode-tool-support.md Append the OpenCode tool example to the --tool examples block in README.md and README.zh-CN.md. ```bash openspdd --tool opencode ``` -------------------------------- ### Install and Initialize OpenSPDD Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md Print the installed version, navigate to your project, and initialize OpenSPDD. Initialization auto-detects the AI tool. ```bash openspdd -v ``` ```bash cd your-project ``` ```bash openspdd init ``` -------------------------------- ### Install Open-SPDD via Go Install Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md Install the latest version of Open-SPDD using the Go toolchain. Ensure your Go environment is set up correctly. ```bash go install github.com/gszhangwei/open-spdd/cmd/openspdd@latest ``` -------------------------------- ### Build Open-SPDD from Source Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md Clone the repository, navigate into the directory, build the executable, and install it. ```bash git clone https://github.com/gszhangwei/open-spdd.git cd open-spdd go build -o openspdd ./cmd/openspdd go install ./cmd/openspdd ``` -------------------------------- ### InstallContext Struct Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-003-202604291446-[Feat]-version-flag-and-uninstall-command.md Represents the context for an installation, including the method, binary paths, formula name for Homebrew, marker path, and a rationale. ```go type InstallContext struct { Method InstallMethod BinaryPath string // raw os.Executable() result ResolvedPath string // after EvalSymlinks FormulaName string // populated only for Homebrew, fixed value MarkerPath string // /openspdd/.path-hint-shown Reason string // human-readable classification rationale } ``` -------------------------------- ### InstallMethod Type and Constants Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-003-202604291446-[Feat]-version-flag-and-uninstall-command.md Defines the `InstallMethod` string type and its associated constants for various installation methods like Homebrew, Go install, and unknown. ```go type InstallMethod string const ( MethodHomebrew InstallMethod = "homebrew" MethodGoInstall InstallMethod = "go-install" MethodUnknown InstallMethod = "unknown" ) ``` -------------------------------- ### Install Open-SPDD via Homebrew Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md Use this command to install Open-SPDD if you are using Homebrew on macOS or Linux. ```bash brew install gszhangwei/tools/openspdd ``` -------------------------------- ### UninstallPlan Struct Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-003-202604291446-[Feat]-version-flag-and-uninstall-command.md Defines the overall uninstall plan, including the installation method, a list of steps, and a summary. ```go type UninstallPlan struct { Method InstallMethod Steps []PlanStep Summary string } ``` -------------------------------- ### Detect Install Context Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-003-202604291446-[Feat]-version-flag-and-uninstall-command.md Determines the installation method and context of the openspdd binary by inspecting its path and environment. This function is safe to call multiple times and does not mutate state. ```go func detectInstallContext() InstallContext { binaryPath, err := os.Executable() if err != nil { return InstallContext{Method: MethodUnknown, Reason: "could not determine executable path: " + err.Error()} } resolved, err := filepath.EvalSymlinks(binaryPath) if err != nil { resolved = binaryPath } method, reason := classifyByPath(resolved) markerPath := pathHintMarkerPath() return InstallContext{ Method: method, BinaryPath: binaryPath, ResolvedPath: resolved, MarkerPath: markerPath, Reason: reason, FormulaName: homebrewFormulaName, // if Homebrew } } ``` -------------------------------- ### Install Optional OpenSPDD Commands Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md These commands are used to install specific optional beta features into your OpenSPDD environment. Each command installs a distinct feature. ```bash # Install a specific optional command openspdd generate spdd-story ``` ```bash openspdd generate spdd-code-review ``` ```bash openspdd generate spdd-api-test ``` ```bash openspdd generate spdd-reverse ``` -------------------------------- ### OpenSPDD Installation Commands Source: https://github.com/gszhangwei/open-spdd/blob/main/docs/design-philosophy.md Installs OpenSPDD using Homebrew for macOS or Go for other systems. After installation, navigate to your project directory and run 'openspdd generate --all' to generate SPDD commands. ```bash brew install gszhangwei/tools/openspdd # macOS ``` ```bash go install github.com/gszhangwei/open-spdd@latest # non-macOS, requires Go ``` ```bash cd your-project openspdd generate --all # Generate SPDD commands ``` -------------------------------- ### GitHub Copilot File Structure Example Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md Illustrates the directory and file layout for GitHub Copilot instructions and prompts used with Open-SPDD. This structure helps organize custom prompts for Copilot. ```text .github/ ├── copilot-instructions.md # Main instruction file (auto-merged with markers) └── copilot-prompts/ ├── spdd-analysis.md ├── spdd-reasons-canvas.md ├── spdd-generate.md ├── spdd-prompt-update.md └── spdd-sync.md ``` -------------------------------- ### Append Codex Tool Example to CLI Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-005-202605100958-[Feat]-cli-codex-skills-support-via-strategy.md Add this line to the `--tool` examples block in README files to show how to use the Codex tool with the CLI. ```bash openspdd --tool codex ``` -------------------------------- ### Classify Binary Path Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-003-202604291446-[Feat]-version-flag-and-uninstall-command.md Classifies the installation method based on the resolved binary path. It checks for Homebrew Cellar paths and Go installation directories. ```go func classifyByPath(resolved string) (InstallMethod, string) { path := filepath.ToSlash(resolved) if strings.Contains(path, "/Cellar/openspdd/") { return MethodHomebrew, "binary resolved under Homebrew Cellar" } goBins := findGoBin() for _, goBin := range goBins { if strings.HasPrefix(path, goBin) { return MethodGoInstall, "binary located in " } } return MethodUnknown, "binary path matched neither Homebrew Cellar nor a Go bin directory" } ``` -------------------------------- ### Build Uninstall Plan for Homebrew Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-003-202604291446-[Feat]-version-flag-and-uninstall-command.md Generates an uninstall plan for Homebrew installations. Includes running 'brew uninstall' and optionally removing a marker file. ```go func buildPlan(ctx InstallContext) UninstallPlan { splan := UninstallPlan{Method: ctx.Method} switch ctx.Method { case MethodHomebrew: splan.Steps = append(splan.Steps, StepRunCommand{ Program: "brew", Args: []string{"uninstall", ctx.FormulaName}, Command: "brew uninstall " + ctx.FormulaName, Description: "Run Homebrew to uninstall the formula", }) if ctx.MarkerPath != "" { splan.Steps = append(splan.Steps, StepRemoveFile{ Paths: []string{ctx.MarkerPath}, Description: "Remove openspdd's first-run marker file", Optional: true, }) } splan.Summary = "openspdd was installed via Homebrew. Will run \"brew uninstall " + ctx.FormulaName + "\" and clean up state." case MethodGoInstall: if runtime.GOOS == "windows" { splan.Steps = append(splan.Steps, StepAdvisory{ Description: "On Windows, the running .exe cannot self-delete. After this command exits, run: del \"" + ctx.ResolvedPath + "\"", }) } else { splan.Steps = append(splan.Steps, StepRemoveFile{ Paths: []string{ctx.ResolvedPath}, Description: "Remove the openspdd binary at " + ctx.ResolvedPath, Optional: false, }) } if ctx.MarkerPath != "" { splan.Steps = append(splan.Steps, StepRemoveFile{ Paths: []string{ctx.MarkerPath}, Description: "Remove openspdd's first-run marker file", Optional: true, }) } splan.Summary = "openspdd was installed via go install. Will remove the binary and clean up state." case MethodUnknown: splan.Steps = append(splan.Steps, StepAdvisory{ Description: "Could not classify how openspdd was installed (resolved path: " + ctx.ResolvedPath + "). Refusing to auto-uninstall. Remove the binary manually.", }) splan.Summary = "openspdd installation method could not be detected — manual removal required." } return splan } ``` -------------------------------- ### List Optional OpenSPDD Commands Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md Use this command to display all optional commands that are available but not installed by default. ```bash # List all optional commands openspdd list --optional ``` -------------------------------- ### OpenSPDD Command-Line Usage Source: https://github.com/gszhangwei/open-spdd/blob/main/docs/design-philosophy.md Examples of using OpenSPDD commands for various stages of AI-assisted development, including analysis, prompt generation, and code synchronization. ```bash /spdd-analysis @requirements/feature.md # Strategic analysis ``` ```bash /spdd-reasons-canvas @spdd/analysis/xxx.md # Generate structured prompts ``` ```bash /spdd-generate @spdd/prompt/xxx.md # Generate code from structured prompts ``` ```bash /spdd-sync @spdd/prompt/xxx.md # Sync changes back to prompts ``` -------------------------------- ### Code vs. Design Intent: Multi-tenancy Example Source: https://github.com/gszhangwei/open-spdd/blob/main/docs/design-philosophy.zh-CN.md Highlights the discrepancy between scanning existing single-tenant code and the design intent to implement multi-tenancy, showing how code scanning can lead AI astray. ```text 场景: 要在现有系统上添加"多租户"支持 Agent 扫描代码库: 当前是单租户架构 Agent 的推断: 继续按单租户模式生成代码 但设计意图是: 这次就是要改成多租户 ``` -------------------------------- ### UninstallPlan Describe Method Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-003-202604291446-[Feat]-version-flag-and-uninstall-command.md Provides a human-readable, multi-line string representation of the uninstall plan. Used for UX confirmation and dry-run outputs. ```go func (p UninstallPlan) Describe() string { // ... implementation details ... } ``` -------------------------------- ### SPDD File Naming Convention Examples Source: https://github.com/gszhangwei/open-spdd/blob/main/internal/templates/data/core/spdd-reasons-canvas.md Examples of the SPDD naming convention for commit messages, including action, scope, and description. ```markdown - **ACTION**: Infer from business context - `[Feat]`, `[Fix]`, `[Refactor]`, `[Test]`, `[Docs]` - **scope**: Infer from context - `api`, `service`, `repo`, `bq`, `db`, `util` (optional) - **description**: Derive from business context - kebab-case, < 10 words Examples: - `GGQPA-XXX-202603061530-[Feat]-api-user-registration.md` - `GGQPA-169-202603061530-[Fix]-service-payment-validation.md` ``` -------------------------------- ### Preview Open-SPDD Uninstallation Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md Use the `--dry-run` flag with the uninstall command to preview the cleanup steps without making any changes to your system. ```bash openspdd uninstall --dry-run ``` -------------------------------- ### Format Uninstall Plan Description Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-003-202604291446-[Feat]-version-flag-and-uninstall-command.md Formats the uninstall plan for display on standard output, detailing each step with relevant commands or paths. ```go func (p UninstallPlan) Describe() string { var builder strings.Builder builder.WriteString(fmt.Sprintf("Uninstall plan (%s):\n", p.Method)) for i, step := range p.Steps { builder.WriteString(fmt.Sprintf(" [%d] %s\n", i+1, step.Describe())) } return builder.String() } ``` -------------------------------- ### main.go Entry Point Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-000-202603091720-[Feat]-cli-env-detection-crud-template-subcommand.md The main function serves as the program's entry point, delegating execution to the cmd.Execute() function. ```go package main import "open-spdd/cmd" func main() { cmd.Execute() } ``` -------------------------------- ### Implement GenerateOne for CopilotStrategy Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-005-202605100958-[Feat]-cli-codex-skills-support-via-strategy.md Handles the generation of a single Copilot prompt file. It computes the prompt directory, creates it if it doesn't exist, and writes the template content. If the file exists and force is false, it returns a skip result. ```go func (s *CopilotInstructionFileStrategy) GenerateOne(workingDir string, tmpl TemplateMeta, force bool) []GenerateResult { promptsDir := filepath.Join(workingDir, ".github", "copilot-prompts") if err := os.MkdirAll(promptsDir, 0755); err != nil { return []GenerateResult{ { Success: false, Error: err, Message: fmt.Sprintf("failed to create prompts directory %s: %v", promptsDir, err), }, } } targetPath := filepath.Join(promptsDir, tmpl.ID+".md") if _, err := os.Stat(targetPath); err == nil && !force { return []GenerateResult{ { Success: false, Error: internal.ErrFileExists, Message: "file already exists (use --force to overwrite)", }, } } if err := os.WriteFile(targetPath, []byte(tmpl.Content), 0644); err != nil { return []GenerateResult{ { Success: false, Error: err, Message: fmt.Sprintf("failed to write prompt file %s: %v", targetPath, err), }, } } return []GenerateResult{ { Success: true, FilePath: targetPath, Message: "prompt file generated", }, } } ``` -------------------------------- ### Structured Prompt for User Registration Service Implementation Source: https://github.com/gszhangwei/open-spdd/blob/main/docs/design-philosophy.zh-CN.md A structured, framework-guided approach to defining requirements for a user registration service, explicitly detailing business context, responsibilities, dependencies, and method logic. ```markdown ## Requirements (为什么做) - 业务背景: MVP 阶段,只需邮箱注册,不需要 OAuth ## Operations (怎么做) ### Create Service Implementation - UserRegistrationServiceImpl 1. Responsibility: Handle user registration business logic 2. Location: `com.example.user.service.impl.UserRegistrationServiceImpl` 3. Dependencies (constructor injection): - `UserRepository userRepository` - `EmailValidator emailValidator` - `PasswordEncoder passwordEncoder` 4. Annotations: `@Service`, `@Transactional` 5. Method `register(UserRegistrationRequest request)`: UserRegistrationResponse - Logic: 1. Call `emailValidator.validate(request.getEmail())` 2. Call `userRepository.existsByEmail(request.getEmail())` → if true, throw `EmailAlreadyExistsException` with message "Email already registered" 3. Call `passwordEncoder.encode(request.getPassword())` → get encodedPassword 4. Create User entity with status `PENDING_VERIFICATION` 5. Call `userRepository.save(user)` → get savedUser 6. Return UserRegistrationResponse with savedUser.getId() ``` -------------------------------- ### Execute Uninstall Plan Logic Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-003-202604291446-[Feat]-version-flag-and-uninstall-command.md Implements the `Execute` method for `uninstallExecutor`, iterating through uninstall steps and handling different step kinds like running commands or removing files. ```go func (e *uninstallExecutor) Execute(plan UninstallPlan) error { for _, step := range plan.Steps { switch step.Kind { case StepRunCommand: path, err := exec.LookPath(step.Program) if err != nil { e.ui.RenderError(" is not on PATH; cannot complete uninstall automatically") return err } cmd := execCommand(path, step.Args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { e.ui.RenderError("Step failed: " + step.Description + ": " + err.Error()) return err } e.ui.RenderSuccess("Done: " + step.Description) case StepRemoveFile: for _, p := range step.Paths { err := os.Remove(p) if err == nil { e.ui.RenderSuccess("Removed: " + p) } else if errors.Is(err, fs.ErrNotExist) { if step.Optional { // Silently skip } else { e.ui.RenderWarning("Already absent: " + p) } } else { if step.Optional { e.ui.RenderWarning("Could not remove " + p + ": " + err.Error()) } else { e.ui.RenderError("Could not remove " + p + ": " + err.Error()) return err } } } case StepAdvisory: e.ui.RenderWarning(step.Description) } } // Post-uninstall sanity check if leftover, err := exec.LookPath("openspdd"); err == nil && leftover != "" { e.ui.RenderWarning("Note: another openspdd binary is still on PATH at " + leftover) } return nil } ``` -------------------------------- ### User Registration Service Implementation Details Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md This excerpt details the implementation of a UserRegistrationService, including its responsibilities, dependencies, methods, and error handling strategies. It specifies validation, password encoding, entity creation, and database saving. ```markdown ### Create UserRegistrationService - `UserRegistrationServiceImpl` 1. **Responsibility**: Handle user registration business logic 2. **Package**: `com.example.user.service.impl` 3. **Implements**: `UserRegistrationService` interface 4. **Dependencies** (constructor injection): - `UserRepository userRepository` - `EmailValidator emailValidator` - `PasswordEncoder passwordEncoder` 5. **Methods**: - `register(UserRegistrationRequest request): UserRegistrationResponse` - **Input Validation**: Call `emailValidator.validate(request.getEmail())` - **Business Logic**: 1. Check if email exists via `userRepository.existsByEmail()` 2. If exists, throw `EmailAlreadyExistsException` with message "Email already registered" 3. Encode password via `passwordEncoder.encode()` 4. Create User entity with status `PENDING_VERIFICATION` 5. Save via `userRepository.save()` - **Exception Handling**: Let exceptions propagate to GlobalExceptionHandler 6. **Annotations**: `@Service`, `@Transactional` ``` -------------------------------- ### AI Over-Performance Example Source: https://github.com/gszhangwei/open-spdd/blob/main/docs/design-philosophy.md Illustrates how AI might add unrequested features or deviate from design intent by overperforming within the 'positive space' of a prompt. ```text You: "Implement this Service" AI: [Added a caching layer you didn't ask for] [Decided on an exception retry strategy on its own] [Placed the transaction boundary at the caller instead of the service] ``` -------------------------------- ### Interactive Open-SPDD Uninstallation Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md Run the uninstall command without flags for an interactive uninstallation. It will print the plan and ask for confirmation before proceeding. ```bash openspdd uninstall ``` -------------------------------- ### Get Generation Strategy Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-005-202605100958-[Feat]-cli-codex-skills-support-via-strategy.md Retrieves the appropriate `GenerationStrategy` for a given `detector.AIToolType`. If a registered factory exists, it is used; otherwise, a default `FlatMarkdownStrategy` is returned. This ensures `StrategyFor` never returns nil. ```go func StrategyFor(tool detector.AIToolType, mgr *EmbeddedTemplateManager) GenerationStrategy { if factory, ok := strategyRegistry[tool]; ok { return factory(mgr) } return &FlatMarkdownStrategy{tool: tool, manager: mgr} } ``` -------------------------------- ### Bash: Edge Case - Missing Required Field Test Source: https://github.com/gszhangwei/open-spdd/blob/main/internal/templates/data/optional/spdd-api-test.md Example of a validation error test, demonstrating how to check for a '400 Bad Request' response when a required field is missing from the request payload. ```bash # Edge Case: Missing required field TEST_ID="EDGE2" TEST_DESC="Missing Required Field" EXPECTED="400" TESTS_TOTAL=$((TESTS_TOTAL + 1)) print_test_header "$TEST_ID: $TEST_DESC" print_expected "HTTP $EXPECTED" print_result HTTP_CODE=$(curl -s -o /tmp/response.txt -w "%{http_code}" -X POST "${BASE_URL}/api/[endpoint]" \ -H "Content-Type: application/json" \ -m 10 \ -d '{}') BODY=$(cat /tmp/response.txt) ``` -------------------------------- ### Initialize OpenSPDD Environment Source: https://github.com/gszhangwei/open-spdd/blob/main/README.md Initialize the OpenSPDD environment. This can be done automatically by detecting the AI tool or by manually specifying the tool. ```bash # Auto-detect and initialize openspdd init ``` ```bash # Specify tool manually openspdd --tool cursor init ``` -------------------------------- ### GitHub Copilot Instructions Template Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-001-202603101000-[Feat]-github-copilot-support.md Provides the core instruction markdown file for GitHub Copilot. It outlines the SPDD methodology, available workflows (REASONS-Canvas, SPDD Generate, SPDD Sync), and usage guidelines. ```markdown # SPDD Framework for GitHub Copilot This project uses the SPDD (Structured Prompt-Driven Development) methodology for AI-assisted development. ## Available Workflows When I ask you to use SPDD workflows, refer to these templates: ### 1. REASONS-Canvas For structured requirement analysis and prompt generation. - **Template**: `.github/copilot-prompts/spdd-reasons-canvas.md` - **Usage**: "Use REASONS-Canvas to design [feature description]" - **Output**: Structured prompt file in `spdd/prompt/` directory ### 2. SPDD Generate For code generation from structured prompt files. - **Template**: `.github/copilot-prompts/spdd-generate.md` - **Usage**: "Generate code from @spdd/prompt/[filename].md" - **Output**: Implementation code following Operations sequence ### 3. SPDD Sync For syncing code changes back to prompt files. - **Template**: `.github/copilot-prompts/spdd-sync.md` - **Usage**: "Sync changes to @spdd/prompt/[filename].md" - **Output**: Updated prompt file reflecting code changes ## How to Use 1. **Read the relevant template file** when I mention an SPDD workflow 2. **Follow the steps** defined in the template 3. **Apply the guardrails** specified in each template ## Quick Reference | Workflow | When to Use | Template Location | |----------|-------------|-------------------| | REASONS-Canvas | Starting new feature/task | `.github/copilot-prompts/spdd-reasons-canvas.md` | | SPDD Generate | Implementing from prompt | `.github/copilot-prompts/spdd-generate.md` | | SPDD Sync | After code refactoring | `.github/copilot-prompts/spdd-sync.md` | ``` -------------------------------- ### Define AIToolType Enum and Detection Methods Source: https://github.com/gszhangwei/open-spdd/blob/main/spdd/prompt/GGQPA-000-202603091720-[Feat]-cli-env-detection-crud-template-subcommand.md Defines a custom string type for AI tool identification and associated constants. Includes methods to get the configuration directory and signature files for each tool type. ```go type AIToolType string const ( Cursor AIToolType = "cursor" ClaudeCode AIToolType = "claude-code" Antigravity AIToolType = "antigravity" Unknown AIToolType = "unknown" ) func (t AIToolType) GetConfigDir() string { switch t { case Cursor: return ".cursor/commands" case ClaudeCode: return ".claude/commands" case Antigravity: return ".antigravity/commands" default: return "" } } func (t AIToolType) GetSignatureFiles() []string { switch t { case Cursor: return []string{".cursor", ".cursorrules"} case ClaudeCode: return []string{".claude", "CLAUDE.md"} case Antigravity: return []string{".antigravity"} default: return nil } } ``` -------------------------------- ### Derive REASONS Canvas File Name Source: https://github.com/gszhangwei/open-spdd/blob/main/internal/templates/data/optional/spdd-reverse.md Specifies the naming convention for REASONS Canvas prompt files, incorporating JIRA ticket, timestamp, scope, and a description of the feature area. Examples illustrate the expected format. ```text {JIRA}-{TIMESTAMP}-[Codify]-{scope}-{description}.md ``` ```text GGQPA-XXX-202603061530-[Codify]-ci-preview-deployment-flow.md ``` ```text GGQPA-42-202603061530-[Codify]-service-billing.md ```