diff --git a/.gemini/commands/implement.toml b/.gemini/commands/implement.toml new file mode 100644 index 00000000000..0dc6cda1de9 --- /dev/null +++ b/.gemini/commands/implement.toml @@ -0,0 +1,650 @@ +description = "Implements a GitHub issue locally with automated review and iteration" + +prompt = """ +You are tasked with implementing GitHub issue #{{args}} from the google/site-kit-wp repository following a strict quality workflow. + +═══════════════════════════════════════════════════════════════════════════════ +CRITICAL INSTRUCTIONS +═══════════════════════════════════════════════════════════════════════════════ + +- You MUST ACTUALLY CREATE, MODIFY, and DELETE files in the current working directory +- DO NOT just output proposed changes or describe what should be done +- Use your developer tools to make ACTUAL file modifications +- This is a LOCAL implementation - DO NOT create commits, pull requests, or push to remote +- All changes remain local for the developer to review, test, and commit manually + +═══════════════════════════════════════════════════════════════════════════════ +WORKFLOW OVERVIEW +═══════════════════════════════════════════════════════════════════════════════ + +This is a multi-phase workflow with quality enforcement: +1. Fetch GitHub Issue +2. Reference Context Documentation +3. Implement Changes +4. Code Review (self-review with scoring) +5. Iteration (up to 3 times if score < 0.85) +6. Documentation (optional, for new architectural patterns) + +You will track your iteration count and exit gracefully if unable to reach a score of 0.85 after 3 iterations. + +═══════════════════════════════════════════════════════════════════════════════ +PHASE 1: FETCH GITHUB ISSUE +═══════════════════════════════════════════════════════════════════════════════ + +**Objective**: Retrieve the issue details and extract implementation instructions + +**Steps**: +1. Use GitHub MCP tools to fetch issue #{{args}} from repository `google/site-kit-wp` + - If MCP tools are not available, use: !{gh issue view {{args}} --json title,body} + +2. Extract the "Acceptance criteria" section from the issue body + - This section typically appears between "## Acceptance criteria" and "## Implementation Brief" + - If the section markers are not present, stop execution and show an error + +3. Extract the "Implementation Brief" section from the issue body + - This section typically appears between "## Implementation Brief" and "## QA Brief" + - If the section markers are not present, stop execution and show an error + +**Error Handling**: +- If the issue does not exist: STOP and report "ERROR: Issue #{{args}} not found in google/site-kit-wp" +- If GitHub API is unavailable: STOP and report the error with suggestions +- If the issue body is empty: STOP and report "ERROR: Issue #{{args}} has no description" +- If "Acceptance criteria" markers are missing: STOP and report "ERROR: Acceptance criteria section markers not found" +- If the "Implementation Brief" markers are missing: STOP and report "ERROR: Implementation Brief section markers not found" + +**Output**: Store the implementation instructions for use in subsequent phases + +═══════════════════════════════════════════════════════════════════════════════ +PHASE 2: REFERENCE CONTEXT DOCUMENTATION +═══════════════════════════════════════════════════════════════════════════════ + +**Objective**: Understand the mandatory conventions and patterns for this codebase + +**Context Files Available**: +All project context files are already loaded via .gemini/settings.json. You have access to: + +**JavaScript/React Context** (13 files in docs/context/js/): +- component-conventions.md - Component structure and naming +- module-architecture.md - Module organization patterns +- state-management.md - State management with WordPress data stores +- hooks.md - React hooks usage and custom hooks +- tests.md - Testing conventions and patterns +- jsdoc.md - Documentation standards +- event-tracking.md - Analytics and event tracking +- feature-flags.md - Feature flag implementation +- feature-tours.md - User onboarding tours +- notifications.md - Notification system patterns +- widgets.md - Dashboard widget creation +- storybook.md - Component story requirements +- utils.md - Utility function conventions + +**PHP Context** (11 files in docs/context/php/): +- module-architecture.md - PHP module patterns +- settings-management.md - Settings API usage +- dependency-injection.md - DI container patterns +- context-pattern.md - Context object usage +- admin-features.md - Admin interface patterns +- rest-api.md - REST API conventions +- asset-management.md - Asset registration +- storage-patterns.md - Data storage patterns +- prompts-and-dismissals.md - User prompt patterns +- trait-composition.md - PHP trait usage +- naming-conventions.md - PHP naming standards + +**Project Overview**: +- AGENTS.md - High-level project architecture and development workflow + +**CRITICAL**: These files define MANDATORY conventions. Any deviation is considered a critical violation. + +═══════════════════════════════════════════════════════════════════════════════ +PHASE 3: IMPLEMENTATION +═══════════════════════════════════════════════════════════════════════════════ + +**Objective**: Implement the issue following documented principles + +Follow these implementation guidelines: + +1. **Understand the Task** + - Carefully read the Implementation Brief and Acceptance Criteria (AC) from Phase 1 + - Note that the AC are the actual requirements that MUST be met + - Break down complex requirements into discrete steps + - Plan your implementation approach before writing code + - Identify all files that need to be created, modified, or deleted + +2. **Implement Following Strict Principles** + - Strictly follow ALL conventions from context documentation + - Study existing code patterns in similar features before implementing + - Use appropriate naming conventions from context docs + - Implement proper error handling and consider edge cases + - Ensure accessibility when implementing UI components + - Follow security best practices (avoid XSS, SQL injection, etc.) + - Use existing utilities and patterns rather than reinventing + +3. **Write Comprehensive Tests** + - **For JS**: Follow testing conventions from docs/context/js/tests.md + - **For PHP**: Follow PHPUnit patterns from docs/context/php/phpunit.md + - Write unit tests for ALL new functionality + - Include tests for edge cases and error scenarios + - Ensure tests are clear, maintainable, and follow existing patterns + - Mock external dependencies appropriately + - Aim for thorough test coverage of business logic + +4. **Document Your Code** + - **For JS**: Follow JSDoc conventions from docs/context/js/jsdoc.md + - **For PHP**: Follow PHPDoc conventions and WordPress documentation standards + - Document all exported functions, components, classes, and complex logic + - Include clear descriptions, parameter types, and return types + - Add inline comments for non-obvious logic + - Update README files if introducing new features + +5. **Consider Integration Points** + + **For JavaScript/React**: + - **Event Tracking**: Use patterns from docs/context/js/event-tracking.md if user actions need tracking + - **Feature Flags**: Follow docs/context/js/feature-flags.md if feature should be toggleable + - **Feature Tours**: Follow docs/context/js/feature-tours.md for new UI features + - **Notifications**: Use docs/context/js/notifications.md for user notifications + - **State Management**: Follow docs/context/js/state-management.md for data stores + - **Widgets**: Follow docs/context/js/widgets.md for dashboard widgets + + **For PHP**: + - **Module Architecture**: Follow docs/context/php/module-architecture.md for module structure + - **Settings**: Use docs/context/php/settings-management.md for settings API + - **REST API**: Follow docs/context/php/rest-api.md for REST endpoints + - **Dependency Injection**: Use docs/context/php/dependency-injection.md for DI patterns + - **Storage**: Follow docs/context/php/storage-patterns.md for data storage + - **Context Pattern**: Use docs/context/php/context-pattern.md for context objects + - **Asset Management**: Follow docs/context/php/asset-management.md for registering assets + - **Admin Features**: Use docs/context/php/admin-features.md for admin UI + - **Prompts**: Follow docs/context/php/prompts-and-dismissals.md for user prompts + +6. **Create Supporting Files** + - **For JS**: Add Storybook stories for UI components (docs/context/js/storybook.md) + - **For PHP**: Create appropriate test fixtures and mock data + - Create fixture data for tests if needed + - Update or create example usage if helpful + +7. **Verify Your Implementation** + Run these verification steps: + + a. **Linting**: + - **For JS changes**: !{npm run lint:js} + - **For PHP changes**: !{composer run lint} + - Fix any linting errors immediately before proceeding + - Do not proceed with failing lints + + b. **Tests**: Run tests to verify changes (MANDATORY - DO NOT SKIP) + - **For JS changes**: Run !{npm run test:js} to execute Jest tests + - **For PHP changes**: Run !{composer run test} to execute PHPUnit tests + - **CRITICAL**: You MUST actually execute these test commands and verify output + - **CRITICAL**: ALL tests must pass before proceeding - a passing test run is REQUIRED + - If tests fail, fix the issues and re-run tests until they pass + - Do NOT proceed to the next phase if tests are failing + + c. **Build**: If you made significant changes, verify the build works + - Run !{npm run build:dev} if appropriate + - Fix any build errors + +8. **Provide Implementation Summary** + After completing implementation, provide a structured summary: + + ``` + IMPLEMENTATION SUMMARY + ====================== + Files Created: [count] + - [file paths with brief description] + + Files Modified: [count] + - [file paths with brief description of changes] + + Files Deleted: [count] + - [file paths with reason for deletion] + + Key Features Implemented: + - [feature 1 with brief description] + - [feature 2 with brief description] + + Tests Added/Modified: + - [test file paths with coverage description] + - Edge cases covered: [list] + + Context Compliance: + - [list of context conventions followed] + - [reference specific docs used] + + Verification Results (MUST INCLUDE ACTUAL TEST OUTPUT): + - Linting: [pass/fail with details] + - JS Tests: [pass/fail - include output from: npm run test:js] + - PHP Tests: [pass/fail - include output from: composer run test] + - Build: [if applicable] + + NOTE: Tests MUST have been executed. If tests were not run, go back and run them. + ``` + +═══════════════════════════════════════════════════════════════════════════════ +PHASE 4: CODE REVIEW +═══════════════════════════════════════════════════════════════════════════════ + +**Objective**: Evaluate implementation quality and adherence to documented principles + +Follow these code review guidelines: + +1. **Verify Requirements Adherence (MANDATORY)** + + Check compliance with the extracted "Acceptance criteria": + - ✓ Implementation fulfills all points in "Acceptance criteria" + - ✓ No required functionality is missing + - ✓ Behavioral requirements are met + - ✓ Edge cases defined in AC are handled + +2. **Verify Context Adherence (MANDATORY)** + + Check strict compliance with documented principles. Review each relevant context file: + + **For JavaScript/React changes**: + - ✓ Component structure follows component-conventions.md + - ✓ Module organization follows module-architecture.md + - ✓ State management follows state-management.md patterns + - ✓ Hooks usage follows hooks.md guidelines + - ✓ Tests follow tests.md conventions + - ✓ JSDoc follows jsdoc.md standards + - ✓ Event tracking follows event-tracking.md (if applicable) + - ✓ Feature flags follow feature-flags.md (if applicable) + - ✓ Tours follow feature-tours.md (if applicable) + - ✓ Notifications follow notifications.md (if applicable) + - ✓ Widgets follow widgets.md (if applicable) + - ✓ Storybook stories follow storybook.md (if UI components) + - ✓ Utils follow utils.md (if utility functions) + + **For PHP changes**: + - ✓ Module structure follows php/module-architecture.md + - ✓ Settings follow php/settings-management.md + - ✓ DI follows php/dependency-injection.md + - ✓ Context usage follows php/context-pattern.md + - ✓ Admin features follow php/admin-features.md + - ✓ REST API follows php/rest-api.md + - ✓ Assets follow php/asset-management.md + - ✓ Storage follows php/storage-patterns.md + - ✓ Prompts follow php/prompts-and-dismissals.md + - ✓ Traits follow php/trait-composition.md + - ✓ Naming follows php/naming-conventions.md + + **CRITICAL**: Document ANY violation found: + - Which principle was violated + - Which context file defines the violated principle (with section reference) + - How the code violates it (specific examples) + - What must be fixed to comply + - Which files are affected + +3. **Analyze Code Quality** + + Review for general code quality issues: + - Code structure and organization + - Readability and maintainability + - Error handling and edge cases + - Security vulnerabilities (XSS, injection, auth bypass, etc.) + - Performance issues (unnecessary re-renders, N+1 queries, etc.) + - Test coverage and test quality + - Documentation completeness and clarity + - Accessibility compliance (WCAG standards) + - Browser/PHP version compatibility + +4. **Score the Implementation (0.0 to 1.0)** + + Assign a score based on this rubric: + + **0.0 - 0.5**: Poor quality OR contains requirements/principle violations + - Use this range if ANY "Acceptance criteria" are not met + - Use this range if ANY documented principles are violated + - Use this if critical security or functionality issues exist + - Use this if tests are missing or failing + - Use this if tests were NOT actually executed (npm run test:js / composer run test) + + **0.5 - 0.84**: Below acceptable quality + - Code works but doesn't meet quality standards + - Multiple code quality issues present + - Tests exist but are incomplete + - Documentation is sparse or unclear + + **0.85 - 0.94**: Good quality, production-ready + - Fulfills ALL requirements from "Acceptance criteria" + - Follows all documented principles + - Code is clean and maintainable + - Comprehensive tests with good coverage + - Tests were ACTUALLY EXECUTED and ALL passed (npm run test:js / composer run test) + - Well documented + - Minor improvements possible but not required + + **0.95 - 1.0**: Excellent, exemplary code + - Exceeds quality standards + - Perfect adherence to all principles + - Exceptional test coverage + - Outstanding documentation + - Sets a positive example for future development + +5. **Provide Review Results** + + Format your review as follows: + + ``` + CODE REVIEW RESULTS + =================== + Score: [0.0 - 1.0] + Status: [approved | needs_improvement] + Iteration: [current iteration number, 1-3] + + REQUIREMENTS VIOLATIONS: [count] + + [If violations exist, list each:] + + Requirement Violation #[n]: + - AC Point: [which point from Acceptance criteria was violated] + - Details: [why the implementation does not meet this requirement] + - Fix Required: [what must be changed to implement the requirement] + + CONTEXT VIOLATIONS: [count] + + [If violations exist, list each:] + + Violation #[n]: + - Principle: [what was violated] + - Context File: [docs/context/.../file.md - section name] + - Violation Details: [how code violates the principle] + - Fix Required: [specific steps to bring into compliance] + - Affected Files: [list of files] + + QUALITY RECOMMENDATIONS: [count] + + [List recommendations by priority:] + + Recommendation #[n]: + - Category: [Security | Performance | Maintainability | Testing | Documentation | Accessibility] + - Priority: [critical | high | medium | low] + - Issue: [description of the issue] + - Suggestion: [specific actionable suggestion] + - Affected Files: [list of files] + + STRENGTHS: + - [positive aspects of the implementation] + - [things done well] + + FILES REVIEWED: [count] + - [list of all files reviewed] + ``` + +═══════════════════════════════════════════════════════════════════════════════ +PHASE 5: ITERATION (if score < 0.85) +═══════════════════════════════════════════════════════════════════════════════ + +**Objective**: Fix issues until code reaches acceptable quality (score ≥ 0.85) + +**Iteration Rules**: +- Maximum 3 iteration attempts +- Track your current iteration number (1, 2, or 3) +- Each iteration must address ALL requirements and context violations +- After each iteration, return to Phase 4 for re-review + +**If score < 0.85, follow these steps**: + +**Iteration [1, 2, or 3]**: + +1. **Address ALL Requirements and Context Violations (MANDATORY)** + - Requirements and context violations are non-negotiable and must be fixed + - Follow the "Fix Required" instructions from the review + - Bring code into full compliance with requirements and documented principles + - Make ACTUAL file modifications to fix each violation + +2. **Address Critical and High Priority Recommendations** + - Fix security vulnerabilities immediately + - Address performance problems + - Improve code structure and maintainability + - Add missing tests or documentation + +3. **Re-run Verification (MANDATORY - DO NOT SKIP)** + - **Linting**: + - For JS: !{npm run lint:js} + - For PHP: !{composer run lint} + - **Tests** (MUST EXECUTE AND PASS): + - For JS: !{npm run test:js} + - For PHP: !{composer run test} + - **CRITICAL**: You MUST actually run these test commands and verify they pass + - **CRITICAL**: Do NOT proceed to Phase 4 re-review until ALL tests pass + - Ensure fixes don't break existing functionality + +4. **Return to Phase 4** + - Re-review your updated implementation + - Provide new score and updated review results + - Include iteration number in review + +5. **Check Score** + - If score ≥ 0.85: Proceed to Phase 6 + - If score < 0.85 AND iteration < 3: Start next iteration + - If score < 0.85 AND iteration = 3: Execute graceful exit (below) + +**Graceful Exit** (if score < 0.85 after 3 iterations): + +If you cannot achieve a score of 0.85 after 3 iterations, exit with this summary: + +``` +IMPLEMENTATION INCOMPLETE - MANUAL INTERVENTION REQUIRED +========================================================= + +Final Score: [score after 3rd iteration] +Iterations Completed: 3 +Status: Needs manual review and fixes + +SUMMARY OF CHANGES MADE: +All changes have been preserved in the working directory for your review. + +Files Created: [count] +- [list with descriptions] + +Files Modified: [count] +- [list with descriptions] + +Files Deleted: [count] +- [list] + +REMAINING ISSUES: + +Context Violations Still Present: [count] +[List each violation with fix instructions] + +Quality Issues Still Present: [count] +[List critical and high priority issues] + +RECOMMENDATIONS FOR MANUAL FIXES: + +1. [Most important fix needed] + - Files: [list] + - Action: [specific steps] + - Reference: [context doc section] + +2. [Second most important fix] + - Files: [list] + - Action: [specific steps] + +[Continue for top 5 issues] + +WHAT WAS COMPLETED SUCCESSFULLY: +- [aspects that meet quality standards] +- [working features] + +FILES REQUIRING ATTENTION: +Priority 1 (Critical): [files with violations] +Priority 2 (Important): [files with quality issues] +Priority 3 (Polish): [files with minor improvements] + +NEXT STEPS: +1. Review the changes made in the working directory +2. Address the remaining context violations (see above) +3. Fix critical quality issues +4. Re-run tests: npm test +5. Re-run linting: npm run lint +6. Consider manual code review with team +``` + +After providing this summary, STOP. Do not attempt further fixes. + +═══════════════════════════════════════════════════════════════════════════════ +PHASE 6: DOCUMENTATION (optional - only if score ≥ 0.85) +═══════════════════════════════════════════════════════════════════════════════ + +**Objective**: Document new architectural patterns or significant concepts + +**When to create documentation**: +- Only if you introduced NEW architectural patterns not covered in docs/context/ +- Only if you made significant design decisions worth documenting +- Only if the implementation adds complex workflows or processes + +**Skip this phase if**: +- You only implemented standard features using existing patterns +- Everything follows documented conventions in docs/context/ +- No new architectural concepts were introduced + +**If documentation is needed**, follow these documentation guidelines: + +1. **Identify Documentation Needs** + - What new concepts were introduced? + - What architectural decisions were made? + - What complex patterns or workflows were created? + +2. **Create Developer Documentation** + - Location: docs/concepts/ directory + - Format: Markdown (.md files) + - Naming: Use descriptive kebab-case names (e.g., widget-lifecycle-management.md) + +3. **Documentation Structure** + Include these sections: + - **Overview**: What is this concept and why does it exist? + - **Core Principles**: Key architectural principles + - **How It Works**: Technical explanation of the pattern + - **Usage Examples**: Code examples showing how to use it + - **Best Practices**: Guidelines for working with this pattern + - **Common Pitfalls**: What to avoid + - **Related Concepts**: Links to related documentation + +4. **Documentation Style** + - Write for human developers, not AI agents + - Use clear, conversational language + - Include practical code examples + - Focus on WHY and HOW, not just WHAT + - Add diagrams if they help understanding (Mermaid or ASCII) + +5. **Example Documentation File:** + ```markdown + # [Concept Name] + + ## Overview + Brief description of what this is and why it exists. + + ## Core Principles + - Principle 1 + - Principle 2 + + ## Architecture + How this concept fits into the system... + + ## Usage Examples + ```javascript + // Example code + ``` + + ## Best Practices + - Practice 1 + - Practice 2 + + ## Common Pitfalls + - Pitfall 1: Description and how to avoid + + ## Related Concepts + - [Link to related doc] + ``` + +6. **Update Existing Documentation (if needed)** + - If changes affect existing docs/concepts/ files, update them + - Mark deprecated information clearly + - Add new sections rather than replacing content + +═══════════════════════════════════════════════════════════════════════════════ +FINAL SUMMARY +═══════════════════════════════════════════════════════════════════════════════ + +After completing all phases (or exiting gracefully), provide a final summary: + +**If score ≥ 0.85 (Success)**: + +``` +IMPLEMENTATION COMPLETE ✓ +========================= + +Issue #{{args}}: [issue title] +Final Score: [score] +Status: Production-ready +Iterations: [number of iterations needed] + +FILES CHANGED: + +Created ([count]): +- [file path]: [brief description] + +Modified ([count]): +- [file path]: [brief description of changes] + +Deleted ([count]): +- [file path]: [reason] + +IMPLEMENTATION HIGHLIGHTS: +- [key feature 1] +- [key feature 2] +- [notable technical decisions] + +QUALITY ASSURANCE: +✓ All documented principles followed +✓ Comprehensive tests added +✓ Linting passed +✓ Tests passed +✓ Code reviewed and approved + +TEST COVERAGE: +- [areas covered by tests] +- [edge cases handled] + +DOCUMENTATION: +- JSDoc added for all new functions/components +- [Concept docs created if applicable] + +NEXT STEPS FOR DEVELOPER: +1. Review the changes in your working directory +2. Run the full test suite: npm test +3. Run full linting: npm run lint +4. Test manually in browser/environment +5. Create a commit: git add . && git commit -m "Implement issue #{{args}}" +6. Create a pull request if desired +7. Request code review from team + +The implementation is ready for your review and testing! +``` + +**If score < 0.85 after 3 iterations (Graceful Exit)**: +Use the "Graceful Exit" summary format from Phase 5. + +═══════════════════════════════════════════════════════════════════════════════ +IMPORTANT REMINDERS +═══════════════════════════════════════════════════════════════════════════════ + +✓ ACTUALLY CREATE AND MODIFY FILES - do not just describe changes +✓ DO NOT create commits or pull requests - keep all changes local +✓ DO NOT proceed to next phase if previous phase failed critically +✓ DO NOT accept score < 0.85 without trying to fix (up to 3 iterations) +✓ DO read and follow ALL context documentation +✓ DO fix ALL violations of documented principles +✓ DO exit gracefully after 3 iterations if unable to reach 0.85 +✓ DO preserve all changes for human review +✓ MUST RUN TESTS before completing implementation: + - JS: !{npm run test:js} + - PHP: !{composer run test} +✓ DO NOT consider implementation complete until ALL tests pass + +Begin with Phase 1: Fetch GitHub Issue #{{args}} +""" diff --git a/.gemini/settings.json b/.gemini/settings.json index 13a8d903846..5c54359156f 100644 --- a/.gemini/settings.json +++ b/.gemini/settings.json @@ -1,3 +1,46 @@ { - "contextFileName": "AGENTS.md" -} \ No newline at end of file + "context": { + "fileName": [ + "AGENTS.md", + "docs/context/js/component-conventions.md", + "docs/context/js/module-architecture.md", + "docs/context/js/widgets.md", + "docs/context/js/feature-tours.md", + "docs/context/js/jsdoc.md", + "docs/context/js/notifications.md", + "docs/context/js/tests.md", + "docs/context/js/utils.md", + "docs/context/js/state-management.md", + "docs/context/js/hooks.md", + "docs/context/js/storybook.md", + "docs/context/js/event-tracking.md", + "docs/context/js/feature-flags.md", + "docs/context/php/admin-features.md", + "docs/context/php/asset-management.md", + "docs/context/php/context-pattern.md", + "docs/context/php/dependency-injection.md", + "docs/context/php/module-architecture.md", + "docs/context/php/naming-conventions.md", + "docs/context/php/phpunit.md", + "docs/context/php/prompts-and-dismissals.md", + "docs/context/php/rest-api.md", + "docs/context/php/settings-management.md", + "docs/context/php/storage-patterns.md", + "docs/context/php/trait-composition.md" + ] + }, + "mcpServers": { + "github": { + "trust": false, + "httpUrl": "https://api.githubcopilot.com/mcp/?toolsets=issues", + "headers": { + "Authorization": "Bearer ${GITHUB_TOKEN}" + }, + "includeTools": [ + "issue_read", + "list_issues", + "search_issues" + ] + } + } +} diff --git a/.github/workflows/gemini.yml b/.github/workflows/gemini.yml new file mode 100644 index 00000000000..89668335475 --- /dev/null +++ b/.github/workflows/gemini.yml @@ -0,0 +1,133 @@ +name: Gemini Implement Issue + +on: + workflow_dispatch: + inputs: + issue_number: + description: GitHub issue number to implement + required: true + type: number + +jobs: + implement: + name: Implement Issue + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: read + issues: read + services: + mysql: + image: mysql:5.7 + env: + MYSQL_ROOT_PASSWORD: wordpress + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + env: + DB_HOST: 127.0.0.1 + DB_PORT: 3306 + MYSQL_USER: root + MYSQL_PASSWORD: wordpress + MYSQL_DATABASE: wordpress_test + WP_VERSION: latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: npm + - name: Install SVN + run: sudo apt-get update && sudo apt-get install -y subversion + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '7.4' + extensions: mysqli, runkit7, uopz + tools: composer:2.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Get Composer Cache Directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT" + - name: Cache Composer dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer- + - name: Validate Composer configuration + run: composer validate --strict + - name: Install Composer dependencies + run: composer install --no-interaction --no-progress + - name: Install npm dependencies + run: npm ci -w assets -w storybook -w tests/js -w tests/e2e --include-workspace-root + - name: Set up WordPress test data + run: tests/phpunit/bin/install-wp-tests.sh "${MYSQL_DATABASE}" "${MYSQL_USER}" "${MYSQL_PASSWORD}" "${DB_HOST}":"${DB_PORT}" "${WP_VERSION}" + - name: Install Gemini CLI + run: | + npm install -g @google/gemini-cli@0.23.0 + gemini --version + - name: Run /implement command + run: | + gemini --yolo --model gemini-3-pro-preview "/implement ${{ inputs.issue_number }}" + env: + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Create patch + run: | + git add . + git diff --staged --binary > implementation.patch + - name: Upload patch + uses: actions/upload-artifact@v4 + with: + name: implementation-patch + path: implementation.patch + + create-pr: + name: Create Pull Request + needs: implement + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + issues: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Download patch + uses: actions/download-artifact@v4 + with: + name: implementation-patch + - name: Apply patch + run: git apply implementation.patch + - name: Create Pull Request + id: create-pr + uses: peter-evans/create-pull-request@v7 + with: + commit-message: 'Implement issue #${{ inputs.issue_number }}' + branch: gemini/issue-${{ inputs.issue_number }} + title: 'Implement #${{ inputs.issue_number }}' + body: | + 🤖 Automated implementation of issue #${{ inputs.issue_number }} + + Generated by Gemini CLI using the `/implement` command. + + **Review checklist:** + - [ ] Code follows documented principles + - [ ] Tests pass locally + - [ ] Linting passes + - [ ] Manual testing completed + - [ ] Code review score ≥ 0.85 + draft: true + - name: Comment on issue + if: steps.create-pr.outputs.pull-request-number + run: | + gh issue comment ${{ inputs.issue_number }} --body "🤖 Automated implementation created in PR #${{ steps.create-pr.outputs.pull-request-number }} + + This is a draft PR generated by Gemini CLI. Please review the changes, run tests, and verify quality before marking ready for review." + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index af07ffaead6..5e61112ac23 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ !.gemini/ .gemini/* !.gemini/settings.json +!.gemini/commands/ +!.gemini/commands/** !.github !.vscode .vscode/* diff --git a/docs/context/js/hooks.md b/docs/context/js/hooks.md index a1c72260a1c..d0d6f8f365b 100644 --- a/docs/context/js/hooks.md +++ b/docs/context/js/hooks.md @@ -422,10 +422,10 @@ import { useSelect, useDispatch } from 'googlesitekit-data'; /** * Custom hook description. * - * @since 1.25.0 + * \@since 1.25.0 * - * @param {string} parameter Description of parameter. - * @return {*} Description of return value. + * \@param {string} parameter Description of parameter. + * \@return {*} Description of return value. */ export default function useCustomHook(parameter) { // Hook implementation diff --git a/docs/context/js/jsdoc.md b/docs/context/js/jsdoc.md index 1652858feb4..b71e7351c95 100644 --- a/docs/context/js/jsdoc.md +++ b/docs/context/js/jsdoc.md @@ -38,11 +38,11 @@ All utility functions, hooks, and complex API functions should follow this patte * * [Brief description of function purpose] * - * @since n.e.x.t + * \@since n.e.x.t * - * @param {type} paramName Description of parameter. - * @param {type} [optionalParam] Description of optional parameter. - * @return {type} Description of return value. + * \@param {type} paramName Description of parameter. + * \@param {type} [optionalParam] Description of optional parameter. + * \@return {type} Description of return value. */ function myFunction( paramName, optionalParam = defaultValue ) { // implementation @@ -51,66 +51,66 @@ function myFunction( paramName, optionalParam = defaultValue ) { ### Required JSDoc Tags -#### @since Tag +#### \@since Tag **Always required** - Documents when the feature was introduced, always use `n.e.x.t` value which will be replaced with the actual version later on. ```javascript /** * Returns a callback to activate a module. * - * @since n.e.x.t + * \@since n.e.x.t * - * @param {string} moduleSlug Module slug. - * @return {Function|null} Callback to activate module. + * \@param {string} moduleSlug Module slug. + * \@return {Function|null} Callback to activate module. */ ``` -#### @param Tag +#### \@param Tag **Required for all parameters** - Documents parameter types and descriptions: ```javascript // Basic parameter -@param {string} dateRange The date range slug. +\@param {string} dateRange The date range slug. // Optional parameter with default -@param {boolean} [invertColor=false] Whether to reverse the +/- colors. +\@param {boolean} [invertColor=false] Whether to reverse the +/- colors. // Complex object parameter -@param {Object} options Configuration options. -@param {string} options.baseName The base name to use. -@param {Function} options.controlCallback Callback function to issue the API request. -@param {Function} [options.validateParams] Optional validation function. +\@param {Object} options Configuration options. +\@param {string} options.baseName The base name to use. +\@param {Function} options.controlCallback Callback function to issue the API request. +\@param {Function} [options.validateParams] Optional validation function. ``` -#### @return Tag +#### \@return Tag **Required for functions that return values** - Documents return type and description: ```javascript -@return {Object} Partial store object with properties 'actions', 'controls', and 'reducer'. -@return {Function|null} Callback function or null if module doesn't exist. -@return {Array.} Array of widget objects. +\@return {Object} Partial store object with properties 'actions', 'controls', and 'reducer'. +\@return {Function|null} Callback function or null if module doesn't exist. +\@return {Array.} Array of widget objects. ``` ### Complex Type Documentation -#### @typedef for Complex Data Structures +#### \@typedef for Complex Data Structures -Use `@typedef` to define complex object structures: +Use `\@typedef` to define complex object structures: ```javascript /** * Parse Analytics 4 report into data suitable for rendering. * - * @typedef {Object} OverallPageMetricsData - * @property {string} metric Google Analytics metric identifier. - * @property {string} title Translated metric title. - * @property {Array.} sparkLineData Data for rendering the sparkline. - * @property {string} [datapointUnit] Optional datapoint unit, e.g. '%', 's'. - * @property {number} total Total count for the metric. - * @property {number} change Monthly change for the metric. + * \@typedef {Object} OverallPageMetricsData + * \@property {string} metric Google Analytics metric identifier. + * \@property {string} title Translated metric title. + * \@property {Array.} sparkLineData Data for rendering the sparkline. + * \@property {string} [datapointUnit] Optional datapoint unit, e.g. '%', 's'. + * \@property {number} total Total count for the metric. + * \@property {number} change Monthly change for the metric. * - * @param {Object} report Raw Analytics report data. - * @return {OverallPageMetricsData} Processed metrics data. + * \@param {Object} report Raw Analytics report data. + * \@return {OverallPageMetricsData} Processed metrics data. */ function parseReportData( report ) { // implementation @@ -126,10 +126,10 @@ Custom hooks require comprehensive JSDoc documentation: * Returns a callback to activate a module. If the call to activate the module * fails, an error will be returned to the returned callback. * - * @since n.e.x.t + * \@since n.e.x.t * - * @param {string} moduleSlug Module slug. - * @return {Function|null} Callback to activate module, null if the module doesn't exist. + * \@param {string} moduleSlug Module slug. + * \@return {Function|null} Callback to activate module, null if the module doesn't exist. */ export default function useActivateModuleCallback( moduleSlug ) { // hook implementation @@ -145,15 +145,15 @@ Utility functions require detailed documentation with examples for complex cases * Creates a store object implementing the necessary infrastructure for making * asynchronous API requests and storing their data. * - * @since n.e.x.t + * \@since n.e.x.t * - * @param {Object} args Arguments for creating the fetch store. - * @param {string} args.baseName The base name to use for all actions. - * @param {Function} args.controlCallback Callback function to issue the API request. - * @param {Function} [args.reducerCallback] Optional reducer to modify state. - * @param {Function} [args.argsToParams] Function that reduces argument list to params. - * @param {Function} [args.validateParams] Function that validates params before request. - * @return {Object} Partial store object with properties 'actions', 'controls', and 'reducer'. + * \@param {Object} args Arguments for creating the fetch store. + * \@param {string} args.baseName The base name to use for all actions. + * \@param {Function} args.controlCallback Callback function to issue the API request. + * \@param {Function} [args.reducerCallback] Optional reducer to modify state. + * \@param {Function} [args.argsToParams] Function that reduces argument list to params. + * \@param {Function} [args.validateParams] Function that validates params before request. + * \@return {Object} Partial store object with properties 'actions', 'controls', and 'reducer'. */ export default function createFetchStore( { baseName, @@ -199,7 +199,7 @@ export default function createFetchStore( { 4. **Create typedefs** for complex recurring data structures ### Version Tracking -1. **Always include @since** for new functions and significant changes using `n.e.x.t` value +1. **Always include \@since** for new functions and significant changes using `n.e.x.t` value 3. **Document breaking changes** in function descriptions ### Consistency Rules diff --git a/docs/context/php/asset-management.md b/docs/context/php/asset-management.md index e1c3feccef1..e3bc7eb9f45 100644 --- a/docs/context/php/asset-management.md +++ b/docs/context/php/asset-management.md @@ -598,14 +598,14 @@ interface Module_With_Assets { /** * Get assets to register for the module. * - * @return Asset[] Array of Asset instances. + * \@return Asset[] Array of Asset instances. */ public function get_assets(); /** * Enqueue all assets for the module. * - * @param string $asset_context Context constant (Asset::CONTEXT_*). + * \@param string $asset_context Context constant (Asset::CONTEXT_*). */ public function enqueue_assets( $asset_context = Asset::CONTEXT_ADMIN_SITEKIT ); } @@ -651,7 +651,7 @@ trait Module_With_Assets_Trait { /** * Set up module assets. * - * @return Asset[] Array of Asset instances. + * \@return Asset[] Array of Asset instances. */ abstract protected function setup_assets(); } @@ -688,8 +688,8 @@ interface Module_With_Inline_Data { /** * Get inline data for the module. * - * @param array $modules_data Existing modules data. - * @return array Updated modules data with module's data added. + * \@param array $modules_data Existing modules data. + * \@return array Updated modules data with module's data added. */ public function get_inline_data( $modules_data ); } @@ -901,8 +901,8 @@ final class Manifest { /** * Get manifest entry for asset handle. * - * @param string $handle Asset handle. - * @return array [ $filename, $hash ] or [ null, null ] if not found. + * \@param string $handle Asset handle. + * \@return array [ $filename, $hash ] or [ null, null ] if not found. */ public static function get( $handle ) { if ( null === self::$data ) { diff --git a/docs/context/php/context-pattern.md b/docs/context/php/context-pattern.md index 6402072ec67..9e6dc0364aa 100644 --- a/docs/context/php/context-pattern.md +++ b/docs/context/php/context-pattern.md @@ -28,8 +28,8 @@ The Context object provides five main categories of functionality: /** * Get absolute path to plugin directory or file. * - * @param string $relative_path Optional. Relative path within plugin. Default '/'. - * @return string Absolute path. + * \@param string $relative_path Optional. Relative path within plugin. Default '/'. + * \@return string Absolute path. */ public function path( $relative_path = '/' ) ``` @@ -58,8 +58,8 @@ $assets_dir = $context->path( 'dist/assets/' ); /** * Get URL to plugin directory or file. * - * @param string $relative_path Optional. Relative path within plugin. Default '/'. - * @return string URL. + * \@param string $relative_path Optional. Relative path within plugin. Default '/'. + * \@return string URL. */ public function url( $relative_path = '/' ) ``` @@ -84,9 +84,9 @@ $script_url = $context->url( 'dist/assets/js/googlesitekit-dashboard.js' ); /** * Get admin URL for a specific Site Kit page. * - * @param string $slug Page slug (e.g., 'dashboard', 'settings'). - * @param array $query_args Optional query parameters. - * @return string Admin URL. + * \@param string $slug Page slug (e.g., 'dashboard', 'settings'). + * \@param array $query_args Optional query parameters. + * \@return string Admin URL. */ public function admin_url( $slug = 'dashboard', array $query_args = array() ) ``` @@ -116,14 +116,14 @@ $settings_url = $context->admin_url( 'settings', array( /** * Check if current request is an AMP request. * - * @return bool True if AMP request. + * \@return bool True if AMP request. */ public function is_amp() /** * Get the AMP mode for the site. * - * @return string 'primary', 'secondary', or empty string if not AMP. + * \@return string 'primary', 'secondary', or empty string if not AMP. */ public function get_amp_mode() ``` @@ -152,14 +152,14 @@ if ( 'primary' === $amp_mode ) { /** * Check if plugin is in network mode (multisite). * - * @return bool True if network mode. + * \@return bool True if network mode. */ public function is_network_mode() /** * Check if plugin is network active. * - * @return bool True if network active. + * \@return bool True if network active. */ public function is_network_active() ``` @@ -190,7 +190,7 @@ if ( $context->is_network_active() ) { /** * Get the reference site URL for the current request context. * - * @return string Reference site URL. + * \@return string Reference site URL. */ public function get_reference_site_url() ``` @@ -235,7 +235,7 @@ private function filter_reference_url( $url = '' ) { /** * Get the canonical home URL. * - * @return string Canonical home URL. + * \@return string Canonical home URL. */ public function get_canonical_home_url() ``` @@ -255,7 +255,7 @@ $home_url = $context->get_canonical_home_url(); /** * Get the reference entity for the current context. * - * @return array Entity information array. + * \@return array Entity information array. */ public function get_reference_entity() ``` @@ -279,9 +279,9 @@ $entity = $context->get_reference_entity(); /** * Get locale for a specific context. * - * @param string $context 'site' or 'user'. Default 'site'. - * @param string $format 'language-code' or 'default'. Default 'default'. - * @return string Locale string. + * \@param string $context 'site' or 'user'. Default 'site'. + * \@param string $format 'language-code' or 'default'. Default 'default'. + * \@return string Locale string. */ public function get_locale( $context = 'site', $format = 'default' ) ``` @@ -314,11 +314,11 @@ The Context provides a safe abstraction for accessing superglobals (GET, POST, e /** * Gets a specific external variable by name and optionally filters it. * - * @param int $type One of INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV. - * @param string $variable_name Name of a variable to get. - * @param int $filter [optional] The ID of the filter to apply. Default FILTER_DEFAULT. - * @param mixed $options [optional] Associative array of options or bitwise disjunction of flags. - * @return mixed Value of the requested variable on success. + * \@param int $type One of INPUT_GET, INPUT_POST, INPUT_COOKIE, INPUT_SERVER, or INPUT_ENV. + * \@param string $variable_name Name of a variable to get. + * \@param int $filter [optional] The ID of the filter to apply. Default FILTER_DEFAULT. + * \@param mixed $options [optional] Associative array of options or bitwise disjunction of flags. + * \@return mixed Value of the requested variable on success. */ public function filter( $type, $variable_name, $filter = FILTER_DEFAULT, $options = 0 ) ``` diff --git a/docs/context/php/dependency-injection.md b/docs/context/php/dependency-injection.md index 0dce6d2614d..8738892ac5a 100644 --- a/docs/context/php/dependency-injection.md +++ b/docs/context/php/dependency-injection.md @@ -419,12 +419,12 @@ class MyClass { /** * Constructor. * - * @since 1.0.0 + * \@since 1.0.0 * - * @param Context $context Plugin context instance. - * @param Options $options Optional. Options instance. Default is a new instance. - * @param User_Options $user_options Optional. User options instance. Default is a new instance. - * @param Authentication $authentication Optional. Authentication instance. Default is a new instance. + * \@param Context $context Plugin context instance. + * \@param Options $options Optional. Options instance. Default is a new instance. + * \@param User_Options $user_options Optional. User options instance. Default is a new instance. + * \@param Authentication $authentication Optional. Authentication instance. Default is a new instance. */ public function __construct( Context $context, @@ -459,8 +459,8 @@ public function __construct( ```php /** - * @param Context $context Plugin context. - * @param Options $options Settings storage. + * \@param Context $context Plugin context. + * \@param Options $options Settings storage. */ ``` diff --git a/docs/context/php/module-architecture.md b/docs/context/php/module-architecture.md index 54a3ced28de..8f3dec3f736 100644 --- a/docs/context/php/module-architecture.md +++ b/docs/context/php/module-architecture.md @@ -118,7 +118,7 @@ interface Module_With_Settings { /** * Get module settings instance. * - * @return Module_Settings + * \@return Module_Settings */ public function get_settings(); } @@ -153,7 +153,7 @@ interface Module_With_Scopes { /** * Get required OAuth scopes. * - * @return array List of Google OAuth scopes. + * \@return array List of Google OAuth scopes. */ public function get_scopes(); } @@ -187,7 +187,7 @@ interface Module_With_Assets { /** * Get module assets to enqueue. * - * @return array Array of Asset objects. + * \@return array Array of Asset objects. */ public function get_assets(); } @@ -228,7 +228,7 @@ interface Module_With_Tag { /** * Get the module tag instance. * - * @return Module_Tag + * \@return Module_Tag */ public function get_tag(); } @@ -259,7 +259,7 @@ interface Module_With_Service_Entity { /** * Get the service entity access. * - * @return Service_Entity_Access + * \@return Service_Entity_Access */ public function get_service_entity(); } @@ -278,7 +278,7 @@ interface Module_With_Inline_Data { /** * Get inline data for the module. * - * @return array Associative array of inline data. + * \@return array Associative array of inline data. */ public function get_inline_data(); } @@ -316,7 +316,7 @@ interface Provides_Feature_Metrics { /** * Get the feature metrics instance. * - * @return Feature_Metrics + * \@return Feature_Metrics */ public function get_feature_metrics(); } diff --git a/docs/context/php/naming-conventions.md b/docs/context/php/naming-conventions.md index 09179d80f6d..16e2d8529b4 100644 --- a/docs/context/php/naming-conventions.md +++ b/docs/context/php/naming-conventions.md @@ -514,7 +514,7 @@ function googlesitekit_is_network_mode() { } /** * Class for managing module settings. * - * @since 1.0.0 + * \@since 1.0.0 * @access private * @ignore */ @@ -527,11 +527,11 @@ final class Module_Settings extends Setting { /** * Get module settings. * - * @since 1.0.0 + * \@since 1.0.0 * - * @param string $key Optional. Setting key. Default empty string. - * @param mixed $default Optional. Default value. Default null. - * @return mixed Setting value or default. + * \@param string $key Optional. Setting key. Default empty string. + * \@param mixed $default Optional. Default value. Default null. + * \@return mixed Setting value or default. */ public function get( $key = '', $default = null ) { ``` @@ -542,24 +542,24 @@ public function get( $key = '', $default = null ) { /** * Plugin context instance. * - * @since 1.0.0 - * @var Context + * \@since 1.0.0 + * \@var Context */ private $context; ``` -### @since Tag Convention +### \@since Tag Convention -For all new code (classes, methods, properties, constants), use `@since n.e.x.t` as a placeholder. +For all new code (classes, methods, properties, constants), use `\@since n.e.x.t` as a placeholder. ```php /** * New method added in development. * - * @since n.e.x.t + * \@since n.e.x.t * - * @param string $param Parameter description. - * @return bool True on success, false otherwise. + * \@param string $param Parameter description. + * \@return bool True on success, false otherwise. */ public function new_method( $param ) { ``` @@ -568,7 +568,7 @@ public function new_method( $param ) { /** * New class added in development. * - * @since n.e.x.t + * \@since n.e.x.t */ final class New_Feature { ``` @@ -577,8 +577,8 @@ final class New_Feature { /** * New property added in development. * - * @since n.e.x.t - * @var string + * \@since n.e.x.t + * \@var string */ private $new_property; ``` diff --git a/docs/context/php/phpunit.md b/docs/context/php/phpunit.md index bf4fa4a7e2b..ec75e971c69 100644 --- a/docs/context/php/phpunit.md +++ b/docs/context/php/phpunit.md @@ -107,7 +107,7 @@ abstract class SettingsTestCase extends TestCase { /** * Get the option name for the setting. * - * @return string + * \@return string */ abstract protected function get_option_name(); diff --git a/docs/context/php/prompts-and-dismissals.md b/docs/context/php/prompts-and-dismissals.md index 1ab9a452bfd..98585ceaa1d 100644 --- a/docs/context/php/prompts-and-dismissals.md +++ b/docs/context/php/prompts-and-dismissals.md @@ -68,9 +68,9 @@ final class Dismissed_Prompts extends User_Setting { /** * Add or update a dismissed prompt. * - * @param string $prompt Prompt slug. - * @param int $expires_in_seconds Expiration in seconds (0 = permanent). - * @return bool True on success. + * \@param string $prompt Prompt slug. + * \@param int $expires_in_seconds Expiration in seconds (0 = permanent). + * \@return bool True on success. */ public function add( $prompt, $expires_in_seconds = self::DISMISS_PROMPT_PERMANENTLY ) { $prompts = $this->get(); @@ -98,8 +98,8 @@ final class Dismissed_Prompts extends User_Setting { /** * Remove a dismissed prompt. * - * @param string $prompt Prompt slug. - * @return bool True on success. + * \@param string $prompt Prompt slug. + * \@return bool True on success. */ public function remove( $prompt ) { $prompts = $this->get(); @@ -115,7 +115,7 @@ final class Dismissed_Prompts extends User_Setting { /** * Get all dismissed prompts. * - * @return array Dismissed prompts with metadata. + * \@return array Dismissed prompts with metadata. */ public function get() { return parent::get() ?: array(); @@ -124,7 +124,7 @@ final class Dismissed_Prompts extends User_Setting { /** * Get default value. * - * @return array Empty array. + * \@return array Empty array. */ protected function get_default() { return array(); @@ -133,8 +133,8 @@ final class Dismissed_Prompts extends User_Setting { /** * Sanitize prompts data. * - * @param array $prompts Prompts data. - * @return array Sanitized prompts. + * \@param array $prompts Prompts data. + * \@return array Sanitized prompts. */ protected function sanitize_callback( $prompts ) { if ( ! is_array( $prompts ) ) { @@ -313,9 +313,9 @@ function AdBlockingRecoveryWidget() { /** * Dismiss a prompt. * - * @param {string} slug Prompt slug. - * @param {Object} options Options object. - * @param {number} options.expiresInSeconds Expiration in seconds (0 = permanent). + * \@param {string} slug Prompt slug. + * \@param {Object} options Options object. + * \@param {number} options.expiresInSeconds Expiration in seconds (0 = permanent). */ *dismissPrompt( slug, { expiresInSeconds = 0 } = {} ) ``` @@ -341,31 +341,31 @@ dismissPrompt( 'my-prompt', { expiresInSeconds: 86400 } ); /** * Get all active dismissed prompts (filters expired). * - * @return {Array} Array of prompt slugs. + * \@return {Array} Array of prompt slugs. */ getDismissedPrompts(); /** * Get dismiss count for a prompt. * - * @param {string} slug Prompt slug. - * @return {number} Number of times dismissed. + * \@param {string} slug Prompt slug. + * \@return {number} Number of times dismissed. */ getPromptDismissCount( slug ); /** * Check if prompt is dismissed. * - * @param {string} slug Prompt slug. - * @return {boolean} True if dismissed and not expired. + * \@param {string} slug Prompt slug. + * \@return {boolean} True if dismissed and not expired. */ isPromptDismissed( slug ); /** * Check if currently dismissing a prompt. * - * @param {string} slug Prompt slug. - * @return {boolean} True if dismissing. + * \@param {string} slug Prompt slug. + * \@return {boolean} True if dismissing. */ isDismissingPrompt( slug ); ``` @@ -464,9 +464,9 @@ final class Dismissed_Items extends User_Setting { /** * Add or update a dismissed item. * - * @param string $item Item slug. - * @param int $expires_in_seconds Expiration in seconds (0 = permanent). - * @return bool True on success. + * \@param string $item Item slug. + * \@param int $expires_in_seconds Expiration in seconds (0 = permanent). + * \@return bool True on success. */ public function add( $item, $expires_in_seconds = self::DISMISS_ITEM_PERMANENTLY ) { $items = $this->get(); @@ -483,8 +483,8 @@ final class Dismissed_Items extends User_Setting { /** * Remove a dismissed item. * - * @param string $item Item slug. - * @return bool True on success. + * \@param string $item Item slug. + * \@return bool True on success. */ public function remove( $item ) { $items = $this->get(); @@ -500,7 +500,7 @@ final class Dismissed_Items extends User_Setting { /** * Get all dismissed items (including expired). * - * @return array Dismissed items with expiration values. + * \@return array Dismissed items with expiration values. */ public function get() { return parent::get() ?: array(); @@ -509,8 +509,8 @@ final class Dismissed_Items extends User_Setting { /** * Check if item is dismissed and not expired. * - * @param string $item Item slug. - * @return bool True if dismissed and not expired. + * \@param string $item Item slug. + * \@return bool True if dismissed and not expired. */ public function is_dismissed( $item ) { $items = $this->get(); @@ -528,7 +528,7 @@ final class Dismissed_Items extends User_Setting { /** * Get only active dismissed items (filters expired). * - * @return array Array of item slugs. + * \@return array Array of item slugs. */ public function get_dismissed_items() { $items = $this->get(); @@ -538,8 +538,8 @@ final class Dismissed_Items extends User_Setting { /** * Filter out expired items. * - * @param array $items Items with expiration values. - * @return array Filtered items. + * \@param array $items Items with expiration values. + * \@return array Filtered items. */ private function filter_dismissed_items( $items ) { return array_filter( @@ -553,7 +553,7 @@ final class Dismissed_Items extends User_Setting { /** * Get default value. * - * @return array Empty array. + * \@return array Empty array. */ protected function get_default() { return array(); @@ -562,8 +562,8 @@ final class Dismissed_Items extends User_Setting { /** * Sanitize items data. * - * @param array $items Items data. - * @return array Sanitized items. + * \@param array $items Items data. + * \@return array Sanitized items. */ protected function sanitize_callback( $items ) { if ( ! is_array( $items ) ) { @@ -805,16 +805,16 @@ function MyNotice() { /** * Dismiss an item. * - * @param {string} slug Item slug. - * @param {Object} options Options object. - * @param {number} options.expiresInSeconds Expiration in seconds (0 = permanent). + * \@param {string} slug Item slug. + * \@param {Object} options Options object. + * \@param {number} options.expiresInSeconds Expiration in seconds (0 = permanent). */ *dismissItem( slug, { expiresInSeconds = 0 } = {} ) /** * Remove dismissed items. * - * @param {...string} slugs Item slugs to remove. + * \@param {...string} slugs Item slugs to remove. */ *removeDismissedItems( ...slugs ) ``` @@ -843,23 +843,23 @@ removeDismissedItems( 'item-1', 'item-2' ); /** * Get all active dismissed items (filters expired). * - * @return {Array} Array of item slugs. + * \@return {Array} Array of item slugs. */ getDismissedItems(); /** * Check if item is dismissed. * - * @param {string} slug Item slug. - * @return {boolean} True if dismissed and not expired. + * \@param {string} slug Item slug. + * \@return {boolean} True if dismissed and not expired. */ isItemDismissed( slug ); /** * Check if currently dismissing an item. * - * @param {string} slug Item slug. - * @return {boolean} True if dismissing. + * \@param {string} slug Item slug. + * \@return {boolean} True if dismissing. */ isDismissingItem( slug ); ``` diff --git a/docs/context/php/rest-api.md b/docs/context/php/rest-api.md index dd472ab4171..2f2dd113550 100644 --- a/docs/context/php/rest-api.md +++ b/docs/context/php/rest-api.md @@ -64,7 +64,7 @@ final class REST_Routes { /** * Filters the list of available REST routes. * - * @param array $routes List of REST_Route objects. + * \@param array $routes List of REST_Route objects. */ return apply_filters( 'googlesitekit_rest_routes', $routes ); } @@ -85,9 +85,9 @@ final class REST_Route { /** * Constructor. * - * @param string $uri Route URI pattern. - * @param array $endpoints Route endpoints configuration. - * @param array $args Optional route arguments. + * \@param string $uri Route URI pattern. + * \@param array $endpoints Route endpoints configuration. + * \@param array $args Optional route arguments. */ public function __construct( $uri, array $endpoints, array $args = array() ) { $this->uri = trim( $uri, '/' ); @@ -135,8 +135,8 @@ final class REST_Route { /** * Parse parameter arguments to ensure proper schema. * - * @param array $args Parameter arguments. - * @return array Parsed arguments. + * \@param array $args Parameter arguments. + * \@return array Parsed arguments. */ private function parse_param_args( $args ) { $parsed_args = array(); @@ -185,7 +185,7 @@ class REST_Feature_Controller { /** * Get REST route definitions. * - * @return array Array of REST_Route objects. + * \@return array Array of REST_Route objects. */ private function get_rest_routes() { // Permission callbacks @@ -241,8 +241,8 @@ class REST_Feature_Controller { /** * GET callback for list endpoint. * - * @param WP_REST_Request $request REST request object. - * @return WP_REST_Response|WP_Error Response object or error. + * \@param WP_REST_Request $request REST request object. + * \@return WP_REST_Response|WP_Error Response object or error. */ public function get_list( WP_REST_Request $request ) { $status = $request->get_param( 'status' ); @@ -256,8 +256,8 @@ class REST_Feature_Controller { /** * POST callback for save endpoint. * - * @param WP_REST_Request $request REST request object. - * @return WP_REST_Response|WP_Error Response object or error. + * \@param WP_REST_Request $request REST request object. + * \@return WP_REST_Response|WP_Error Response object or error. */ public function save_item( WP_REST_Request $request ) { $name = $request->get_param( 'name' ); @@ -360,7 +360,7 @@ final class REST_Modules_Controller { /** * Get list of modules. * - * @return WP_REST_Response Response with modules list. + * \@return WP_REST_Response Response with modules list. */ public function get_modules() { $modules = array_values( @@ -373,8 +373,8 @@ final class REST_Modules_Controller { /** * Activate a module. * - * @param WP_REST_Request $request REST request. - * @return WP_REST_Response|WP_Error Response or error. + * \@param WP_REST_Request $request REST request. + * \@return WP_REST_Response|WP_Error Response or error. */ public function activate_module( WP_REST_Request $request ) { $slug = $request->get_param( 'slug' ); diff --git a/docs/context/php/settings-management.md b/docs/context/php/settings-management.md index fc30c74e5fb..e05d8b1f0d4 100644 --- a/docs/context/php/settings-management.md +++ b/docs/context/php/settings-management.md @@ -51,7 +51,7 @@ abstract class Setting { /** * Check if setting exists in database. * - * @return bool True if setting exists. + * \@return bool True if setting exists. */ public function has() { $value = $this->get(); @@ -63,7 +63,7 @@ abstract class Setting { /** * Get setting value. * - * @return mixed Setting value. + * \@return mixed Setting value. */ public function get() { $option = $this->get_option(); @@ -78,8 +78,8 @@ abstract class Setting { /** * Set setting value. * - * @param mixed $value New value. - * @return bool True on success. + * \@param mixed $value New value. + * \@return bool True on success. */ public function set( $value ) { return $this->update_option( $value ); @@ -88,7 +88,7 @@ abstract class Setting { /** * Delete setting. * - * @return bool True on success. + * \@return bool True on success. */ public function delete() { return $this->delete_option(); @@ -97,8 +97,8 @@ abstract class Setting { /** * Register callback for setting changes. * - * @param callable $callback Function to call when setting changes. - * @return callable Unsubscribe function. + * \@param callable $callback Function to call when setting changes. + * \@return callable Unsubscribe function. */ public function on_change( callable $callback ) { // Observer pattern implementation @@ -107,7 +107,7 @@ abstract class Setting { /** * Get setting type. * - * @return string Setting type (string, number, integer, boolean, array, object). + * \@return string Setting type (string, number, integer, boolean, array, object). */ protected function get_type() { return 'array'; @@ -116,7 +116,7 @@ abstract class Setting { /** * Get default value. * - * @return mixed Default value. + * \@return mixed Default value. */ protected function get_default() { return array(); @@ -125,7 +125,7 @@ abstract class Setting { /** * Get sanitization callback. * - * @return callable Sanitization function. + * \@return callable Sanitization function. */ protected function get_sanitize_callback() { return null; @@ -174,8 +174,8 @@ abstract class Module_Settings extends Setting { /** * Merge partial settings with existing settings. * - * @param array $partial Partial settings to merge. - * @return bool True on success. + * \@param array $partial Partial settings to merge. + * \@return bool True on success. */ public function merge( array $partial ) { $settings = $this->get(); @@ -197,7 +197,7 @@ abstract class Module_Settings extends Setting { /** * Check if any settings have changed from their saved values. * - * @return bool True if any setting has changed. + * \@return bool True if any setting has changed. */ public function have_changed() { $settings = $this->get(); @@ -209,7 +209,7 @@ abstract class Module_Settings extends Setting { /** * Get saved settings (before any modifications). * - * @return array Saved settings. + * \@return array Saved settings. */ protected function get_saved() { return $this->get(); @@ -253,7 +253,7 @@ final class Settings extends Module_Settings { /** * Get default settings. * - * @return array Default settings. + * \@return array Default settings. */ protected function get_default() { return array( @@ -275,7 +275,7 @@ final class Settings extends Module_Settings { /** * Get setting type. * - * @return string Setting type. + * \@return string Setting type. */ protected function get_type() { return 'array'; @@ -284,7 +284,7 @@ final class Settings extends Module_Settings { /** * Get sanitization callback. * - * @return callable Sanitization function. + * \@return callable Sanitization function. */ protected function get_sanitize_callback() { return function ( $option ) { @@ -319,8 +319,8 @@ final class Settings extends Module_Settings { /** * Sanitize property ID. * - * @param string $property_id Property ID to sanitize. - * @return string Sanitized property ID. + * \@param string $property_id Property ID to sanitize. + * \@return string Sanitized property ID. */ private function sanitize_property_id( $property_id ) { // Allow special values @@ -349,7 +349,7 @@ final class Analytics_4 extends Module implements Module_With_Settings { /** * Set up module settings. * - * @return Module_Settings Settings instance. + * \@return Module_Settings Settings instance. */ protected function setup_settings() { return new Settings( $this->options ); @@ -386,7 +386,7 @@ trait Setting_With_Owned_Keys_Trait { /** * Get owned setting keys. * - * @return array List of owned setting keys. + * \@return array List of owned setting keys. */ protected function get_owned_keys() { return array(); @@ -395,7 +395,7 @@ trait Setting_With_Owned_Keys_Trait { /** * Check if owned settings have changed. * - * @return bool True if owned settings changed. + * \@return bool True if owned settings changed. */ public function have_owned_settings_changed() { $settings = $this->get(); @@ -448,7 +448,7 @@ interface Setting_With_ViewOnly_Keys_Interface { /** * Get view-only setting keys. * - * @return array List of view-only keys. + * \@return array List of view-only keys. */ public function get_view_only_keys(); } diff --git a/docs/context/php/storage-patterns.md b/docs/context/php/storage-patterns.md index c96a3355cc0..c63e51e6cbb 100644 --- a/docs/context/php/storage-patterns.md +++ b/docs/context/php/storage-patterns.md @@ -42,8 +42,8 @@ final class Options implements Options_Interface { /** * Get option value. * - * @param string $option Option name. - * @return mixed Option value or false if not found. + * \@param string $option Option name. + * \@return mixed Option value or false if not found. */ public function get( $option ) { if ( $this->context->is_network_mode() ) { @@ -55,9 +55,9 @@ final class Options implements Options_Interface { /** * Set option value. * - * @param string $option Option name. - * @param mixed $value Option value. - * @return bool True on success. + * \@param string $option Option name. + * \@param mixed $value Option value. + * \@return bool True on success. */ public function set( $option, $value ) { if ( $this->context->is_network_mode() ) { @@ -69,8 +69,8 @@ final class Options implements Options_Interface { /** * Delete option. * - * @param string $option Option name. - * @return bool True on success. + * \@param string $option Option name. + * \@return bool True on success. */ public function delete( $option ) { if ( $this->context->is_network_mode() ) { @@ -82,8 +82,8 @@ final class Options implements Options_Interface { /** * Check if option exists. * - * @param string $option Option name. - * @return bool True if option exists. + * \@param string $option Option name. + * \@return bool True if option exists. */ public function has( $option ) { $value = $this->get( $option ); @@ -176,8 +176,8 @@ final class User_Options implements User_Options_Interface { /** * Get option for the current user. * - * @param string $option Option name. - * @return mixed Option value or false. + * \@param string $option Option name. + * \@return mixed Option value or false. */ public function get( $option ) { $user_id = $this->get_user_id(); @@ -196,9 +196,9 @@ final class User_Options implements User_Options_Interface { /** * Set option for the current user. * - * @param string $option Option name. - * @param mixed $value Option value. - * @return bool True on success. + * \@param string $option Option name. + * \@param mixed $value Option value. + * \@return bool True on success. */ public function set( $option, $value ) { $user_id = $this->get_user_id(); @@ -212,8 +212,8 @@ final class User_Options implements User_Options_Interface { /** * Delete user option. * - * @param string $option Option name. - * @return bool True on success. + * \@param string $option Option name. + * \@return bool True on success. */ public function delete( $option ) { $user_id = $this->get_user_id(); @@ -227,8 +227,8 @@ final class User_Options implements User_Options_Interface { /** * Check if user has option. * - * @param string $option Option name. - * @return bool True if option exists. + * \@param string $option Option name. + * \@return bool True if option exists. */ public function has( $option ) { return false !== $this->get( $option ); @@ -237,7 +237,7 @@ final class User_Options implements User_Options_Interface { /** * Get current user ID. * - * @return int User ID. + * \@return int User ID. */ public function get_user_id() { return $this->user_id; @@ -246,8 +246,8 @@ final class User_Options implements User_Options_Interface { /** * Switch to a different user context. * - * @param int $user_id User ID to switch to. - * @return bool True on success. + * \@param int $user_id User ID to switch to. + * \@return bool True on success. */ public function switch_user( $user_id ) { $user_id = (int) $user_id; @@ -375,8 +375,8 @@ final class Transients { /** * Get transient value. * - * @param string $transient Transient name. - * @return mixed Transient value or false if not found/expired. + * \@param string $transient Transient name. + * \@return mixed Transient value or false if not found/expired. */ public function get( $transient ) { if ( $this->context->is_network_mode() ) { @@ -388,10 +388,10 @@ final class Transients { /** * Set transient value. * - * @param string $transient Transient name. - * @param mixed $value Transient value. - * @param int $expiration Expiration in seconds. - * @return bool True on success. + * \@param string $transient Transient name. + * \@param mixed $value Transient value. + * \@param int $expiration Expiration in seconds. + * \@return bool True on success. */ public function set( $transient, $value, $expiration = 0 ) { if ( $this->context->is_network_mode() ) { @@ -403,8 +403,8 @@ final class Transients { /** * Delete transient. * - * @param string $transient Transient name. - * @return bool True on success. + * \@param string $transient Transient name. + * \@return bool True on success. */ public function delete( $transient ) { if ( $this->context->is_network_mode() ) { diff --git a/docs/context/php/trait-composition.md b/docs/context/php/trait-composition.md index d65d6ad68fa..177c5682091 100644 --- a/docs/context/php/trait-composition.md +++ b/docs/context/php/trait-composition.md @@ -26,8 +26,8 @@ trait Method_Proxy_Trait { /** * Get a proxy closure for a class method. * - * @param string $method Method name. - * @return callable Proxy closure. + * \@param string $method Method name. + * \@return callable Proxy closure. */ private function get_method_proxy( $method ) { return function ( ...$args ) use ( $method ) { @@ -38,8 +38,8 @@ trait Method_Proxy_Trait { /** * Get a proxy closure that only executes once. * - * @param string $method Method name. - * @return callable Proxy closure. + * \@param string $method Method name. + * \@return callable Proxy closure. */ private function get_method_proxy_once( $method ) { return function ( ...$args ) use ( $method ) { @@ -91,7 +91,7 @@ trait User_Aware_Trait { /** * Get current user ID. * - * @return int User ID. + * \@return int User ID. */ public function get_user_id() { return $this->user_id; @@ -100,8 +100,8 @@ trait User_Aware_Trait { /** * Switch to a different user context. * - * @param int $user_id User ID to switch to. - * @return bool True on success. + * \@param int $user_id User ID to switch to. + * \@return bool True on success. */ public function switch_user( $user_id ) { $this->user_id = (int) $user_id; @@ -146,14 +146,14 @@ trait Module_With_Settings_Trait { /** * Set up module settings. * - * @return Module_Settings Settings instance. + * \@return Module_Settings Settings instance. */ abstract protected function setup_settings(); /** * Get module settings instance. * - * @return Module_Settings Settings instance. + * \@return Module_Settings Settings instance. */ public function get_settings() { if ( ! $this->settings instanceof Module_Settings ) { @@ -194,14 +194,14 @@ trait Module_With_Scopes_Trait { /** * Set up required OAuth scopes. * - * @return array List of OAuth scopes. + * \@return array List of OAuth scopes. */ abstract protected function setup_scopes(); /** * Get required OAuth scopes. * - * @return array OAuth scopes. + * \@return array OAuth scopes. */ public function get_scopes() { if ( empty( $this->scopes ) ) { @@ -240,7 +240,7 @@ trait Module_With_Owner_Trait { /** * Get the module owner's user ID. * - * @return int Owner user ID. + * \@return int Owner user ID. */ public function get_owner_id() { if ( ! $this instanceof Module_With_Settings ) { @@ -254,7 +254,7 @@ trait Module_With_Owner_Trait { /** * Get OAuth client for the module owner. * - * @return OAuth_Client Owner's OAuth client. + * \@return OAuth_Client Owner's OAuth client. */ public function get_owner_oauth_client() { if ( $this->owner_oauth_client instanceof OAuth_Client ) { @@ -283,8 +283,8 @@ trait Module_With_Owner_Trait { /** * Set the module owner. * - * @param int $owner_id Owner user ID. - * @return bool True on success. + * \@param int $owner_id Owner user ID. + * \@return bool True on success. */ public function set_owner_id( $owner_id ) { if ( ! $this instanceof Module_With_Settings ) { @@ -327,14 +327,14 @@ trait Module_With_Assets_Trait { /** * Set up module assets. * - * @return array List of Asset objects. + * \@return array List of Asset objects. */ abstract protected function setup_assets(); /** * Get module assets. * - * @return array Asset objects. + * \@return array Asset objects. */ public function get_assets() { if ( null === $this->module_assets ) { @@ -380,14 +380,14 @@ trait Module_With_Tag_Trait { /** * Set up module tag. * - * @return Module_Tag Tag instance. + * \@return Module_Tag Tag instance. */ abstract protected function setup_tag(); /** * Get module tag instance. * - * @return Module_Tag Tag instance. + * \@return Module_Tag Tag instance. */ public function get_tag() { if ( ! $this->tag instanceof Module_Tag ) { @@ -429,14 +429,14 @@ trait Module_With_Data_Available_State_Trait { /** * Set up data available state. * - * @return Data_Available_State State instance. + * \@return Data_Available_State State instance. */ abstract protected function setup_data_available_state(); /** * Get data available state instance. * - * @return Data_Available_State State instance. + * \@return Data_Available_State State instance. */ public function get_data_available_state() { if ( ! $this->data_available_state instanceof Data_Available_State ) { @@ -460,7 +460,7 @@ trait Setting_With_Owned_Keys_Trait { /** * Get owned setting keys. * - * @return array List of owned keys. + * \@return array List of owned keys. */ protected function get_owned_keys() { return array(); @@ -469,7 +469,7 @@ trait Setting_With_Owned_Keys_Trait { /** * Check if owned settings have changed. * - * @return bool True if owned settings changed. + * \@return bool True if owned settings changed. */ public function have_owned_settings_changed() { $settings = $this->get(); @@ -490,7 +490,7 @@ trait Setting_With_Owned_Keys_Trait { /** * Get owned settings slugs. * - * @return array Owned setting keys. + * \@return array Owned setting keys. */ public function get_owned_settings_slugs() { return $this->get_owned_keys();