260318:1237 Fix UUID #4
Build and Deploy / deploy (push) Successful in 11m17s

This commit is contained in:
admin
2026-03-18 12:37:29 +07:00
parent 5d89079c2a
commit ba642e7e42
71 changed files with 533 additions and 319 deletions
+78 -78
View File
@@ -15,7 +15,7 @@ Welcome to the **Antigravity Edition** of Spec-Kit. This system is architected t
In this edition, Spec-Kit commands have been split into two interactive layers: In this edition, Spec-Kit commands have been split into two interactive layers:
1. **Workflows (`/command`)**: High-level orchestrations that guide the agent through a series of logical steps. **The easiest way to run a skill is by typing its corresponding workflow command.** 1. **Workflows (`/command`)**: High-level orchestrations that guide the agent through a series of logical steps. **The easiest way to run a skill is by typing its corresponding workflow command.**
2. **Skills (`@speckit.name`)**: Packaged agentic capabilities. Mentions of a skill give the agent immediate context and autonomous "know-how" to execute the specific toolset associated with that phase. 2. **Skills (`@speckit-name`)**: Packaged agentic capabilities. Mentions of a skill give the agent immediate context and autonomous "know-how" to execute the specific toolset associated with that phase.
> **To understand the power of Skills in Antigravity, read the docs here:** > **To understand the power of Skills in Antigravity, read the docs here:**
> [https://antigravity.google/docs/skills](https://antigravity.google/docs/skills) > [https://antigravity.google/docs/skills](https://antigravity.google/docs/skills)
@@ -38,15 +38,15 @@ Some skills and scripts reference a `.specify/` directory for templates and proj
```text ```text
.specify/ .specify/
├── templates/ ├── templates/
│ ├── spec-template.md # Template for /speckit.specify │ ├── spec-template.md # Template for /speckit-specify
│ ├── plan-template.md # Template for /speckit.plan │ ├── plan-template.md # Template for /speckit-plan
│ ├── tasks-template.md # Template for /speckit.tasks │ ├── tasks-template.md # Template for /speckit-tasks
│ └── agent-file-template.md # Template for update-agent-context.sh │ └── agent-file-template.md # Template for update-agent-context.sh
└── memory/ └── memory/
└── constitution.md # Project governance rules (/speckit.constitution) └── constitution.md # Project governance rules (/speckit-constitution)
``` ```
> **Note:** If `.specify/` is absent, skills will still function — they'll create blank files instead of using templates. The constitution workflow (`/speckit.constitution`) will create this structure for you on first run. > **Note:** If `.specify/` is absent, skills will still function — they'll create blank files instead of using templates. The constitution workflow (`/speckit-constitution`) will create this structure for you on first run.
--- ---
@@ -59,34 +59,34 @@ The toolkit is organized into modular components that provide both the logic (Sc
├── skills/ # @ Mentions (Agent Intelligence) ├── skills/ # @ Mentions (Agent Intelligence)
│ ├── nestjs-best-practices/ # NestJS Architecture Patterns │ ├── nestjs-best-practices/ # NestJS Architecture Patterns
│ ├── next-best-practices/ # Next.js App Router Patterns │ ├── next-best-practices/ # Next.js App Router Patterns
│ ├── speckit.analyze/ # Consistency Checker │ ├── speckit-analyze/ # Consistency Checker
│ ├── speckit.checker/ # Static Analysis Aggregator │ ├── speckit-checker/ # Static Analysis Aggregator
│ ├── speckit.checklist/ # Requirements Validator │ ├── speckit-checklist/ # Requirements Validator
│ ├── speckit.clarify/ # Ambiguity Resolver │ ├── speckit-clarify/ # Ambiguity Resolver
│ ├── speckit.constitution/ # Governance Manager │ ├── speckit-constitution/ # Governance Manager
│ ├── speckit.diff/ # Artifact Comparator │ ├── speckit-diff/ # Artifact Comparator
│ ├── speckit.implement/ # Code Builder (Anti-Regression) │ ├── speckit-implement/ # Code Builder (Anti-Regression)
│ ├── speckit.migrate/ # Legacy Code Migrator │ ├── speckit-migrate/ # Legacy Code Migrator
│ ├── speckit.plan/ # Technical Planner │ ├── speckit-plan/ # Technical Planner
│ ├── speckit.quizme/ # Logic Challenger (Red Team) │ ├── speckit-quizme/ # Logic Challenger (Red Team)
│ ├── speckit.reviewer/ # Code Reviewer │ ├── speckit-reviewer/ # Code Reviewer
│ ├── speckit.security-audit/ # Security Auditor (OWASP/CASL/ClamAV) │ ├── speckit-security-audit/ # Security Auditor (OWASP/CASL/ClamAV)
│ ├── speckit.specify/ # Feature Definer │ ├── speckit-specify/ # Feature Definer
│ ├── speckit.status/ # Progress Dashboard │ ├── speckit-status/ # Progress Dashboard
│ ├── speckit.tasks/ # Task Breaker │ ├── speckit-tasks/ # Task Breaker
│ ├── speckit.taskstoissues/ # Issue Tracker Syncer (GitHub + Gitea) │ ├── speckit-taskstoissues/ # Issue Tracker Syncer (GitHub + Gitea)
│ ├── speckit.tester/ # Test Runner & Coverage │ ├── speckit-tester/ # Test Runner & Coverage
│ └── speckit.validate/ # Implementation Validator │ └── speckit-validate/ # Implementation Validator
├── workflows/ # / Slash Commands (Orchestration) ├── workflows/ # / Slash Commands (Orchestration)
│ ├── 00-speckit.all.md # Full Pipeline (10 steps: Specify → Validate) │ ├── 00-speckit-all.md # Full Pipeline (10 steps: Specify → Validate)
│ ├── 0111-speckit.*.md # Individual phase workflows │ ├── 0111-speckit-*.md # Individual phase workflows
│ ├── speckit.prepare.md # Prep Pipeline (5 steps: Specify → Analyze) │ ├── speckit-prepare.md # Prep Pipeline (5 steps: Specify → Analyze)
│ ├── schema-change.md # DB Schema Change (ADR-009) │ ├── schema-change.md # DB Schema Change (ADR-009)
│ ├── create-backend-module.md # NestJS Module Scaffolding │ ├── create-backend-module.md # NestJS Module Scaffolding
│ ├── create-frontend-page.md # Next.js Page Scaffolding │ ├── create-frontend-page.md # Next.js Page Scaffolding
│ ├── deploy.md # Deployment via Gitea CI/CD │ ├── deploy.md # Deployment via Gitea CI/CD
│ └── util-speckit.*.md # Utilities (checklist, diff, migrate, etc.) │ └── util-speckit-*.md # Utilities (checklist, diff, migrate, etc.)
└── scripts/ └── scripts/
├── bash/ # Bash Core (Kinetic logic) ├── bash/ # Bash Core (Kinetic logic)
@@ -112,27 +112,27 @@ The toolkit is organized into modular components that provide both the logic (Sc
| Phase | Workflow Trigger | Antigravity Skill | Role | | Phase | Workflow Trigger | Antigravity Skill | Role |
| :---------------- | :---------------------------- | :------------------------ | :------------------------------------------------------ | | :---------------- | :---------------------------- | :------------------------ | :------------------------------------------------------ |
| **Full Pipeline** | `/00-speckit.all` | N/A | Runs full SDLC pipeline (10 steps: Specify → Validate). | | **Full Pipeline** | `/00-speckit-all` | N/A | Runs full SDLC pipeline (10 steps: Specify → Validate). |
| **Governance** | `/01-speckit.constitution` | `@speckit.constitution` | Establishes project rules & principles. | | **Governance** | `/01-speckit-constitution` | `@speckit-constitution` | Establishes project rules & principles. |
| **Definition** | `/02-speckit.specify` | `@speckit.specify` | Drafts structured `spec.md`. | | **Definition** | `/02-speckit-specify` | `@speckit-specify` | Drafts structured `spec.md`. |
| **Ambiguity** | `/03-speckit.clarify` | `@speckit.clarify` | Resolves gaps post-spec. | | **Ambiguity** | `/03-speckit-clarify` | `@speckit-clarify` | Resolves gaps post-spec. |
| **Architecture** | `/04-speckit.plan` | `@speckit.plan` | Generates technical `plan.md`. | | **Architecture** | `/04-speckit-plan` | `@speckit-plan` | Generates technical `plan.md`. |
| **Decomposition** | `/05-speckit.tasks` | `@speckit.tasks` | Breaks plans into atomic tasks. | | **Decomposition** | `/05-speckit-tasks` | `@speckit-tasks` | Breaks plans into atomic tasks. |
| **Consistency** | `/06-speckit.analyze` | `@speckit.analyze` | Cross-checks Spec vs Plan vs Tasks. | | **Consistency** | `/06-speckit-analyze` | `@speckit-analyze` | Cross-checks Spec vs Plan vs Tasks. |
| **Execution** | `/07-speckit.implement` | `@speckit.implement` | Builds implementation with safety protocols. | | **Execution** | `/07-speckit-implement` | `@speckit-implement` | Builds implementation with safety protocols. |
| **Quality** | `/08-speckit.checker` | `@speckit.checker` | Runs static analysis (Linting, Security, Types). | | **Quality** | `/08-speckit-checker` | `@speckit-checker` | Runs static analysis (Linting, Security, Types). |
| **Testing** | `/09-speckit.tester` | `@speckit.tester` | Runs test suite & reports coverage. | | **Testing** | `/09-speckit-tester` | `@speckit-tester` | Runs test suite & reports coverage. |
| **Review** | `/10-speckit.reviewer` | `@speckit.reviewer` | Performs code review (Logic, Perf, Style). | | **Review** | `/10-speckit-reviewer` | `@speckit-reviewer` | Performs code review (Logic, Perf, Style). |
| **Validation** | `/11-speckit.validate` | `@speckit.validate` | Verifies implementation matches Spec requirements. | | **Validation** | `/11-speckit-validate` | `@speckit-validate` | Verifies implementation matches Spec requirements. |
| **Preparation** | `/speckit.prepare` | N/A | Runs Specify → Analyze prep sequence (5 steps). | | **Preparation** | `/speckit-prepare` | N/A | Runs Specify → Analyze prep sequence (5 steps). |
| **Schema** | `/schema-change` | N/A | DB schema changes per ADR-009 (no migrations). | | **Schema** | `/schema-change` | N/A | DB schema changes per ADR-009 (no migrations). |
| **Security** | N/A | `@speckit.security-audit` | OWASP Top 10 + CASL + ClamAV audit. | | **Security** | N/A | `@speckit-security-audit` | OWASP Top 10 + CASL + ClamAV audit. |
| **Checklist** | `/util-speckit.checklist` | `@speckit.checklist` | Generates feature checklists. | | **Checklist** | `/util-speckit-checklist` | `@speckit-checklist` | Generates feature checklists. |
| **Diff** | `/util-speckit.diff` | `@speckit.diff` | Compares artifact versions. | | **Diff** | `/util-speckit-diff` | `@speckit-diff` | Compares artifact versions. |
| **Migration** | `/util-speckit.migrate` | `@speckit.migrate` | Port existing code to Spec-Kit. | | **Migration** | `/util-speckit-migrate` | `@speckit-migrate` | Port existing code to Spec-Kit. |
| **Red Team** | `/util-speckit.quizme` | `@speckit.quizme` | Challenges logical flaws. | | **Red Team** | `/util-speckit-quizme` | `@speckit-quizme` | Challenges logical flaws. |
| **Status** | `/util-speckit.status` | `@speckit.status` | Shows feature completion status. | | **Status** | `/util-speckit-status` | `@speckit-status` | Shows feature completion status. |
| **Tracking** | `/util-speckit.taskstoissues` | `@speckit.taskstoissues` | Syncs tasks to GitHub/Gitea issues. | | **Tracking** | `/util-speckit-taskstoissues` | `@speckit-taskstoissues` | Syncs tasks to GitHub/Gitea issues. |
--- ---
@@ -142,14 +142,14 @@ The following skills are designed to work together as a comprehensive defense ag
| Step | Skill | Core Question | Focus | | Step | Skill | Core Question | Focus |
| :-------------- | :------------------ | :-------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- | | :-------------- | :------------------ | :-------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1. Checker** | `@speckit.checker` | _"Is the code compliant?"_ | **Syntax & Security**. Runs compilation, linting (ESLint/GolangCI), and vulnerability scans (npm audit/govulncheck). Catches low-level errors first. | | **1. Checker** | `@speckit-checker` | _"Is the code compliant?"_ | **Syntax & Security**. Runs compilation, linting (ESLint/GolangCI), and vulnerability scans (npm audit/govulncheck). Catches low-level errors first. |
| **2. Tester** | `@speckit.tester` | _"Does it work?"_ | **Functionality**. Executes your test suite (Jest/Pytest/Go Test) to ensure logic performs as expected and tests pass. | | **2. Tester** | `@speckit-tester` | _"Does it work?"_ | **Functionality**. Executes your test suite (Jest/Pytest/Go Test) to ensure logic performs as expected and tests pass. |
| **3. Reviewer** | `@speckit.reviewer` | _"Is the code written well?"_ | **Quality & Maintainability**. Analyzes code structure for complexity, performance bottlenecks, and best practices, acting as a senior peer reviewer. | | **3. Reviewer** | `@speckit-reviewer` | _"Is the code written well?"_ | **Quality & Maintainability**. Analyzes code structure for complexity, performance bottlenecks, and best practices, acting as a senior peer reviewer. |
| **4. Validate** | `@speckit.validate` | _"Did we build the right thing?"_ | **Requirements**. Semantically compares the implementation against the defined `spec.md` and `plan.md` to ensure all feature requirements are met. | | **4. Validate** | `@speckit-validate` | _"Did we build the right thing?"_ | **Requirements**. Semantically compares the implementation against the defined `spec.md` and `plan.md` to ensure all feature requirements are met. |
> **🤖 Power User Tip:** You can amplify this pipeline by creating a custom **MCP Server** or subagent that delegates heavy reasoning to a dedicated LLM. > **🤖 Power User Tip:** You can amplify this pipeline by creating a custom **MCP Server** or subagent that delegates heavy reasoning to a dedicated LLM.
> >
> - **Use Case:** Bind the `@speckit.validate` and `@speckit.reviewer` steps to a large-context model. > - **Use Case:** Bind the `@speckit-validate` and `@speckit-reviewer` steps to a large-context model.
> - **Benefit:** Large-context models (1M+ tokens) excel at analyzing the full project context against the Spec, finding subtle logical flaws that smaller models miss. > - **Benefit:** Large-context models (1M+ tokens) excel at analyzing the full project context against the Spec, finding subtle logical flaws that smaller models miss.
> - **How:** Create a wrapper script `scripts/gemini-reviewer.sh` that pipes the `tasks.md` and codebase to an LLM, then expose this as a tool. > - **How:** Create a wrapper script `scripts/gemini-reviewer.sh` that pipes the `tasks.md` and codebase to an LLM, then expose this as a tool.
@@ -161,28 +161,28 @@ These workflows function as the "Control Plane" of the project, managing everyth
| Step | Workflow | Core Question | Focus | | Step | Workflow | Core Question | Focus |
| :----------------- | :-------------------------------------------------- | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- | | :----------------- | :-------------------------------------------------- | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **1. Preparation** | `/speckit.prepare` | _"Are we ready?"_ | **The Macro-Workflow**. Runs Skills 0206 (Specify $\to$ Clarify $\to$ Plan $\to$ Tasks $\to$ Analyze) in one sequence to go from "Idea" to "Ready to Code". | | **1. Preparation** | `/speckit-prepare` | _"Are we ready?"_ | **The Macro-Workflow**. Runs Skills 0206 (Specify $\to$ Clarify $\to$ Plan $\to$ Tasks $\to$ Analyze) in one sequence to go from "Idea" to "Ready to Code". |
| **2. Migration** | `/util-speckit.migrate` | _"Can we import?"_ | **Onboarding**. Reverse-engineers existing code into `spec.md`, `plan.md`, and `tasks.md`. | | **2. Migration** | `/util-speckit-migrate` | _"Can we import?"_ | **Onboarding**. Reverse-engineers existing code into `spec.md`, `plan.md`, and `tasks.md`. |
| **3. Red Team** | `/util-speckit.quizme` | _"What did we miss?"_ | **Hardening**. Socratic questioning to find logical gaps in your specification before you plan. | | **3. Red Team** | `/util-speckit-quizme` | _"What did we miss?"_ | **Hardening**. Socratic questioning to find logical gaps in your specification before you plan. |
| **4. Export** | `/util-speckit.taskstoissues` | _"Who does what?"_ | **Handoff**. Converts your `tasks.md` into GitHub or Gitea issues with labels and milestones. | | **4. Export** | `/util-speckit-taskstoissues` | _"Who does what?"_ | **Handoff**. Converts your `tasks.md` into GitHub or Gitea issues with labels and milestones. |
| **5. Status** | `/util-speckit.status` | _"Are we there yet?"_ | **Tracking**. Scans all artifacts to report feature completion percentage. | | **5. Status** | `/util-speckit-status` | _"Are we there yet?"_ | **Tracking**. Scans all artifacts to report feature completion percentage. |
| **6. Utilities** | `/util-speckit.diff` <br> `/util-speckit.checklist` | _"What changed?"_ | **Support**. View artifact diffs or generate quick acceptance checklists. | | **6. Utilities** | `/util-speckit-diff` <br> `/util-speckit-checklist` | _"What changed?"_ | **Support**. View artifact diffs or generate quick acceptance checklists. |
### 🔄 The Design Sequence ### 🔄 The Design Sequence
**Stage 1: Inception** **Stage 1: Inception**
- _Legacy Project?_ $\to$ Run **`/util-speckit.migrate`**. - _Legacy Project?_ $\to$ Run **`/util-speckit-migrate`**.
- _New Feature?_ $\to$ Run **`/speckit.prepare`**. - _New Feature?_ $\to$ Run **`/speckit-prepare`**.
**Stage 2: Hardening** **Stage 2: Hardening**
- Run **`/util-speckit.quizme`** to catch edge cases. - Run **`/util-speckit-quizme`** to catch edge cases.
- Run **`/speckit.prepare`** again to regenerate the Plan based on restricted specs. - Run **`/speckit-prepare`** again to regenerate the Plan based on restricted specs.
**Stage 3: Handoff** **Stage 3: Handoff**
- Run **`/util-speckit.taskstoissues`** to sync with your board. - Run **`/util-speckit-taskstoissues`** to sync with your board.
--- ---
@@ -192,17 +192,17 @@ Once the `.agents` folder is present, you can talk to your agent as a partner. Y
**Example Conversation:** **Example Conversation:**
> **Developer**: "**/02-speckit.specify** Add a real-time notification system using WebSockets." > **Developer**: "**/02-speckit-specify** Add a real-time notification system using WebSockets."
> >
> **Antigravity**: "Understood. Executing the **@speckit.specify** skill. Creating feature branch `005-notifications` and drafting the initial specification..." > **Antigravity**: "Understood. Executing the **@speckit-specify** skill. Creating feature branch `005-notifications` and drafting the initial specification..."
> _(Antigravity creates branch, drafts spec.md)_ > _(Antigravity creates branch, drafts spec.md)_
> >
> **Antigravity**: "Specification ready. I've identified some ambiguity in the reconnection strategy. Should I use **@speckit.clarify** to resolve this?" > **Antigravity**: "Specification ready. I've identified some ambiguity in the reconnection strategy. Should I use **@speckit-clarify** to resolve this?"
> >
> **Developer**: "Yes, use **@speckit.clarify**." > **Developer**: "Yes, use **@speckit-clarify**."
> _(Antigravity executes the skill logic, interviewing the developer and updating the spec)_ > _(Antigravity executes the skill logic, interviewing the developer and updating the spec)_
> >
> **Antigravity**: "The spec is now robust. I'm ready to trigger **/04-speckit.plan** and **/05-speckit.tasks** to prepare for implementation." > **Antigravity**: "The spec is now robust. I'm ready to trigger **/04-speckit-plan** and **/05-speckit-tasks** to prepare for implementation."
--- ---
@@ -212,7 +212,7 @@ To get the most out of this system, follow these **Spec-Driven Development (SDD)
### 1. The Constitution is King 👑 ### 1. The Constitution is King 👑
**Never skip `/01-speckit.constitution`.** **Never skip `/01-speckit-constitution`.**
- This file is the "Context Window Anchor" for the AI. - This file is the "Context Window Anchor" for the AI.
- It prevents hallucinations about tech stack (e.g., "Don't use jQuery" or "Always use TypeScript strict mode"). - It prevents hallucinations about tech stack (e.g., "Don't use jQuery" or "Always use TypeScript strict mode").
@@ -222,9 +222,9 @@ To get the most out of this system, follow these **Spec-Driven Development (SDD)
Don't rush to code. The workflow exists to catch errors _cheaply_ before they become expensive bugs. Don't rush to code. The workflow exists to catch errors _cheaply_ before they become expensive bugs.
- **Ambiguity Layer**: `/03-speckit.clarify` catches misunderstandings. - **Ambiguity Layer**: `/03-speckit-clarify` catches misunderstandings.
- **Logic Layer**: `/util-speckit.quizme` catches edge cases. - **Logic Layer**: `/util-speckit-quizme` catches edge cases.
- **Consistency Layer**: `/06-speckit.analyze` catches gaps between Spec and Plan. - **Consistency Layer**: `/06-speckit-analyze` catches gaps between Spec and Plan.
### 3. The 15-Minute Rule ⏱️ ### 3. The 15-Minute Rule ⏱️
@@ -240,17 +240,17 @@ If you change your mind mid-project:
1. Don't just edit the code. 1. Don't just edit the code.
2. Edit the `spec.md` to reflect the new requirement. 2. Edit the `spec.md` to reflect the new requirement.
3. Run `/util-speckit.diff` to see the drift. 3. Run `/util-speckit-diff` to see the drift.
4. This keeps your documentation alive and truthful. 4. This keeps your documentation alive and truthful.
--- ---
## 🧩 Adaptation Notes ## 🧩 Adaptation Notes
- **Skill-Based Autonomy**: Mentions like `@speckit.plan` trigger the agent's internalized understanding of how to perform that role. - **Skill-Based Autonomy**: Mentions like `@speckit-plan` trigger the agent's internalized understanding of how to perform that role.
- **Shared Script Core**: Logic resides in `.agents/scripts/bash` (modular) with PowerShell equivalents in `scripts/powershell/` for Windows-native execution. - **Shared Script Core**: Logic resides in `.agents/scripts/bash` (modular) with PowerShell equivalents in `scripts/powershell/` for Windows-native execution.
- **Agent-Native**: Designed to be invoked via Antigravity tool calls and reasoning rather than just terminal strings. - **Agent-Native**: Designed to be invoked via Antigravity tool calls and reasoning rather than just terminal strings.
- **LCBP3-DMS Specific**: Includes project-specific skills (`nestjs-best-practices`, `next-best-practices`, `speckit.security-audit`) and workflows (`/schema-change`, `/create-backend-module`, `/deploy`). - **LCBP3-DMS Specific**: Includes project-specific skills (`nestjs-best-practices`, `next-best-practices`, `speckit-security-audit`) and workflows (`/schema-change`, `/create-backend-module`, `/deploy`).
--- ---
@@ -287,8 +287,8 @@ If you change your mind mid-project:
| Create Frontend Page | `/create-frontend-page` | Next.js App Router page | | Create Frontend Page | `/create-frontend-page` | Next.js App Router page |
| Schema Change | `/schema-change` | ADR-009: No migrations | | Schema Change | `/schema-change` | ADR-009: No migrations |
| Deploy | `/deploy` | Blue-Green via Gitea CI/CD | | Deploy | `/deploy` | Blue-Green via Gitea CI/CD |
| UAT Feature Check | `/11-speckit.validate` | vs `01-05-acceptance-criteria.md` | | UAT Feature Check | `/11-speckit-validate` | vs `01-05-acceptance-criteria.md` |
| Security Audit | `@speckit.security-audit` | OWASP + CASL + ClamAV | | Security Audit | `@speckit-security-audit` | OWASP + CASL + ClamAV |
### 🚫 Critical Forbidden Actions ### 🚫 Critical Forbidden Actions
@@ -1,9 +1,9 @@
--- ---
name: speckit.analyze name: speckit-analyze
description: Perform a non-destructive cross-artifact consistency and quality analysis across spec.md, plan.md, and tasks.md after task generation. description: Perform a non-destructive cross-artifact consistency and quality analysis across spec.md, plan.md, and tasks.md after task generation.
version: 1.0.0 version: 1.0.0
depends-on: depends-on:
- speckit.tasks - speckit-tasks
--- ---
## User Input ## User Input
@@ -21,13 +21,13 @@ You are the **Antigravity Consistency Analyst**. Your role is to identify incons
## Task ## Task
### Goal ### Goal
Identify inconsistencies, duplications, ambiguities, and underspecified items across the three core artifacts (`spec.md`, `plan.md`, `tasks.md`) before implementation. This command MUST run only after `/speckit.tasks` has successfully produced a complete `tasks.md`. Identify inconsistencies, duplications, ambiguities, and underspecified items across the three core artifacts (`spec.md`, `plan.md`, `tasks.md`) before implementation. This command MUST run only after `/speckit-tasks` has successfully produced a complete `tasks.md`.
## Operating Constraints ## Operating Constraints
**STRICTLY READ-ONLY**: Do **not** modify any files. Output a structured analysis report. Offer an optional remediation plan (user must explicitly approve before any follow-up editing commands would be invoked manually). **STRICTLY READ-ONLY**: Do **not** modify any files. Output a structured analysis report. Offer an optional remediation plan (user must explicitly approve before any follow-up editing commands would be invoked manually).
**Constitution Authority**: The project constitution (`.specify/memory/constitution.md`) is **non-negotiable** within this analysis scope. Constitution conflicts are automatically CRITICAL and require adjustment of the spec, plan, or tasks—not dilution, reinterpretation, or silent ignoring of the principle. If a principle itself needs to change, that must occur in a separate, explicit constitution update outside `/speckit.analyze`. **Constitution Authority**: The project constitution (`.specify/memory/constitution.md`) is **non-negotiable** within this analysis scope. Constitution conflicts are automatically CRITICAL and require adjustment of the spec, plan, or tasks—not dilution, reinterpretation, or silent ignoring of the principle. If a principle itself needs to change, that must occur in a separate, explicit constitution update outside `/speckit-analyze`.
### Steps ### Steps
@@ -163,9 +163,9 @@ Output a Markdown report (no file writes) with the following structure:
At end of report, output a concise Next Actions block: At end of report, output a concise Next Actions block:
- If CRITICAL issues exist: Recommend resolving before `/speckit.implement` - If CRITICAL issues exist: Recommend resolving before `/speckit-implement`
- If only LOW/MEDIUM: User may proceed, but provide improvement suggestions - If only LOW/MEDIUM: User may proceed, but provide improvement suggestions
- Provide explicit command suggestions: e.g., "Run /speckit.specify with refinement", "Run /speckit.plan to adjust architecture", "Manually edit tasks.md to add coverage for 'performance-metrics'" - Provide explicit command suggestions: e.g., "Run /speckit-specify with refinement", "Run /speckit-plan to adjust architecture", "Manually edit tasks.md to add coverage for 'performance-metrics'"
### 8. Offer Remediation ### 8. Offer Remediation
@@ -1,5 +1,5 @@
--- ---
name: speckit.checker name: speckit-checker
description: Run static analysis tools and aggregate results. description: Run static analysis tools and aggregate results.
version: 1.0.0 version: 1.0.0
depends-on: [] depends-on: []
@@ -1,5 +1,5 @@
--- ---
name: speckit.checklist name: speckit-checklist
description: Generate a custom checklist for the current feature based on user requirements. description: Generate a custom checklist for the current feature based on user requirements.
version: 1.0.0 version: 1.0.0
--- ---
@@ -101,7 +101,7 @@ You are the **Antigravity Quality Gatekeeper**. Your role is to validate the qua
- Format: `[domain].md` - Format: `[domain].md`
- If file exists, append to existing file - If file exists, append to existing file
- Number items sequentially starting from CHK001 - Number items sequentially starting from CHK001
- Each `/speckit.checklist` run creates a NEW file (never overwrites existing checklists) - Each `/speckit-checklist` run creates a NEW file (never overwrites existing checklists)
**CORE PRINCIPLE - Test the Requirements, Not the Implementation**: **CORE PRINCIPLE - Test the Requirements, Not the Implementation**:
Every checklist item MUST evaluate the REQUIREMENTS THEMSELVES for: Every checklist item MUST evaluate the REQUIREMENTS THEMSELVES for:
@@ -219,7 +219,7 @@ You are the **Antigravity Quality Gatekeeper**. Your role is to validate the qua
- Actor/timing - Actor/timing
- Any explicit user-specified must-have items incorporated - Any explicit user-specified must-have items incorporated
**Important**: Each `/speckit.checklist` command invocation creates a checklist file using short, descriptive names unless file already exists. This allows: **Important**: Each `/speckit-checklist` command invocation creates a checklist file using short, descriptive names unless file already exists. This allows:
- Multiple checklists of different types (e.g., `ux.md`, `test.md`, `security.md`) - Multiple checklists of different types (e.g., `ux.md`, `test.md`, `security.md`)
- Simple, memorable filenames that indicate checklist purpose - Simple, memorable filenames that indicate checklist purpose
@@ -4,13 +4,13 @@
**Created**: [DATE] **Created**: [DATE]
**Feature**: [Link to spec.md or relevant documentation] **Feature**: [Link to spec.md or relevant documentation]
**Note**: This checklist is generated by the `/speckit.checklist` command based on feature context and requirements. **Note**: This checklist is generated by the `/speckit-checklist` command based on feature context and requirements.
<!-- <!--
============================================================================ ============================================================================
IMPORTANT: The checklist items below are SAMPLE ITEMS for illustration only. IMPORTANT: The checklist items below are SAMPLE ITEMS for illustration only.
The /speckit.checklist command MUST replace these with actual items based on: The /speckit-checklist command MUST replace these with actual items based on:
- User's specific checklist request - User's specific checklist request
- Feature requirements from spec.md - Feature requirements from spec.md
- Technical context from plan.md - Technical context from plan.md
@@ -1,12 +1,12 @@
--- ---
name: speckit.clarify name: speckit-clarify
description: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarification questions and encoding answers back into the spec. description: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarification questions and encoding answers back into the spec.
version: 1.0.0 version: 1.0.0
depends-on: depends-on:
- speckit.specify - speckit-specify
handoffs: handoffs:
- label: Build Technical Plan - label: Build Technical Plan
agent: speckit.plan agent: speckit-plan
prompt: Create a plan for the spec. I am building with... prompt: Create a plan for the spec. I am building with...
--- ---
@@ -28,7 +28,7 @@ You are the **Antigravity Ambiguity Buster**. Your role is to interrogate specif
Goal: Detect and reduce ambiguity or missing decision points in the active feature specification and record the clarifications directly in the spec file. Goal: Detect and reduce ambiguity or missing decision points in the active feature specification and record the clarifications directly in the spec file.
Note: This clarification workflow is expected to run (and be completed) BEFORE invoking `/speckit.plan`. If the user explicitly states they are skipping clarification (e.g., exploratory spike), you may proceed, but must warn that downstream rework risk increases. Note: This clarification workflow is expected to run (and be completed) BEFORE invoking `/speckit-plan`. If the user explicitly states they are skipping clarification (e.g., exploratory spike), you may proceed, but must warn that downstream rework risk increases.
Execution steps: Execution steps:
@@ -36,7 +36,7 @@ Execution steps:
- `FEATURE_DIR` - `FEATURE_DIR`
- `FEATURE_SPEC` - `FEATURE_SPEC`
- (Optionally capture `IMPL_PLAN`, `TASKS` for future chained flows.) - (Optionally capture `IMPL_PLAN`, `TASKS` for future chained flows.)
- If JSON parsing fails, abort and instruct user to re-run `/speckit.specify` or verify feature branch environment. - If JSON parsing fails, abort and instruct user to re-run `/speckit-specify` or verify feature branch environment.
- For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\\''m Groot' (or double-quote if possible: "I'm Groot"). - For single quotes in args like "I'm Groot", use escape syntax: e.g 'I'\\''m Groot' (or double-quote if possible: "I'm Groot").
2. Load the current spec file. Perform a structured ambiguity & coverage scan using this taxonomy. For each category, mark status: Clear / Partial / Missing. Produce an internal coverage map used for prioritization (do not output raw map unless no questions will be asked). 2. Load the current spec file. Perform a structured ambiguity & coverage scan using this taxonomy. For each category, mark status: Clear / Partial / Missing. Produce an internal coverage map used for prioritization (do not output raw map unless no questions will be asked).
@@ -175,13 +175,13 @@ Execution steps:
- Path to updated spec. - Path to updated spec.
- Sections touched (list names). - Sections touched (list names).
- Coverage summary table listing each taxonomy category with Status: Resolved (was Partial/Missing and addressed), Deferred (exceeds question quota or better suited for planning), Clear (already sufficient), Outstanding (still Partial/Missing but low impact). - Coverage summary table listing each taxonomy category with Status: Resolved (was Partial/Missing and addressed), Deferred (exceeds question quota or better suited for planning), Clear (already sufficient), Outstanding (still Partial/Missing but low impact).
- If any Outstanding or Deferred remain, recommend whether to proceed to `/speckit.plan` or run `/speckit.clarify` again later post-plan. - If any Outstanding or Deferred remain, recommend whether to proceed to `/speckit-plan` or run `/speckit-clarify` again later post-plan.
- Suggested next command. - Suggested next command.
Behavior rules: Behavior rules:
- If no meaningful ambiguities found (or all potential questions would be low-impact), respond: "No critical ambiguities detected worth formal clarification." and suggest proceeding. - If no meaningful ambiguities found (or all potential questions would be low-impact), respond: "No critical ambiguities detected worth formal clarification." and suggest proceeding.
- If spec file missing, instruct user to run `/speckit.specify` first (do not create a new spec here). - If spec file missing, instruct user to run `/speckit-specify` first (do not create a new spec here).
- Never exceed 5 total asked questions (clarification retries for a single question do not count as new questions). - Never exceed 5 total asked questions (clarification retries for a single question do not count as new questions).
- Avoid speculative tech stack questions unless the absence blocks functional clarity. - Avoid speculative tech stack questions unless the absence blocks functional clarity.
- Respect user early termination signals ("stop", "done", "proceed"). - Respect user early termination signals ("stop", "done", "proceed").
@@ -1,10 +1,10 @@
--- ---
name: speckit.constitution name: speckit-constitution
description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync. description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync.
version: 1.0.0 version: 1.0.0
handoffs: handoffs:
- label: Build Specification - label: Build Specification
agent: speckit.specify agent: speckit-specify
prompt: Implement the feature specification based on the updated constitution. I want to build... prompt: Implement the feature specification based on the updated constitution. I want to build...
--- ---
@@ -1,5 +1,5 @@
--- ---
name: speckit.diff name: speckit-diff
description: Compare two versions of a spec or plan to highlight changes. description: Compare two versions of a spec or plan to highlight changes.
version: 1.0.0 version: 1.0.0
depends-on: [] depends-on: []
@@ -1,9 +1,9 @@
--- ---
name: speckit.implement name: speckit-implement
description: Execute the implementation plan by processing and executing all tasks defined in tasks.md (with Ironclad Anti-Regression Protocols) description: Execute the implementation plan by processing and executing all tasks defined in tasks.md (with Ironclad Anti-Regression Protocols)
version: 1.0.0 version: 1.0.0
depends-on: depends-on:
- speckit.tasks - speckit-tasks
--- ---
## User Input ## User Input
@@ -245,4 +245,4 @@ At the start of execution and after every 3 modifications:
--- ---
Note: This command assumes a complete task breakdown exists in tasks.md. If tasks are incomplete or missing, suggest running `/speckit.tasks` first to regenerate the task list. Note: This command assumes a complete task breakdown exists in tasks.md. If tasks are incomplete or missing, suggest running `/speckit-tasks` first to regenerate the task list.
@@ -1,5 +1,5 @@
--- ---
name: speckit.migrate name: speckit-migrate
description: Migrate existing projects into the speckit structure by generating spec.md, plan.md, and tasks.md from existing code. description: Migrate existing projects into the speckit structure by generating spec.md, plan.md, and tasks.md from existing code.
version: 1.0.0 version: 1.0.0
depends-on: [] depends-on: []
@@ -1,16 +1,16 @@
--- ---
name: speckit.plan name: speckit-plan
description: Execute the implementation planning workflow using the plan template to generate design artifacts. description: Execute the implementation planning workflow using the plan template to generate design artifacts.
version: 1.0.0 version: 1.0.0
depends-on: depends-on:
- speckit.specify - speckit-specify
handoffs: handoffs:
- label: Create Tasks - label: Create Tasks
agent: speckit.tasks agent: speckit-tasks
prompt: Break the plan into tasks prompt: Break the plan into tasks
send: true send: true
- label: Create Checklist - label: Create Checklist
agent: speckit.checklist agent: speckit-checklist
prompt: Create a checklist for the following domain... prompt: Create a checklist for the following domain...
--- ---
@@ -3,7 +3,7 @@
**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link] **Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md` **Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow. **Note**: This template is filled in by the `/speckit-plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
## Summary ## Summary
@@ -39,12 +39,12 @@
```text ```text
specs/[###-feature]/ specs/[###-feature]/
├── plan.md # This file (/speckit.plan command output) ├── plan.md # This file (/speckit-plan command output)
├── research.md # Phase 0 output (/speckit.plan command) ├── research.md # Phase 0 output (/speckit-plan command)
├── data-model.md # Phase 1 output (/speckit.plan command) ├── data-model.md # Phase 1 output (/speckit-plan command)
├── quickstart.md # Phase 1 output (/speckit.plan command) ├── quickstart.md # Phase 1 output (/speckit-plan command)
├── contracts/ # Phase 1 output (/speckit.plan command) ├── contracts/ # Phase 1 output (/speckit-plan command)
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan) └── tasks.md # Phase 2 output (/speckit-tasks command - NOT created by /speckit-plan)
``` ```
### Source Code (repository root) ### Source Code (repository root)
@@ -1,10 +1,10 @@
--- ---
name: speckit.quizme name: speckit-quizme
description: Challenge the specification with Socratic questioning to identify logical gaps, unhandled edge cases, and robustness issues. description: Challenge the specification with Socratic questioning to identify logical gaps, unhandled edge cases, and robustness issues.
version: 1.0.0 version: 1.0.0
handoffs: handoffs:
- label: Clarify Spec Requirements - label: Clarify Spec Requirements
agent: speckit.clarify agent: speckit-clarify
prompt: Clarify specification requirements prompt: Clarify specification requirements
--- ---
@@ -24,7 +24,7 @@ You are the **Antigravity Red Teamer**. Your role is to play the "Socratic Teach
### Outline ### Outline
Goal: Act as a "Red Team" or "Socratic Teacher" to challenge the current feature specification. Unlike `speckit.clarify` (which looks for missing definitions), `speckit.quizme` looks for logical fallacies, race conditions, naive assumptions, and "happy path" bias. Goal: Act as a "Red Team" or "Socratic Teacher" to challenge the current feature specification. Unlike `speckit-clarify` (which looks for missing definitions), `speckit-quizme` looks for logical fallacies, race conditions, naive assumptions, and "happy path" bias.
Execution steps: Execution steps:
@@ -1,5 +1,5 @@
--- ---
name: speckit.reviewer name: speckit-reviewer
description: Perform code review with actionable feedback and suggestions. description: Perform code review with actionable feedback and suggestions.
version: 1.0.0 version: 1.0.0
depends-on: [] depends-on: []
@@ -1,9 +1,9 @@
--- ---
name: speckit.security-audit name: speckit-security-audit
description: Perform a security-focused audit of the codebase against OWASP Top 10, CASL authorization, and LCBP3-DMS security requirements. description: Perform a security-focused audit of the codebase against OWASP Top 10, CASL authorization, and LCBP3-DMS security requirements.
version: 1.0.0 version: 1.0.0
depends-on: depends-on:
- speckit.checker - speckit-checker
--- ---
## Role ## Role
@@ -1,13 +1,13 @@
--- ---
name: speckit.specify name: speckit-specify
description: Create or update the feature specification from a natural language feature description. description: Create or update the feature specification from a natural language feature description.
version: 1.0.0 version: 1.0.0
handoffs: handoffs:
- label: Build Technical Plan - label: Build Technical Plan
agent: speckit.plan agent: speckit-plan
prompt: Create a plan for the spec. I am building with... prompt: Create a plan for the spec. I am building with...
- label: Clarify Spec Requirements - label: Clarify Spec Requirements
agent: speckit.clarify agent: speckit-clarify
prompt: Clarify specification requirements prompt: Clarify specification requirements
send: true send: true
--- ---
@@ -28,7 +28,7 @@ You are the **Antigravity Domain Scribe**. Your role is to translate natural lan
### Outline ### Outline
The text the user typed after `/speckit.specify` in the triggering message **is** the feature description. Assume you always have it available in this conversation even if `{{args}}` appears literally below. Do not ask the user to repeat it unless they provided an empty command. The text the user typed after `/speckit-specify` in the triggering message **is** the feature description. Assume you always have it available in this conversation even if `{{args}}` appears literally below. Do not ask the user to repeat it unless they provided an empty command.
Given that feature description, do this: Given that feature description, do this:
@@ -143,7 +143,7 @@ Given that feature description, do this:
## Notes ## Notes
- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan` - Items marked incomplete require spec updates before `/speckit-clarify` or `/speckit-plan`
``` ```
b. **Run Validation Check**: Review the spec against each checklist item: b. **Run Validation Check**: Review the spec against each checklist item:
@@ -196,7 +196,7 @@ Given that feature description, do this:
d. **Update Checklist**: After each validation iteration, update the checklist file with current pass/fail status d. **Update Checklist**: After each validation iteration, update the checklist file with current pass/fail status
7. Report completion with branch name, spec file path, checklist results, and readiness for the next phase (`/speckit.clarify` or `/speckit.plan`). 7. Report completion with branch name, spec file path, checklist results, and readiness for the next phase (`/speckit-clarify` or `/speckit-plan`).
**NOTE:** The script creates and checks out the new branch and initializes the spec file before writing. **NOTE:** The script creates and checks out the new branch and initializes the spec file before writing.
@@ -1,5 +1,5 @@
--- ---
name: speckit.status name: speckit-status
description: Display a dashboard showing feature status, completion percentage, and blockers. description: Display a dashboard showing feature status, completion percentage, and blockers.
version: 1.0.0 version: 1.0.0
depends-on: [] depends-on: []
@@ -1,16 +1,16 @@
--- ---
name: speckit.tasks name: speckit-tasks
description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts. description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
version: 1.0.0 version: 1.0.0
depends-on: depends-on:
- speckit.plan - speckit-plan
handoffs: handoffs:
- label: Analyze For Consistency - label: Analyze For Consistency
agent: speckit.analyze agent: speckit-analyze
prompt: Run a project analysis for consistency prompt: Run a project analysis for consistency
send: true send: true
- label: Implement Project - label: Implement Project
agent: speckit.implement agent: speckit-implement
prompt: Start the implementation in phases prompt: Start the implementation in phases
send: true send: true
--- ---
@@ -29,7 +29,7 @@ description: "Task list template for feature implementation"
============================================================================ ============================================================================
IMPORTANT: The tasks below are SAMPLE TASKS for illustration purposes only. IMPORTANT: The tasks below are SAMPLE TASKS for illustration purposes only.
The /speckit.tasks command MUST replace these with actual tasks based on: The /speckit-tasks command MUST replace these with actual tasks based on:
- User stories from spec.md (with their priorities P1, P2, P3...) - User stories from spec.md (with their priorities P1, P2, P3...)
- Feature requirements from plan.md - Feature requirements from plan.md
- Entities from data-model.md - Entities from data-model.md
@@ -1,9 +1,9 @@
--- ---
name: speckit.taskstoissues name: speckit-taskstoissues
description: Convert existing tasks into actionable, dependency-ordered issues for the feature based on available design artifacts. description: Convert existing tasks into actionable, dependency-ordered issues for the feature based on available design artifacts.
version: 1.1.0 version: 1.1.0
depends-on: depends-on:
- speckit.tasks - speckit-tasks
tools: ['github/github-mcp-server/issue_write'] tools: ['github/github-mcp-server/issue_write']
--- ---
@@ -112,7 +112,7 @@ Convert all tasks from `tasks.md` into well-structured issues on the appropriate
--- ---
_Auto-generated by speckit.taskstoissues from `tasks.md`_ _Auto-generated by speckit-taskstoissues from `tasks.md`_
``` ```
7. **Apply Labels** — Assign labels based on task metadata: 7. **Apply Labels** — Assign labels based on task metadata:
@@ -1,5 +1,5 @@
--- ---
name: speckit.tester name: speckit-tester
description: Execute tests, measure coverage, and report results. description: Execute tests, measure coverage, and report results.
version: 1.0.0 version: 1.0.0
depends-on: [] depends-on: []
@@ -1,9 +1,9 @@
--- ---
name: speckit.validate name: speckit-validate
description: Validate that implementation matches specification requirements. description: Validate that implementation matches specification requirements.
version: 1.0.0 version: 1.0.0
depends-on: depends-on:
- speckit.implement - speckit-implement
--- ---
## User Input ## User Input
@@ -2,79 +2,79 @@
description: Run the full speckit pipeline from specification to analysis in one command. description: Run the full speckit pipeline from specification to analysis in one command.
--- ---
# Workflow: speckit.all # Workflow: speckit-all
This meta-workflow orchestrates the **complete development lifecycle**, from specification through implementation and validation. For the preparation-only pipeline (steps 1-5), use `/speckit.prepare` instead. This meta-workflow orchestrates the **complete development lifecycle**, from specification through implementation and validation. For the preparation-only pipeline (steps 1-5), use `/speckit-prepare` instead.
## Preparation Phase (Steps 1-5) ## Preparation Phase (Steps 1-5)
1. **Specify** (`/speckit.specify`): 1. **Specify** (`/speckit-specify`):
- Use the `view_file` tool to read: `.agents/skills/speckit.specify/SKILL.md` - Use the `view_file` tool to read: `.agents/skills/speckit-specify/SKILL.md`
- Execute with user's feature description - Execute with user's feature description
- Creates: `spec.md` - Creates: `spec.md`
2. **Clarify** (`/speckit.clarify`): 2. **Clarify** (`/speckit-clarify`):
- Use the `view_file` tool to read: `.agents/skills/speckit.clarify/SKILL.md` - Use the `view_file` tool to read: `.agents/skills/speckit-clarify/SKILL.md`
- Execute to resolve ambiguities - Execute to resolve ambiguities
- Updates: `spec.md` - Updates: `spec.md`
3. **Plan** (`/speckit.plan`): 3. **Plan** (`/speckit-plan`):
- Use the `view_file` tool to read: `.agents/skills/speckit.plan/SKILL.md` - Use the `view_file` tool to read: `.agents/skills/speckit-plan/SKILL.md`
- Execute to create technical design - Execute to create technical design
- Creates: `plan.md` - Creates: `plan.md`
4. **Tasks** (`/speckit.tasks`): 4. **Tasks** (`/speckit-tasks`):
- Use the `view_file` tool to read: `.agents/skills/speckit.tasks/SKILL.md` - Use the `view_file` tool to read: `.agents/skills/speckit-tasks/SKILL.md`
- Execute to generate task breakdown - Execute to generate task breakdown
- Creates: `tasks.md` - Creates: `tasks.md`
5. **Analyze** (`/speckit.analyze`): 5. **Analyze** (`/speckit-analyze`):
- Use the `view_file` tool to read: `.agents/skills/speckit.analyze/SKILL.md` - Use the `view_file` tool to read: `.agents/skills/speckit-analyze/SKILL.md`
- Execute to validate consistency across spec, plan, and tasks - Execute to validate consistency across spec, plan, and tasks
- Output: Analysis report - Output: Analysis report
- **Gate**: If critical issues found, stop and fix before proceeding - **Gate**: If critical issues found, stop and fix before proceeding
## Implementation Phase (Steps 6-7) ## Implementation Phase (Steps 6-7)
6. **Implement** (`/speckit.implement`): 6. **Implement** (`/speckit-implement`):
- Use the `view_file` tool to read: `.agents/skills/speckit.implement/SKILL.md` - Use the `view_file` tool to read: `.agents/skills/speckit-implement/SKILL.md`
- Execute all tasks from `tasks.md` with anti-regression protocols - Execute all tasks from `tasks.md` with anti-regression protocols
- Output: Working implementation - Output: Working implementation
7. **Check** (`/speckit.checker`): 7. **Check** (`/speckit-checker`):
- Use the `view_file` tool to read: `.agents/skills/speckit.checker/SKILL.md` - Use the `view_file` tool to read: `.agents/skills/speckit-checker/SKILL.md`
- Run static analysis (linters, type checkers, security scanners) - Run static analysis (linters, type checkers, security scanners)
- Output: Checker report - Output: Checker report
## Verification Phase (Steps 8-10) ## Verification Phase (Steps 8-10)
8. **Test** (`/speckit.tester`): 8. **Test** (`/speckit-tester`):
- Use the `view_file` tool to read: `.agents/skills/speckit.tester/SKILL.md` - Use the `view_file` tool to read: `.agents/skills/speckit-tester/SKILL.md`
- Run tests with coverage - Run tests with coverage
- Output: Test + coverage report - Output: Test + coverage report
9. **Review** (`/speckit.reviewer`): 9. **Review** (`/speckit-reviewer`):
- Use the `view_file` tool to read: `.agents/skills/speckit.reviewer/SKILL.md` - Use the `view_file` tool to read: `.agents/skills/speckit-reviewer/SKILL.md`
- Perform code review - Perform code review
- Output: Review report with findings - Output: Review report with findings
10. **Validate** (`/speckit.validate`): 10. **Validate** (`/speckit-validate`):
- Use the `view_file` tool to read: `.agents/skills/speckit.validate/SKILL.md` - Use the `view_file` tool to read: `.agents/skills/speckit-validate/SKILL.md`
- Verify implementation matches spec requirements - Verify implementation matches spec requirements
- Output: Validation report (pass/fail) - Output: Validation report (pass/fail)
## Usage ## Usage
``` ```
/speckit.all "Build a user authentication system with OAuth2 support" /speckit-all "Build a user authentication system with OAuth2 support"
``` ```
## Pipeline Comparison ## Pipeline Comparison
| Pipeline | Steps | Use When | | Pipeline | Steps | Use When |
| ------------------ | ------------------------- | -------------------------------------- | | ------------------ | ------------------------- | -------------------------------------- |
| `/speckit.prepare` | 1-5 (Specify → Analyze) | Planning only — you'll implement later | | `/speckit-prepare` | 1-5 (Specify → Analyze) | Planning only — you'll implement later |
| `/speckit.all` | 1-10 (Specify → Validate) | Full lifecycle in one pass | | `/speckit-all` | 1-10 (Specify → Validate) | Full lifecycle in one pass |
## On Error ## On Error
@@ -82,4 +82,4 @@ If any step fails, stop the pipeline and report:
- Which step failed - Which step failed
- The error message - The error message
- Suggested remediation (e.g., "Run `/speckit.clarify` to resolve ambiguities before continuing") - Suggested remediation (e.g., "Run `/speckit-clarify` to resolve ambiguities before continuing")
@@ -2,13 +2,13 @@
description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync. description: Create or update the project constitution from interactive or provided principle inputs, ensuring all dependent templates stay in sync.
--- ---
# Workflow: speckit.constitution # Workflow: speckit-constitution
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill. - The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.constitution/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-constitution/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
@@ -2,14 +2,14 @@
description: Create or update the feature specification from a natural language feature description. description: Create or update the feature specification from a natural language feature description.
--- ---
# Workflow: speckit.specify # Workflow: speckit-specify
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill. - The user has provided an input prompt. Treat this as the primary input for the skill.
- This is typically the starting point of a new feature. - This is typically the starting point of a new feature.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.specify/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-specify/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
@@ -2,17 +2,17 @@
description: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarification questions and encoding answers back into the spec. description: Identify underspecified areas in the current feature spec by asking up to 5 highly targeted clarification questions and encoding answers back into the spec.
--- ---
# Workflow: speckit.clarify # Workflow: speckit-clarify
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill. - The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.clarify/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-clarify/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic. - Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**: 4. **On Error**:
- If `spec.md` is missing: Run `/speckit.specify` first to create the feature specification - If `spec.md` is missing: Run `/speckit-specify` first to create the feature specification
@@ -2,17 +2,17 @@
description: Execute the implementation planning workflow using the plan template to generate design artifacts. description: Execute the implementation planning workflow using the plan template to generate design artifacts.
--- ---
# Workflow: speckit.plan # Workflow: speckit-plan
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill. - The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.plan/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-plan/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic. - Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**: 4. **On Error**:
- If `spec.md` is missing: Run `/speckit.specify` first to create the feature specification - If `spec.md` is missing: Run `/speckit-specify` first to create the feature specification
@@ -2,18 +2,18 @@
description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts. description: Generate an actionable, dependency-ordered tasks.md for the feature based on available design artifacts.
--- ---
# Workflow: speckit.tasks # Workflow: speckit-tasks
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill. - The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.tasks/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-tasks/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic. - Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**: 4. **On Error**:
- If `plan.md` is missing: Run `/speckit.plan` first - If `plan.md` is missing: Run `/speckit-plan` first
- If `spec.md` is missing: Run `/speckit.specify` first - If `spec.md` is missing: Run `/speckit-specify` first
@@ -4,19 +4,19 @@ description: Perform a non-destructive cross-artifact consistency and quality an
// turbo-all // turbo-all
# Workflow: speckit.analyze # Workflow: speckit-analyze
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill. - The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.analyze/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-analyze/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic. - Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**: 4. **On Error**:
- If `spec.md` is missing: Run `/speckit.specify` first - If `spec.md` is missing: Run `/speckit-specify` first
- If `plan.md` is missing: Run `/speckit.plan` first - If `plan.md` is missing: Run `/speckit-plan` first
- If `tasks.md` is missing: Run `/speckit.tasks` first - If `tasks.md` is missing: Run `/speckit-tasks` first
@@ -2,19 +2,19 @@
description: Execute the implementation plan by processing and executing all tasks defined in tasks.md description: Execute the implementation plan by processing and executing all tasks defined in tasks.md
--- ---
# Workflow: speckit.implement # Workflow: speckit-implement
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill. - The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.implement/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-implement/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic. - Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**: 4. **On Error**:
- If `tasks.md` is missing: Run `/speckit.tasks` first - If `tasks.md` is missing: Run `/speckit-tasks` first
- If `plan.md` is missing: Run `/speckit.plan` first - If `plan.md` is missing: Run `/speckit-plan` first
- If `spec.md` is missing: Run `/speckit.specify` first - If `spec.md` is missing: Run `/speckit-specify` first
@@ -4,13 +4,13 @@ description: Run static analysis tools and aggregate results.
// turbo-all // turbo-all
# Workflow: speckit.checker # Workflow: speckit-checker
1. **Context Analysis**: 1. **Context Analysis**:
- The user may specify paths to check or run on entire project. - The user may specify paths to check or run on entire project.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.checker/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-checker/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
@@ -4,13 +4,13 @@ description: Execute tests, measure coverage, and report results.
// turbo-all // turbo-all
# Workflow: speckit.tester # Workflow: speckit-tester
1. **Context Analysis**: 1. **Context Analysis**:
- The user may specify test paths, options, or just run all tests. - The user may specify test paths, options, or just run all tests.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.tester/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-tester/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
@@ -2,13 +2,13 @@
description: Perform code review with actionable feedback and suggestions. description: Perform code review with actionable feedback and suggestions.
--- ---
# Workflow: speckit.reviewer # Workflow: speckit-reviewer
1. **Context Analysis**: 1. **Context Analysis**:
- The user may specify files to review, "staged" for git staged changes, or "branch" for branch diff. - The user may specify files to review, "staged" for git staged changes, or "branch" for branch diff.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.reviewer/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-reviewer/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
@@ -2,18 +2,18 @@
description: Validate that implementation matches specification requirements. description: Validate that implementation matches specification requirements.
--- ---
# Workflow: speckit.validate # Workflow: speckit-validate
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill. - The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.validate/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-validate/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic. - Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**: 4. **On Error**:
- If `tasks.md` is missing: Run `/speckit.tasks` first - If `tasks.md` is missing: Run `/speckit-tasks` first
- If implementation not started: Run `/speckit.implement` first - If implementation not started: Run `/speckit-implement` first
@@ -2,26 +2,26 @@
description: Execute the full preparation pipeline (Specify -> Clarify -> Plan -> Tasks -> Analyze) in sequence. description: Execute the full preparation pipeline (Specify -> Clarify -> Plan -> Tasks -> Analyze) in sequence.
--- ---
# Workflow: speckit.prepare # Workflow: speckit-prepare
This workflow orchestrates the sequential execution of the Speckit preparation phase skills (02-06). This workflow orchestrates the sequential execution of the Speckit preparation phase skills (02-06).
1. **Step 1: Specify (Skill 02)** 1. **Step 1: Specify (Skill 02)**
- Goal: Create or update the `spec.md` based on user input. - Goal: Create or update the `spec.md` based on user input.
- Action: Read and execute `.agents/skills/speckit.specify/SKILL.md`. - Action: Read and execute `.agents/skills/speckit-specify/SKILL.md`.
2. **Step 2: Clarify (Skill 03)** 2. **Step 2: Clarify (Skill 03)**
- Goal: Refine the `spec.md` by identifying and resolving ambiguities. - Goal: Refine the `spec.md` by identifying and resolving ambiguities.
- Action: Read and execute `.agents/skills/speckit.clarify/SKILL.md`. - Action: Read and execute `.agents/skills/speckit-clarify/SKILL.md`.
3. **Step 3: Plan (Skill 04)** 3. **Step 3: Plan (Skill 04)**
- Goal: Generate `plan.md` from the finalized spec. - Goal: Generate `plan.md` from the finalized spec.
- Action: Read and execute `.agents/skills/speckit.plan/SKILL.md`. - Action: Read and execute `.agents/skills/speckit-plan/SKILL.md`.
4. **Step 4: Tasks (Skill 05)** 4. **Step 4: Tasks (Skill 05)**
- Goal: Generate actionable `tasks.md` from the plan. - Goal: Generate actionable `tasks.md` from the plan.
- Action: Read and execute `.agents/skills/speckit.tasks/SKILL.md`. - Action: Read and execute `.agents/skills/speckit-tasks/SKILL.md`.
5. **Step 5: Analyze (Skill 06)** 5. **Step 5: Analyze (Skill 06)**
- Goal: Validate consistency across all design artifacts (spec, plan, tasks). - Goal: Validate consistency across all design artifacts (spec, plan, tasks).
- Action: Read and execute `.agents/skills/speckit.analyze/SKILL.md`. - Action: Read and execute `.agents/skills/speckit-analyze/SKILL.md`.
@@ -2,17 +2,17 @@
description: Generate a custom checklist for the current feature based on user requirements. description: Generate a custom checklist for the current feature based on user requirements.
--- ---
# Workflow: speckit.checklist # Workflow: speckit-checklist
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill. - The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.checklist/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-checklist/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic. - Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**: 4. **On Error**:
- If `spec.md` is missing: Run `/speckit.specify` first to create the feature specification - If `spec.md` is missing: Run `/speckit-specify` first to create the feature specification
@@ -2,13 +2,13 @@
description: Compare two versions of a spec or plan to highlight changes. description: Compare two versions of a spec or plan to highlight changes.
--- ---
# Workflow: speckit.diff # Workflow: speckit-diff
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt (optional file paths or version references). - The user has provided an input prompt (optional file paths or version references).
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.diff/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-diff/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
@@ -16,4 +16,4 @@ description: Compare two versions of a spec or plan to highlight changes.
4. **On Error**: 4. **On Error**:
- If no files to compare: Use current feature's `spec.md` vs git HEAD - If no files to compare: Use current feature's `spec.md` vs git HEAD
- If `spec.md` doesn't exist: Run `/speckit.specify` first - If `spec.md` doesn't exist: Run `/speckit-specify` first
@@ -2,13 +2,13 @@
description: Migrate existing projects into the speckit structure by generating spec.md, plan.md, and tasks.md from existing code. description: Migrate existing projects into the speckit structure by generating spec.md, plan.md, and tasks.md from existing code.
--- ---
# Workflow: speckit.migrate # Workflow: speckit-migrate
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt (path to analyze, feature name). - The user has provided an input prompt (path to analyze, feature name).
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.migrate/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-migrate/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
@@ -4,17 +4,17 @@ description: Challenge the specification with Socratic questioning to identify l
// turbo-all // turbo-all
# Workflow: speckit.quizme # Workflow: speckit-quizme
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill. - The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.quizme/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-quizme/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic. - Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**: 4. **On Error**:
- If required files don't exist, inform the user which prerequisite workflow to run first (e.g., `/speckit.specify` to create `spec.md`). - If required files don't exist, inform the user which prerequisite workflow to run first (e.g., `/speckit-specify` to create `spec.md`).
@@ -4,17 +4,17 @@ description: Display a dashboard showing feature status, completion percentage,
// turbo-all // turbo-all
# Workflow: speckit.status # Workflow: speckit-status
1. **Context Analysis**: 1. **Context Analysis**:
- The user may optionally specify a feature to focus on. - The user may optionally specify a feature to focus on.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.status/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-status/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic. - Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**: 4. **On Error**:
- If no features exist: Report "No features found. Run `/speckit.specify` to create your first feature." - If no features exist: Report "No features found. Run `/speckit-specify` to create your first feature."
@@ -2,17 +2,17 @@
description: Convert existing tasks into actionable, dependency-ordered GitHub issues for the feature based on available design artifacts. description: Convert existing tasks into actionable, dependency-ordered GitHub issues for the feature based on available design artifacts.
--- ---
# Workflow: speckit.taskstoissues # Workflow: speckit-taskstoissues
1. **Context Analysis**: 1. **Context Analysis**:
- The user has provided an input prompt. Treat this as the primary input for the skill. - The user has provided an input prompt. Treat this as the primary input for the skill.
2. **Load Skill**: 2. **Load Skill**:
- Use the `view_file` tool to read the skill file at: `.agents/skills/speckit.taskstoissues/SKILL.md` - Use the `view_file` tool to read the skill file at: `.agents/skills/speckit-taskstoissues/SKILL.md`
3. **Execute**: 3. **Execute**:
- Follow the instructions in the `SKILL.md` exactly. - Follow the instructions in the `SKILL.md` exactly.
- Apply the user's prompt as the input arguments/context for the skill's logic. - Apply the user's prompt as the input arguments/context for the skill's logic.
4. **On Error**: 4. **On Error**:
- If `tasks.md` is missing: Run `/speckit.tasks` first - If `tasks.md` is missing: Run `/speckit-tasks` first
@@ -88,14 +88,31 @@ export class DocumentNumberingController {
}) })
@RequirePermission('correspondence.read') @RequirePermission('correspondence.read')
async previewNumber(@Body() dto: PreviewNumberDto) { async previewNumber(@Body() dto: PreviewNumberDto) {
// ADR-019: Resolve UUID→INT for project and organization IDs
const resolvedProjectId = await this.numberingService.resolveIdForPreview(
'project',
dto.projectId
);
const resolvedOriginatorId =
await this.numberingService.resolveIdForPreview(
'organization',
dto.originatorOrganizationId
);
const resolvedRecipientId = dto.recipientOrganizationId
? await this.numberingService.resolveIdForPreview(
'organization',
dto.recipientOrganizationId
)
: undefined;
return this.numberingService.previewNumber({ return this.numberingService.previewNumber({
projectId: dto.projectId, projectId: resolvedProjectId,
originatorOrganizationId: dto.originatorOrganizationId, originatorOrganizationId: resolvedOriginatorId,
typeId: dto.correspondenceTypeId, typeId: dto.correspondenceTypeId,
subTypeId: dto.subTypeId, subTypeId: dto.subTypeId,
rfaTypeId: dto.rfaTypeId, rfaTypeId: dto.rfaTypeId,
disciplineId: dto.disciplineId, disciplineId: dto.disciplineId,
recipientOrganizationId: dto.recipientOrganizationId, recipientOrganizationId: resolvedRecipientId,
year: dto.year, year: dto.year,
customTokens: dto.customTokens, customTokens: dto.customTokens,
}); });
@@ -1,18 +1,16 @@
// File: src/modules/document-numbering/dto/preview-number.dto.ts // File: src/modules/document-numbering/dto/preview-number.dto.ts
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsInt, IsOptional, IsObject } from 'class-validator'; import { IsInt, IsNotEmpty, IsOptional, IsObject } from 'class-validator';
import { Type } from 'class-transformer'; import { Type } from 'class-transformer';
export class PreviewNumberDto { export class PreviewNumberDto {
@ApiProperty({ description: 'Project ID' }) @ApiProperty({ description: 'Project ID or UUID' })
@IsInt() @IsNotEmpty()
@Type(() => Number) projectId!: number | string;
projectId!: number;
@ApiProperty({ description: 'Originator organization ID' }) @ApiProperty({ description: 'Originator organization ID or UUID' })
@IsInt() @IsNotEmpty()
@Type(() => Number) originatorOrganizationId!: number | string;
originatorOrganizationId!: number;
@ApiProperty({ description: 'Correspondence type ID' }) @ApiProperty({ description: 'Correspondence type ID' })
@IsInt() @IsInt()
@@ -43,11 +41,9 @@ export class PreviewNumberDto {
@Type(() => Number) @Type(() => Number)
year?: number; year?: number;
@ApiPropertyOptional({ description: 'Recipient organization ID' }) @ApiPropertyOptional({ description: 'Recipient organization ID or UUID' })
@IsOptional() @IsOptional()
@IsInt() recipientOrganizationId?: number | string;
@Type(() => Number)
recipientOrganizationId?: number;
@ApiPropertyOptional({ description: 'Custom tokens' }) @ApiPropertyOptional({ description: 'Custom tokens' })
@IsOptional() @IsOptional()
@@ -26,6 +26,7 @@ import { GenerateNumberContext } from '../interfaces/document-numbering.interfac
import { ReserveNumberDto } from '../dto/reserve-number.dto'; import { ReserveNumberDto } from '../dto/reserve-number.dto';
import { ConfirmReservationDto } from '../dto/confirm-reservation.dto'; import { ConfirmReservationDto } from '../dto/confirm-reservation.dto';
import { Project } from '../../project/entities/project.entity'; import { Project } from '../../project/entities/project.entity';
import { Organization } from '../../organization/entities/organization.entity';
@Injectable() @Injectable()
export class DocumentNumberingService { export class DocumentNumberingService {
@@ -66,6 +67,33 @@ export class DocumentNumberingService {
return project.id; return project.id;
} }
/**
* ADR-019: Public facade for controllers to resolve project/organization IDs
*/
async resolveIdForPreview(
type: 'project' | 'organization',
id: number | string
): Promise<number> {
if (type === 'project') return this.resolveProjectId(id);
return this.resolveOrganizationId(id);
}
/**
* ADR-019: Resolve organizationId (INT or UUID string) to internal INT ID
*/
private async resolveOrganizationId(orgId: number | string): Promise<number> {
if (typeof orgId === 'number') return orgId;
const num = Number(orgId);
if (!isNaN(num)) return num;
const org = await this.entityManager.findOne(Organization, {
where: { uuid: orgId },
select: ['id'],
});
if (!org)
throw new NotFoundException(`Organization with UUID ${orgId} not found`);
return org.id;
}
async generateNextNumber( async generateNextNumber(
ctx: GenerateNumberContext ctx: GenerateNumberContext
): Promise<{ number: string; auditId: number }> { ): Promise<{ number: string; auditId: number }> {
@@ -13,6 +13,7 @@ import { AsBuiltDrawingRevision } from './entities/asbuilt-drawing-revision.enti
import { ShopDrawingRevision } from './entities/shop-drawing-revision.entity'; import { ShopDrawingRevision } from './entities/shop-drawing-revision.entity';
import { Attachment } from '../../common/file-storage/entities/attachment.entity'; import { Attachment } from '../../common/file-storage/entities/attachment.entity';
import { User } from '../user/entities/user.entity'; import { User } from '../user/entities/user.entity';
import { Project } from '../project/entities/project.entity';
// DTOs // DTOs
import { CreateAsBuiltDrawingDto } from './dto/create-asbuilt-drawing.dto'; import { CreateAsBuiltDrawingDto } from './dto/create-asbuilt-drawing.dto';
@@ -39,6 +40,22 @@ export class AsBuiltDrawingService {
private dataSource: DataSource private dataSource: DataSource
) {} ) {}
/**
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
*/
private async resolveProjectId(projectId: number | string): Promise<number> {
if (typeof projectId === 'number') return projectId;
const num = Number(projectId);
if (!isNaN(num)) return num;
const project = await this.dataSource.manager.findOne(Project, {
where: { uuid: projectId },
select: ['id'],
});
if (!project)
throw new NotFoundException(`Project with UUID ${projectId} not found`);
return project.id;
}
/** /**
* สร้าง AS Built Drawing ใหม่ พร้อม Revision แรก (Rev 0) * สร้าง AS Built Drawing ใหม่ พร้อม Revision แรก (Rev 0)
*/ */
@@ -73,9 +90,14 @@ export class AsBuiltDrawingService {
}); });
} }
// ADR-019: Resolve UUID→INT
const internalProjectId = await this.resolveProjectId(
createDto.projectId
);
// 3. Create Master AS Built Drawing // 3. Create Master AS Built Drawing
const asBuiltDrawing = queryRunner.manager.create(AsBuiltDrawing, { const asBuiltDrawing = queryRunner.manager.create(AsBuiltDrawing, {
projectId: createDto.projectId, projectId: internalProjectId,
drawingNumber: createDto.drawingNumber, drawingNumber: createDto.drawingNumber,
mainCategoryId: createDto.mainCategoryId, mainCategoryId: createDto.mainCategoryId,
subCategoryId: createDto.subCategoryId, subCategoryId: createDto.subCategoryId,
@@ -12,6 +12,7 @@ import { ContractDrawing } from './entities/contract-drawing.entity';
import { Attachment } from '../../common/file-storage/entities/attachment.entity'; import { Attachment } from '../../common/file-storage/entities/attachment.entity';
import { User } from '../user/entities/user.entity'; import { User } from '../user/entities/user.entity';
import { Contract } from '../contract/entities/contract.entity'; import { Contract } from '../contract/entities/contract.entity';
import { Project } from '../project/entities/project.entity';
// DTOs // DTOs
import { CreateContractDrawingDto } from './dto/create-contract-drawing.dto'; import { CreateContractDrawingDto } from './dto/create-contract-drawing.dto';
@@ -36,6 +37,22 @@ export class ContractDrawingService {
private dataSource: DataSource private dataSource: DataSource
) {} ) {}
/**
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
*/
private async resolveProjectId(projectId: number | string): Promise<number> {
if (typeof projectId === 'number') return projectId;
const num = Number(projectId);
if (!isNaN(num)) return num;
const project = await this.dataSource.manager.findOne(Project, {
where: { uuid: projectId },
select: ['id'],
});
if (!project)
throw new NotFoundException(`Project with UUID ${projectId} not found`);
return project.id;
}
/** /**
* Resolve issueDate from contract.startDate for file storage path * Resolve issueDate from contract.startDate for file storage path
* Fallback: contract.startDate → current date * Fallback: contract.startDate → current date
@@ -54,10 +71,13 @@ export class ContractDrawingService {
* - ผูกไฟล์แนบและ Commit ไฟล์จาก Temp -> Permanent * - ผูกไฟล์แนบและ Commit ไฟล์จาก Temp -> Permanent
*/ */
async create(createDto: CreateContractDrawingDto, user: User) { async create(createDto: CreateContractDrawingDto, user: User) {
// ADR-019: Resolve UUID→INT for projectId
const internalProjectId = await this.resolveProjectId(createDto.projectId);
// 1. ตรวจสอบเลขที่แบบซ้ำ (Unique per Project) // 1. ตรวจสอบเลขที่แบบซ้ำ (Unique per Project)
const exists = await this.drawingRepo.findOne({ const exists = await this.drawingRepo.findOne({
where: { where: {
projectId: createDto.projectId, projectId: internalProjectId,
contractDrawingNo: createDto.contractDrawingNo, contractDrawingNo: createDto.contractDrawingNo,
}, },
}); });
@@ -83,7 +103,7 @@ export class ContractDrawingService {
// 3. สร้าง Entity // 3. สร้าง Entity
const drawing = queryRunner.manager.create(ContractDrawing, { const drawing = queryRunner.manager.create(ContractDrawing, {
projectId: createDto.projectId, projectId: internalProjectId,
contractDrawingNo: createDto.contractDrawingNo, contractDrawingNo: createDto.contractDrawingNo,
title: createDto.title, title: createDto.title,
mapCatId: createDto.mapCatId, // Updated mapCatId: createDto.mapCatId, // Updated
@@ -98,9 +118,8 @@ export class ContractDrawingService {
// 4. Commit Files (ย้ายไฟล์จริง) // 4. Commit Files (ย้ายไฟล์จริง)
if (createDto.attachmentIds?.length) { if (createDto.attachmentIds?.length) {
// ✅ FIX TS2345: แปลง number[] เป็น string[] ก่อนส่ง // ✅ FIX TS2345: แปลง number[] เป็น string[] ก่อนส่ง
const issueDate = await this.resolveIssueDateByProject( const issueDate =
createDto.projectId await this.resolveIssueDateByProject(internalProjectId);
);
await this.fileStorageService.commit( await this.fileStorageService.commit(
createDto.attachmentIds.map(String), createDto.attachmentIds.map(String),
{ issueDate, documentType: 'ContractDrawing' } { issueDate, documentType: 'ContractDrawing' }
@@ -13,6 +13,7 @@ import { ShopDrawingRevision } from './entities/shop-drawing-revision.entity';
import { ContractDrawing } from './entities/contract-drawing.entity'; import { ContractDrawing } from './entities/contract-drawing.entity';
import { Attachment } from '../../common/file-storage/entities/attachment.entity'; import { Attachment } from '../../common/file-storage/entities/attachment.entity';
import { User } from '../user/entities/user.entity'; import { User } from '../user/entities/user.entity';
import { Project } from '../project/entities/project.entity';
// DTOs // DTOs
import { CreateShopDrawingDto } from './dto/create-shop-drawing.dto'; import { CreateShopDrawingDto } from './dto/create-shop-drawing.dto';
@@ -39,6 +40,22 @@ export class ShopDrawingService {
private dataSource: DataSource private dataSource: DataSource
) {} ) {}
/**
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
*/
private async resolveProjectId(projectId: number | string): Promise<number> {
if (typeof projectId === 'number') return projectId;
const num = Number(projectId);
if (!isNaN(num)) return num;
const project = await this.dataSource.manager.findOne(Project, {
where: { uuid: projectId },
select: ['id'],
});
if (!project)
throw new NotFoundException(`Project with UUID ${projectId} not found`);
return project.id;
}
/** /**
* สร้าง Shop Drawing ใหม่ พร้อม Revision แรก (Rev 0) * สร้าง Shop Drawing ใหม่ พร้อม Revision แรก (Rev 0)
*/ */
@@ -73,9 +90,14 @@ export class ShopDrawingService {
}); });
} }
// ADR-019: Resolve UUID→INT
const internalProjectId = await this.resolveProjectId(
createDto.projectId
);
// 3. Create Master Shop Drawing // 3. Create Master Shop Drawing
const shopDrawing = queryRunner.manager.create(ShopDrawing, { const shopDrawing = queryRunner.manager.create(ShopDrawing, {
projectId: createDto.projectId, projectId: internalProjectId,
drawingNumber: createDto.drawingNumber, drawingNumber: createDto.drawingNumber,
mainCategoryId: createDto.mainCategoryId, mainCategoryId: createDto.mainCategoryId,
subCategoryId: createDto.subCategoryId, subCategoryId: createDto.subCategoryId,
+11 -3
View File
@@ -11,10 +11,18 @@ import {
} from 'class-validator'; } from 'class-validator';
export class CreateRfaDto { export class CreateRfaDto {
@ApiProperty({ description: 'ID ของโครงการ', example: 1 }) @ApiProperty({ description: 'ID or UUID ของโครงการ', example: 1 })
@IsInt()
@IsNotEmpty() @IsNotEmpty()
projectId!: number; projectId!: number | string;
@ApiProperty({ description: 'Contract ID or UUID', required: false })
@IsString()
@IsOptional()
contractId?: string;
@ApiProperty({ description: 'To Organization ID or UUID', required: false })
@IsOptional()
toOrganizationId?: number | string;
@ApiProperty({ description: 'ID ของประเภท RFA', example: 1 }) @ApiProperty({ description: 'ID ของประเภท RFA', example: 1 })
@IsInt() @IsInt()
+41 -4
View File
@@ -12,6 +12,8 @@ import { InjectRepository } from '@nestjs/typeorm';
import { DataSource, In, Repository } from 'typeorm'; import { DataSource, In, Repository } from 'typeorm';
// Entities // Entities
import { Project } from '../project/entities/project.entity';
import { Organization } from '../organization/entities/organization.entity';
import { CorrespondenceRouting } from '../correspondence/entities/correspondence-routing.entity'; import { CorrespondenceRouting } from '../correspondence/entities/correspondence-routing.entity';
import { Correspondence } from '../correspondence/entities/correspondence.entity'; import { Correspondence } from '../correspondence/entities/correspondence.entity';
import { CorrespondenceRevision } from '../correspondence/entities/correspondence-revision.entity'; import { CorrespondenceRevision } from '../correspondence/entities/correspondence-revision.entity';
@@ -81,7 +83,42 @@ export class RfaService {
private searchService: SearchService private searchService: SearchService
) {} ) {}
/**
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
*/
private async resolveProjectId(projectId: number | string): Promise<number> {
if (typeof projectId === 'number') return projectId;
const num = Number(projectId);
if (!isNaN(num)) return num;
const project = await this.dataSource.manager.findOne(Project, {
where: { uuid: projectId },
select: ['id'],
});
if (!project)
throw new NotFoundException(`Project with UUID ${projectId} not found`);
return project.id;
}
/**
* ADR-019: Resolve organizationId (INT or UUID string) to internal INT ID
*/
private async resolveOrganizationId(orgId: number | string): Promise<number> {
if (typeof orgId === 'number') return orgId;
const num = Number(orgId);
if (!isNaN(num)) return num;
const org = await this.dataSource.manager.findOne(Organization, {
where: { uuid: orgId },
select: ['id'],
});
if (!org)
throw new NotFoundException(`Organization with UUID ${orgId} not found`);
return org.id;
}
async create(createDto: CreateRfaDto, user: User) { async create(createDto: CreateRfaDto, user: User) {
// ADR-019: Resolve UUID→INT for projectId
const internalProjectId = await this.resolveProjectId(createDto.projectId);
const rfaType = await this.rfaTypeRepo.findOne({ const rfaType = await this.rfaTypeRepo.findOne({
where: { id: createDto.rfaTypeId }, where: { id: createDto.rfaTypeId },
}); });
@@ -115,7 +152,7 @@ export class RfaService {
// [UPDATED] Generate Document Number with Discipline // [UPDATED] Generate Document Number with Discipline
const docNumber = await this.numberingService.generateNextNumber({ const docNumber = await this.numberingService.generateNextNumber({
projectId: createDto.projectId, projectId: internalProjectId,
originatorOrganizationId: userOrgId, originatorOrganizationId: userOrgId,
typeId: createDto.rfaTypeId, typeId: createDto.rfaTypeId,
disciplineId: createDto.disciplineId ?? 0, // ✅ ส่ง disciplineId ไปด้วย (0 ถ้าไม่มี) disciplineId: createDto.disciplineId ?? 0, // ✅ ส่ง disciplineId ไปด้วย (0 ถ้าไม่มี)
@@ -142,7 +179,7 @@ export class RfaService {
const correspondence = queryRunner.manager.create(Correspondence, { const correspondence = queryRunner.manager.create(Correspondence, {
correspondenceNumber: docNumber.number, correspondenceNumber: docNumber.number,
correspondenceTypeId: createDto.rfaTypeId, correspondenceTypeId: createDto.rfaTypeId,
projectId: createDto.projectId, projectId: internalProjectId,
originatorId: userOrgId, originatorId: userOrgId,
isInternal: false, isInternal: false,
createdBy: user.user_id, createdBy: user.user_id,
@@ -219,7 +256,7 @@ export class RfaService {
'rfa', 'rfa',
savedRfa.id.toString(), savedRfa.id.toString(),
{ {
projectId: createDto.projectId, projectId: internalProjectId,
originatorId: userOrgId, originatorId: userOrgId,
disciplineId: createDto.disciplineId, disciplineId: createDto.disciplineId,
initiatorId: user.user_id, initiatorId: user.user_id,
@@ -240,7 +277,7 @@ export class RfaService {
title: createDto.subject, title: createDto.subject,
description: createDto.description, description: createDto.description,
status: 'DRAFT', status: 'DRAFT',
projectId: createDto.projectId, projectId: internalProjectId,
createdAt: new Date(), createdAt: new Date(),
}) })
.catch((err) => this.logger.error(`Indexing failed: ${err}`)); .catch((err) => this.logger.error(`Indexing failed: ${err}`));
@@ -16,6 +16,7 @@ import { Correspondence } from '../correspondence/entities/correspondence.entity
import { CorrespondenceRevision } from '../correspondence/entities/correspondence-revision.entity'; import { CorrespondenceRevision } from '../correspondence/entities/correspondence-revision.entity';
import { CorrespondenceType } from '../correspondence/entities/correspondence-type.entity'; import { CorrespondenceType } from '../correspondence/entities/correspondence-type.entity';
import { CorrespondenceStatus } from '../correspondence/entities/correspondence-status.entity'; import { CorrespondenceStatus } from '../correspondence/entities/correspondence-status.entity';
import { Project } from '../project/entities/project.entity';
@Injectable() @Injectable()
export class TransmittalService { export class TransmittalService {
@@ -34,6 +35,22 @@ export class TransmittalService {
private dataSource: DataSource private dataSource: DataSource
) {} ) {}
/**
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
*/
private async resolveProjectId(projectId: number | string): Promise<number> {
if (typeof projectId === 'number') return projectId;
const num = Number(projectId);
if (!isNaN(num)) return num;
const project = await this.dataSource.manager.findOne(Project, {
where: { uuid: projectId },
select: ['id'],
});
if (!project)
throw new NotFoundException(`Project with UUID ${projectId} not found`);
return project.id;
}
async create(createDto: CreateTransmittalDto, user: User) { async create(createDto: CreateTransmittalDto, user: User) {
// 1. Get Transmittal Type (Assuming Code '901' or 'TRN') // 1. Get Transmittal Type (Assuming Code '901' or 'TRN')
const type = await this.typeRepo.findOne({ const type = await this.typeRepo.findOne({
@@ -58,9 +75,14 @@ export class TransmittalService {
} }
try { try {
// ADR-019: Resolve UUID→INT for projectId
const internalProjectId = await this.resolveProjectId(
createDto.projectId
);
// 2. Generate Number // 2. Generate Number
const docNumber = await this.numberingService.generateNextNumber({ const docNumber = await this.numberingService.generateNextNumber({
projectId: createDto.projectId, projectId: internalProjectId,
originatorOrganizationId: user.primaryOrganizationId, originatorOrganizationId: user.primaryOrganizationId,
typeId: type.id, typeId: type.id,
year: new Date().getFullYear(), year: new Date().getFullYear(),
@@ -74,7 +96,7 @@ export class TransmittalService {
const correspondence = queryRunner.manager.create(Correspondence, { const correspondence = queryRunner.manager.create(Correspondence, {
correspondenceNumber: docNumber.number, correspondenceNumber: docNumber.number,
correspondenceTypeId: type.id, correspondenceTypeId: type.id,
projectId: createDto.projectId, projectId: internalProjectId,
originatorId: user.primaryOrganizationId, originatorId: user.primaryOrganizationId,
isInternal: false, isInternal: false,
createdBy: user.user_id, createdBy: user.user_id,
@@ -5,7 +5,6 @@ import {
MinLength, MinLength,
IsOptional, IsOptional,
IsBoolean, IsBoolean,
IsInt,
} from 'class-validator'; } from 'class-validator';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
@@ -44,10 +43,12 @@ export class CreateUserDto {
@IsOptional() @IsOptional()
lineId?: string; lineId?: string;
@ApiPropertyOptional({ description: 'Primary Organization ID', example: 1 }) @ApiPropertyOptional({
@IsInt() description: 'Primary Organization ID or UUID',
example: 1,
})
@IsOptional() @IsOptional()
primaryOrganizationId?: number; // รับเป็น ID ของ Organization primaryOrganizationId?: number | string; // ADR-019: Accept INT or UUID
@ApiPropertyOptional({ description: 'Is user active?', default: true }) @ApiPropertyOptional({ description: 'Is user active?', default: true })
@IsBoolean() @IsBoolean()
@@ -16,11 +16,9 @@ export class SearchUserDto {
@Type(() => Number) @Type(() => Number)
roleId?: number; roleId?: number;
@ApiPropertyOptional({ description: 'Filter by Organization ID' }) @ApiPropertyOptional({ description: 'Filter by Organization ID or UUID' })
@IsOptional() @IsOptional()
@IsInt() primaryOrganizationId?: number | string; // ADR-019: Accept INT or UUID
@Type(() => Number)
primaryOrganizationId?: number;
@ApiPropertyOptional({ description: 'Page number', default: 1 }) @ApiPropertyOptional({ description: 'Page number', default: 1 })
@IsOptional() @IsOptional()
+40 -2
View File
@@ -17,6 +17,7 @@ import { Role } from './entities/role.entity';
import { Permission } from './entities/permission.entity'; import { Permission } from './entities/permission.entity';
import { CreateUserDto } from './dto/create-user.dto'; import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto'; import { UpdateUserDto } from './dto/update-user.dto';
import { Organization } from '../organization/entities/organization.entity';
@Injectable() @Injectable()
export class UserService { export class UserService {
@@ -30,13 +31,35 @@ export class UserService {
@Inject(CACHE_MANAGER) private cacheManager: Cache @Inject(CACHE_MANAGER) private cacheManager: Cache
) {} ) {}
/**
* ADR-019: Resolve organizationId (INT or UUID string) to internal INT ID
*/
private async resolveOrganizationId(orgId: number | string): Promise<number> {
if (typeof orgId === 'number') return orgId;
const num = Number(orgId);
if (!isNaN(num)) return num;
const org = await this.usersRepository.manager.findOne(Organization, {
where: { uuid: orgId },
select: ['id'],
});
if (!org)
throw new NotFoundException(`Organization with UUID ${orgId} not found`);
return org.id;
}
// 1. สร้างผู้ใช้ (Hash Password ก่อนบันทึก) // 1. สร้างผู้ใช้ (Hash Password ก่อนบันทึก)
async create(createUserDto: CreateUserDto): Promise<User> { async create(createUserDto: CreateUserDto): Promise<User> {
const salt = await bcrypt.genSalt(); const salt = await bcrypt.genSalt();
const hashedPassword = await bcrypt.hash(createUserDto.password, salt); const hashedPassword = await bcrypt.hash(createUserDto.password, salt);
// ADR-019: Resolve UUID→INT for primaryOrganizationId
const resolvedOrgId = createUserDto.primaryOrganizationId
? await this.resolveOrganizationId(createUserDto.primaryOrganizationId)
: undefined;
const newUser = this.usersRepository.create({ const newUser = this.usersRepository.create({
...createUserDto, ...createUserDto,
primaryOrganizationId: resolvedOrgId,
password: hashedPassword, password: hashedPassword,
}); });
@@ -91,8 +114,12 @@ export class UserService {
} }
if (primaryOrganizationId) { if (primaryOrganizationId) {
// ADR-019: Resolve UUID→INT for filtering
const resolvedOrgId = await this.resolveOrganizationId(
primaryOrganizationId
);
query.andWhere('user.primaryOrganizationId = :orgId', { query.andWhere('user.primaryOrganizationId = :orgId', {
orgId: primaryOrganizationId, orgId: resolvedOrgId,
}); });
} }
@@ -164,7 +191,18 @@ export class UserService {
updateUserDto.password = await bcrypt.hash(updateUserDto.password, salt); updateUserDto.password = await bcrypt.hash(updateUserDto.password, salt);
} }
const updatedUser = this.usersRepository.merge(user, updateUserDto); // ADR-019: Resolve UUID→INT for primaryOrganizationId before merge
const resolvedDto: Record<string, unknown> = { ...updateUserDto };
if (updateUserDto.primaryOrganizationId !== undefined) {
resolvedDto.primaryOrganizationId = await this.resolveOrganizationId(
updateUserDto.primaryOrganizationId
);
}
const updatedUser = this.usersRepository.merge(
user,
resolvedDto as Partial<User>
);
const savedUser = await this.usersRepository.save(updatedUser); const savedUser = await this.usersRepository.save(updatedUser);
// ⚠️ สำคัญ: เมื่อมีการแก้ไขข้อมูล User ต้องเคลียร์ Cache สิทธิ์เสมอ // ⚠️ สำคัญ: เมื่อมีการแก้ไขข้อมูล User ต้องเคลียร์ Cache สิทธิ์เสมอ
@@ -44,7 +44,7 @@ export default function UsersPage() {
const { data: users, isLoading } = useUsers({ const { data: users, isLoading } = useUsers({
search: search || undefined, search: search || undefined,
primaryOrganizationId: selectedOrgId ? parseInt(selectedOrgId) : undefined, primaryOrganizationId: selectedOrgId ?? undefined,
}); });
const { data: organizations = [] } = useOrganizations(); const { data: organizations = [] } = useOrganizations();
@@ -94,7 +94,7 @@ export default function UsersPage() {
header: "Organization", header: "Organization",
cell: ({ row }) => { cell: ({ row }) => {
const orgId = row.original.primaryOrganizationId; const orgId = row.original.primaryOrganizationId;
const org = (organizations as Organization[]).find((o) => o.id === orgId); const org = (organizations as Organization[]).find((o) => (o.id ?? o.uuid) === orgId?.toString() || o.uuid === orgId?.toString());
return org ? org.organizationCode : "-"; return org ? org.organizationCode : "-";
}, },
}, },
@@ -186,7 +186,7 @@ export default function UsersPage() {
<SelectContent> <SelectContent>
<SelectItem value="all">All Organizations</SelectItem> <SelectItem value="all">All Organizations</SelectItem>
{Array.isArray(organizations) && (organizations as Organization[]).map((org) => ( {Array.isArray(organizations) && (organizations as Organization[]).map((org) => (
<SelectItem key={org.uuid} value={(org.id ?? org.uuid).toString()}> <SelectItem key={org.uuid} value={org.uuid}>
{org.organizationCode} - {org.organizationName} {org.organizationCode} - {org.organizationName}
</SelectItem> </SelectItem>
))} ))}
+1 -1
View File
@@ -14,7 +14,7 @@ function RFAsContent() {
const page = parseInt(searchParams.get('page') || '1'); const page = parseInt(searchParams.get('page') || '1');
const statusId = searchParams.get('status') ? parseInt(searchParams.get('status')!) : undefined; const statusId = searchParams.get('status') ? parseInt(searchParams.get('status')!) : undefined;
const search = searchParams.get('search') || undefined; const search = searchParams.get('search') || undefined;
const projectId = searchParams.get('projectId') ? parseInt(searchParams.get('projectId')!) : undefined; const projectId = searchParams.get('projectId') || undefined; // ADR-019: Pass UUID string directly
const revisionStatus = (searchParams.get('revisionStatus') as 'CURRENT' | 'ALL' | 'OLD') || 'CURRENT'; const revisionStatus = (searchParams.get('revisionStatus') as 'CURRENT' | 'ALL' | 'OLD') || 'CURRENT';
+6 -9
View File
@@ -36,7 +36,7 @@ const userSchema = z.object({
confirmPassword: z.string().optional(), confirmPassword: z.string().optional(),
isActive: z.boolean().optional(), isActive: z.boolean().optional(),
lineId: z.string().optional(), lineId: z.string().optional(),
primaryOrganizationId: z.number().optional(), primaryOrganizationId: z.string().optional(),
roleIds: z.array(z.number()).optional(), roleIds: z.array(z.number()).optional(),
}).refine((data) => { }).refine((data) => {
// If password is provided (creating or resetting), confirmPassword must match // If password is provided (creating or resetting), confirmPassword must match
@@ -107,7 +107,7 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
lastName: user.lastName, lastName: user.lastName,
isActive: user.isActive, isActive: user.isActive,
lineId: user.lineId || "", lineId: user.lineId || "",
primaryOrganizationId: user.primaryOrganizationId, primaryOrganizationId: user.primaryOrganizationId?.toString(),
roleIds: user.roles?.map((r: any) => r.roleId) || [], roleIds: user.roles?.map((r: any) => r.roleId) || [],
password: "", password: "",
confirmPassword: "" confirmPassword: ""
@@ -221,22 +221,19 @@ export function UserDialog({ open, onOpenChange, user }: UserDialogProps) {
<div> <div>
<Label>Primary Organization</Label> <Label>Primary Organization</Label>
<Select <Select
value={watch("primaryOrganizationId")?.toString()} value={watch("primaryOrganizationId") ?? undefined}
onValueChange={(val) => onValueChange={(val) =>
setValue("primaryOrganizationId", parseInt(val)) setValue("primaryOrganizationId", val)
} }
> >
<SelectTrigger> <SelectTrigger>
<SelectValue placeholder="Select Organization" /> <SelectValue placeholder="Select Organization" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{/* TODO: ADR-019 — Backend DTO needs to accept UUID for primaryOrganization.
Currently using org.id which is excluded from API responses.
Temporary: org.id may still exist in some query responses. */}
{organizations?.map((org: any) => ( {organizations?.map((org: any) => (
<SelectItem <SelectItem
key={org.uuid ?? org.id} key={org.uuid}
value={(org.id ?? 0).toString()} value={org.uuid}
> >
{org.organizationCode} - {org.organizationName} {org.organizationCode} - {org.organizationName}
</SelectItem> </SelectItem>
+6 -6
View File
@@ -80,7 +80,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
const router = useRouter(); const router = useRouter();
// Hooks // Hooks
const { data: contractCategories } = useContractDrawingCategories(); const { data: contractCategories } = useContractDrawingCategories(projectId);
const { data: shopMainCats } = useShopMainCategories(projectId); const { data: shopMainCats } = useShopMainCategories(projectId);
const [selectedShopMainCat, setSelectedShopMainCat] = useState<number | undefined>(); const [selectedShopMainCat, setSelectedShopMainCat] = useState<number | undefined>();
@@ -202,7 +202,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{contractCategories?.map((c: any) => ( {contractCategories?.map((c: any) => (
<SelectItem key={c.id} value={String(c.id)}>{c.name}</SelectItem> <SelectItem key={c.id} value={String(c.id)}>{c.catName || c.catCode || c.name}</SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
@@ -253,7 +253,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{shopMainCats?.map((c: any) => ( {shopMainCats?.map((c: any) => (
<SelectItem key={c.id} value={String(c.id)}>{c.name}</SelectItem> <SelectItem key={c.id} value={String(c.id)}>{c.mainCategoryName || c.mainCategoryCode || c.name}</SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
@@ -269,7 +269,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{shopSubCats?.map((c: any) => ( {shopSubCats?.map((c: any) => (
<SelectItem key={c.id} value={String(c.id)}>{c.name}</SelectItem> <SelectItem key={c.id} value={String(c.id)}>{c.subCategoryName || c.subCategoryCode || c.name}</SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
@@ -323,7 +323,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{shopMainCats?.map((c: any) => ( {shopMainCats?.map((c: any) => (
<SelectItem key={c.id} value={String(c.id)}>{c.name}</SelectItem> <SelectItem key={c.id} value={String(c.id)}>{c.mainCategoryName || c.mainCategoryCode || c.name}</SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
@@ -339,7 +339,7 @@ export function DrawingUploadForm({ projectId = 1 }: DrawingUploadFormProps) {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{shopSubCats?.map((c: any) => ( {shopSubCats?.map((c: any) => (
<SelectItem key={c.id} value={String(c.id)}>{c.name}</SelectItem> <SelectItem key={c.id} value={String(c.id)}>{c.subCategoryName || c.subCategoryCode || c.name}</SelectItem>
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
@@ -37,7 +37,7 @@ const VARIABLES = [
export interface TemplateEditorProps { export interface TemplateEditorProps {
template?: NumberingTemplate; template?: NumberingTemplate;
projectId: number; projectId: number | string;
projectName: string; projectName: string;
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
correspondenceTypes: any[]; correspondenceTypes: any[];
+7 -7
View File
@@ -30,14 +30,14 @@ const rfaItemSchema = z.object({
unit: z.string().min(1, "Unit is required"), unit: z.string().min(1, "Unit is required"),
}); });
const rfaSchema = z.object({ const rfaSchema = z.object({
contractId: z.number().min(1, "Contract is required"), contractId: z.string().min(1, "Contract is required"),
disciplineId: z.number().min(1, "Discipline is required"), disciplineId: z.number().min(1, "Discipline is required"),
rfaTypeId: z.number().min(1, "Type is required"), rfaTypeId: z.number().min(1, "Type is required"),
subject: z.string().min(5, "Subject must be at least 5 characters"), subject: z.string().min(5, "Subject must be at least 5 characters"),
description: z.string().optional(), description: z.string().optional(),
body: z.string().optional(), body: z.string().optional(),
remarks: z.string().optional(), remarks: z.string().optional(),
toOrganizationId: z.number().min(1, "Please select To Organization"), toOrganizationId: z.string().min(1, "Please select To Organization"),
dueDate: z.string().optional(), dueDate: z.string().optional(),
shopDrawingRevisionIds: z.array(z.number()).optional(), shopDrawingRevisionIds: z.array(z.number()).optional(),
items: z.array(rfaItemSchema).min(1, "At least one item is required"), items: z.array(rfaItemSchema).min(1, "At least one item is required"),
@@ -63,14 +63,14 @@ export function RFAForm() {
} = useForm<RFAFormData>({ } = useForm<RFAFormData>({
resolver: zodResolver(rfaSchema), resolver: zodResolver(rfaSchema),
defaultValues: { defaultValues: {
contractId: 0, contractId: "",
disciplineId: 0, disciplineId: 0,
rfaTypeId: 0, rfaTypeId: 0,
subject: "", subject: "",
description: "", description: "",
body: "", body: "",
remarks: "", remarks: "",
toOrganizationId: 0, toOrganizationId: "",
dueDate: "", dueDate: "",
shopDrawingRevisionIds: [], shopDrawingRevisionIds: [],
items: [{ itemNo: "1", description: "", quantity: 0, unit: "" }], items: [{ itemNo: "1", description: "", quantity: 0, unit: "" }],
@@ -182,7 +182,7 @@ export function RFAForm() {
<div> <div>
<Label>Contract *</Label> <Label>Contract *</Label>
<Select <Select
onValueChange={(val) => setValue("contractId", Number(val))} onValueChange={(val) => setValue("contractId", val)}
disabled={isLoadingContracts} disabled={isLoadingContracts}
> >
<SelectTrigger> <SelectTrigger>
@@ -190,8 +190,8 @@ export function RFAForm() {
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{contracts?.map((c: any) => ( {contracts?.map((c: any) => (
<SelectItem key={c.id || c.contract_id} value={String(c.id || c.contract_id)}> <SelectItem key={c.id} value={String(c.id)}>
{c.name || c.contract_no} {c.contractName || c.name || c.contractCode}
</SelectItem> </SelectItem>
))} ))}
</SelectContent> </SelectContent>
@@ -59,7 +59,7 @@ const itemSchema = z.object({
// Main form schema // Main form schema
const formSchema = z.object({ const formSchema = z.object({
correspondenceId: z.number().min(1, "Correspondence is required"), // Linked correspondence (e.g. Originator Letter) correspondenceId: z.string().min(1, "Correspondence is required"), // ADR-019: UUID string
subject: z.string().min(1, "Subject is required"), subject: z.string().min(1, "Subject is required"),
purpose: z.enum(["FOR_APPROVAL", "FOR_INFORMATION", "FOR_REVIEW", "OTHER"]), purpose: z.enum(["FOR_APPROVAL", "FOR_INFORMATION", "FOR_REVIEW", "OTHER"]),
remarks: z.string().optional(), remarks: z.string().optional(),
@@ -127,7 +127,7 @@ export function TransmittalForm() {
const selectedDocId = form.watch("correspondenceId"); const selectedDocId = form.watch("correspondenceId");
const selectedDoc = correspondences?.data?.find( const selectedDoc = correspondences?.data?.find(
(c: { id: number }) => c.id === selectedDocId (c: { uuid: string }) => c.uuid === selectedDocId
); );
return ( return (
@@ -172,19 +172,19 @@ export function TransmittalForm() {
<CommandEmpty>No document found.</CommandEmpty> <CommandEmpty>No document found.</CommandEmpty>
<CommandGroup> <CommandGroup>
{correspondences?.data?.map( {correspondences?.data?.map(
(doc: { id: number; correspondence_number: string }) => ( (doc: { uuid: string; correspondence_number: string }) => (
<CommandItem <CommandItem
key={doc.id} key={doc.uuid}
value={doc.correspondence_number} value={doc.correspondence_number}
onSelect={() => { onSelect={() => {
form.setValue("correspondenceId", doc.id); form.setValue("correspondenceId", doc.uuid);
setDocOpen(false); setDocOpen(false);
}} }}
> >
<Check <Check
className={cn( className={cn(
"mr-2 h-4 w-4", "mr-2 h-4 w-4",
doc.id === field.value doc.uuid === field.value
? "opacity-100" ? "opacity-100"
: "opacity-0" : "opacity-0"
)} )}
+4 -3
View File
@@ -104,10 +104,11 @@ export function useCorrespondenceTypes() {
// --- Drawing Categories Hooks --- // --- Drawing Categories Hooks ---
export function useContractDrawingCategories() { export function useContractDrawingCategories(projectId?: number | string) {
return useQuery({ return useQuery({
queryKey: ['contract-drawing-categories'], queryKey: ['contract-drawing-categories', projectId],
queryFn: () => masterDataService.getContractDrawingCategories(), queryFn: () => masterDataService.getContractDrawingCategories(projectId),
enabled: !!projectId,
}); });
} }
+3 -2
View File
@@ -10,7 +10,7 @@ import apiClient from '@/lib/api/client';
*/ */
export interface NumberingTemplate { export interface NumberingTemplate {
id: number; id: number;
projectId: number; projectId: number | string;
correspondenceTypeId: number | null; // null = Default Format for project correspondenceTypeId: number | null; // null = Default Format for project
correspondenceType?: { correspondenceType?: {
id: number; id: number;
@@ -18,9 +18,10 @@ export interface NumberingTemplate {
typeName: string; typeName: string;
} | null; } | null;
project?: { project?: {
id: number; id: number | string;
projectCode: string; projectCode: string;
projectName: string; projectName: string;
uuid?: string;
}; };
formatTemplate: string; formatTemplate: string;
description?: string; description?: string;
+4 -2
View File
@@ -187,8 +187,10 @@ export const masterDataService = {
// --- Drawing Categories --- // --- Drawing Categories ---
getContractDrawingCategories: async () => { getContractDrawingCategories: async (projectId?: number | string) => {
const response = await apiClient.get("/drawings/contract/categories"); const response = await apiClient.get("/drawings/contract/categories", {
params: { projectId }
});
return response.data.data || response.data; return response.data.data || response.data;
}, },
@@ -15,7 +15,7 @@ const mapWorkflow = (backendObj: any): Workflow => {
workflowId: backendObj.id, workflowId: backendObj.id,
workflowName: backendObj.dsl?.workflowName || backendObj.workflow_code, workflowName: backendObj.dsl?.workflowName || backendObj.workflow_code,
description: backendObj.description || backendObj.dsl?.description || '', description: backendObj.description || backendObj.dsl?.description || '',
workflowType: backendObj.workflow_code, workflowType: backendObj.workflow_code?.toUpperCase() || backendObj.workflow_code,
version: backendObj.version || 1, version: backendObj.version || 1,
isActive: backendObj.is_active, isActive: backendObj.is_active,
dslDefinition: typeof backendObj.dsl === 'string' ? backendObj.dsl : backendObj.dsl?.dslDefinition || JSON.stringify(backendObj.dsl, null, 2), dslDefinition: typeof backendObj.dsl === 'string' ? backendObj.dsl : backendObj.dsl?.dslDefinition || JSON.stringify(backendObj.dsl, null, 2),
+8 -5
View File
@@ -2,8 +2,8 @@
// --- Create --- // --- Create ---
export interface CreateRfaDto { export interface CreateRfaDto {
/** ID ของโครงการ */ /** ID or UUID ของโครงการ */
projectId: number; projectId: number | string; // ADR-019: Accept UUID
/** ประเภท RFA (เช่น DWG, MAT) */ /** ประเภท RFA (เช่น DWG, MAT) */
rfaTypeId: number; rfaTypeId: number;
@@ -20,8 +20,11 @@ export interface CreateRfaDto {
/** หมายเหตุ */ /** หมายเหตุ */
remarks?: string; remarks?: string;
/** Contract UUID (optional) */
contractId?: string; // ADR-019: Contract UUID
/** ส่งถึงใคร (สำหรับ Routing Step 1) */ /** ส่งถึงใคร (สำหรับ Routing Step 1) */
toOrganizationId: number; toOrganizationId: number | string; // ADR-019: Accept UUID
/** รายละเอียดเพิ่มเติม */ /** รายละเอียดเพิ่มเติม */
description?: string; description?: string;
@@ -41,8 +44,8 @@ export type UpdateRfaDto = Partial<CreateRfaDto>;
// --- Search --- // --- Search ---
export interface SearchRfaDto { export interface SearchRfaDto {
/** Filter by Project ID (optional to allow cross-project search) */ /** Filter by Project ID or UUID (optional to allow cross-project search) */
projectId?: number; projectId?: number | string; // ADR-019: Accept UUID
/** กรองตามประเภท RFA */ /** กรองตามประเภท RFA */
rfaTypeId?: number; rfaTypeId?: number;
@@ -9,12 +9,12 @@ export enum TransmittalPurpose {
// --- Create --- // --- Create ---
export interface CreateTransmittalDto { export interface CreateTransmittalDto {
projectId?: number; projectId?: number | string; // ADR-019: Accept UUID
recipientOrganizationId?: number; recipientOrganizationId?: number | string; // ADR-019: Accept UUID
subject: string; subject: string;
purpose?: string; purpose?: string;
remarks?: string; remarks?: string;
correspondenceId: number; // For now linked correspondence correspondenceId: number | string; // ADR-019: Accept UUID
items: CreateTransmittalItemDto[]; items: CreateTransmittalItemDto[];
} }
@@ -30,7 +30,7 @@ export type UpdateTransmittalDto = Partial<CreateTransmittalDto>;
// --- Search --- // --- Search ---
export interface SearchTransmittalDto { export interface SearchTransmittalDto {
/** บังคับระบุ Project */ /** บังคับระบุ Project */
projectId: number; projectId: number | string; // ADR-019: Accept UUID
purpose?: TransmittalPurpose; purpose?: TransmittalPurpose;
+1 -1
View File
@@ -8,7 +8,7 @@ export interface CreateUserDto {
firstName?: string; firstName?: string;
lastName?: string; lastName?: string;
lineId?: string; lineId?: string;
primaryOrganizationId?: number; primaryOrganizationId?: number | string; // ADR-019: Accept UUID
isActive?: boolean; isActive?: boolean;
} }
+3 -1
View File
@@ -51,7 +51,9 @@ export interface RFA {
} }
export interface CreateRFADto { export interface CreateRFADto {
projectId: number; projectId: number | string; // ADR-019: Accept UUID
contractId?: string; // ADR-019: Contract UUID
toOrganizationId?: number | string; // ADR-019: Recipient org UUID
rfaTypeId: number; rfaTypeId: number;
disciplineId?: number; disciplineId?: number;
subject: string; subject: string;
+3 -3
View File
@@ -20,7 +20,7 @@ export interface User {
lastName: string; lastName: string;
isActive: boolean; isActive: boolean;
lineId?: string; lineId?: string;
primaryOrganizationId?: number; primaryOrganizationId?: number | string; // ADR-019: May be INT or UUID
organization?: UserOrganization; organization?: UserOrganization;
roles?: Role[]; roles?: Role[];
@@ -42,7 +42,7 @@ export interface CreateUserDto {
password?: string; password?: string;
isActive: boolean; isActive: boolean;
lineId?: string; lineId?: string;
primaryOrganizationId?: number; primaryOrganizationId?: number | string; // ADR-019: Accept UUID
roleIds: number[]; roleIds: number[];
} }
@@ -53,5 +53,5 @@ export interface SearchUserDto {
limit?: number; limit?: number;
search?: string; search?: string;
roleId?: number; roleId?: number;
primaryOrganizationId?: number; primaryOrganizationId?: number | string; // ADR-019: Accept UUID
} }