diff --git a/.github/agents/copilot-instructions.md b/.github/agents/copilot-instructions.md index aedc49d..01f172e 100644 --- a/.github/agents/copilot-instructions.md +++ b/.github/agents/copilot-instructions.md @@ -1,43 +1,10 @@ # ordrctrl Development Guidelines -Auto-generated from all feature plans. Last updated: 2026-03-04 +Auto-generated from all feature plans. Last updated: 2026-03-22 ## Active Technologies -- TypeScript 5.4 (backend: Node.js/Fastify; frontend: React/Next.js 14) + Fastify (backend API), React + Tailwind CSS (frontend), Prisma ORM (002-uncheck-completed) -- PostgreSQL 16 (primary data), Redis 7 (session cache, BullMQ job queue) (002-uncheck-completed) -- TypeScript 5.4 (backend + frontend) + Fastify 4, Prisma 5, BullMQ (backend); Next.js 14 + React 18 + Tailwind CSS (frontend); Zod (validation); Vitest + Supertest (tests) (003-selective-import) -- PostgreSQL 16 (Prisma ORM) — two new columns on `Integration` table; Redis 7 for BullMQ sync queue (003-selective-import) -- TypeScript 5.x / Node.js 20 LTS (backend); React 18 + TypeScript (frontend) + Fastify (HTTP server), Prisma (ORM + migrations), BullMQ (sync queue), node-fetch (CalDAV HTTP), zod (input validation), AES-256-GCM via `backend/src/lib/encryption.ts` (004-apple-basic-auth) -- PostgreSQL via Prisma — one new field (`calendarEventWindowDays Int @default(30)`) on existing `Integration` model; one migration required (004-apple-basic-auth) -- TypeScript (Node.js backend, React frontend) + Fastify (API), Prisma (ORM), Zod (validation), React Query (frontend state) (005-feed-dismissal) -- TypeScript — backend Node.js, frontend Next.js 14 / React 18 + Prisma ORM, BullMQ, existing IntegrationAdapter interface (007-source-sync) -- PostgreSQL via Prisma — one schema migration required (new field + enum) (007-source-sync) -- TypeScript (Node.js 18, Next.js 14) + Prisma ORM, Express-style API routes (Next.js App Router + custom backend), React 18, BullMQ (sync scheduler) (008-clear-completed) -- TypeScript (Node.js 20, Next.js 14) + Fastify (backend), Next.js + React 18 (frontend), Prisma ORM, BullMQ (sync queue), Vitest (tests) (009-multi-account) -- PostgreSQL (via Prisma) (009-multi-account) -- TypeScript 5 (backend Node.js 20, frontend Next.js 14) + Fastify 4, Prisma 5, React 18, Tailwind CSS, BullMQ, Redis (010-task-inbox) -- PostgreSQL (via Prisma ORM) (010-task-inbox) -- TypeScript 5 (backend Node.js 20, frontend Next.js 14.1.3) + Fastify 4, Prisma 5, React 18, Tailwind CSS, BullMQ, Redis (011-feed-ux-enhancements) -- TypeScript 5.x (frontend + backend) + Next.js 14 App Router, React, Express, Prisma, BullMQ, Redis (012-app-polish-bugfix) -- PostgreSQL (via Prisma), Redis (sync-status cache) (012-app-polish-bugfix) -- TypeScript 5.x (backend Node 20 LTS + frontend Next.js 15 App Router) + Fastify (backend API), React 19, Prisma 5 ORM, PostgreSQL 16, Redis (013-task-content-enhancements) -- PostgreSQL via Prisma (primary), Redis (short-lived sync cache metadata) (013-task-content-enhancements) -- TypeScript 5.4.2 / React 18.2.0 / Node.js 18+ + Vite 5.1.4 (already installed), react-router-dom v6 (to add), Tailwind CSS 3.4.1 (014-vite-migration) -- N/A — frontend only; all persistence is in the backend (014-vite-migration) -- TypeScript 5.4 (frontend), Rust 1.75+ (Tauri shell), Swift/Kotlin (Capacitor native bridge — generated) (015-native-app-targets) -- No new storage layer — session cookies persisted by native WebView OS cookie jar; `@capacitor/preferences` for lightweight native-only flags (e.g., last-seen notification timestamp) (015-native-app-targets) -- Redis (existing) — new `oauth:state:{value}` key namespace with 5-min TTL (016-native-auth-fixes) -- TypeScript 5.4 (frontend + backend), Rust 1.75+ (Tauri shell — unaffected) + Fastify 4, Prisma ORM, React 18, Vite 5, Vitest — all existing; no new dependencies (017-task-rename-polish) -- PostgreSQL (existing `SyncOverride` table — adding one new enum value); no schema migrations beyond enum update (017-task-rename-polish) -- TypeScript 5.4 (frontend), Node 20 (CI runners) (018-e2e-testing) -- N/A (tests are read-only; no test data written to persistent storage) (018-e2e-testing) -- TypeScript 5.4 / React 18.2 + React Router DOM v6, Capacitor 8 (iOS/Android), Tauri 2 (macOS/Windows), Vite, Vitest, Playwright (019-task-timeline-view) -- No new storage — `FeedItem[]` from existing `useFeed` hook; view preference via existing `NativePrefs` pattern (`@capacitor/preferences` → `localStorage` fallback) (019-task-timeline-view) -- New `frontend/src/utils/dateUtils.ts`: shared timezone-safe date helpers — `isAllDayDate`, `toLocalMidnight`, `formatRelativeDay`, `formatLocalTime`, `toLocalDateTimeInput`, `toLocalDateInput`; imported by `useTimeline`, `FeedItemRow`, `EditTaskModal` (019-task-timeline-view) -- All-day calendar events use UTC noon sentinel (`T12:00:00.000Z`). Use `isAllDayDate()` to detect; use `toLocalMidnight()` for bucket/day comparison — do NOT call `setHours(0,0,0,0)` directly on dates from external sources. For display, use `formatLocalTime()` (returns `""` for all-day) and `toLocalDateInput()` / `toLocalDateTimeInput()` for form inputs (019-task-timeline-view) -- Apple Calendar events may have `dueAt: null`; always use `dueAt ?? startAt` as the date key for bucketing and display (019-task-timeline-view) -- TypeScript 5.x (frontend + backend) + Next.js 14 (frontend), Fastify 4 (backend API), Prisma (ORM), (001-mvp-core) +- Node.js (TypeScript) — pnpm monorepo; hook scripts in Bash + Copilot CLI agent/skill/hook system; GitHub MCP; `gh` CLI fallback; `jq` (Bash hooks); `git` (020-speckit-workflow) ## Project Structure @@ -52,13 +19,11 @@ npm test && npm run lint ## Code Style -TypeScript 5.x (frontend + backend): Follow standard conventions +Node.js (TypeScript) — pnpm monorepo; hook scripts in Bash: Follow standard conventions ## Recent Changes -- 019-task-timeline-view: Added timeline view components, useTimeline hook, dateUtils.ts (toLocalMidnight/isAllDayDate/formatLocalTime), Apple Calendar dueAt fix, UTC-midnight all-day display fixes -- 018-e2e-testing: Added TypeScript 5.4 (frontend), Node 20 (CI runners) -- 017-task-rename-polish: Added TypeScript 5.4 (frontend + backend), Rust 1.75+ (Tauri shell — unaffected) + Fastify 4, Prisma ORM, React 18, Vite 5, Vitest — all existing; no new dependencies +- 020-speckit-workflow: Added Node.js (TypeScript) — pnpm monorepo; hook scripts in Bash + Copilot CLI agent/skill/hook system; GitHub MCP; `gh` CLI fallback; `jq` (Bash hooks); `git` diff --git a/.github/agents/issue-triage.agent.md b/.github/agents/issue-triage.agent.md new file mode 100644 index 0000000..d8f5e76 --- /dev/null +++ b/.github/agents/issue-triage.agent.md @@ -0,0 +1,171 @@ +--- +description: Analyze all open GitHub issues for a repository, group related issues by theme, and produce a prioritized triage report with actionable recommendations. +tools: ['github/github-mcp-server/list_issues', 'github/github-mcp-server/issue_read', 'github/github-mcp-server/search_issues'] +--- + +## User Input + +```text +$ARGUMENTS +``` + +You **MUST** consider the user input before proceeding. The user may specify: +- A specific repository (`owner/repo`) — if not provided, detect from `git config --get remote.origin.url` +- A focus area or label filter to narrow scope +- A maximum number of issues to analyze (default: all open issues, up to 200) + +## Goal + +Fetch all open issues for the target repository, semantically cluster them into related groups, score each issue for priority, and produce a compact triage report the team can act on immediately. + +## Operating Constraints + +**READ-ONLY**: Do **not** create, edit, close, or label any issues. Output a structured report only. + +**Repository Safety**: Confirm the target repository before fetching. If the user did not specify a repo, derive it from the Git remote and display it before proceeding. + +## Execution Steps + +### 1. Resolve Target Repository + +If the user provided `owner/repo`, use it directly. Otherwise, run: + +```bash +git config --get remote.origin.url +``` + +Parse the output to extract `owner` and `repo`. Display: `Analyzing issues for: owner/repo` before proceeding. + +> [!CAUTION] +> Never fetch or create issues in a repository that does not match the user's intent. Confirm with the user if ambiguous. + +### 2. Fetch Open Issues + +Use the GitHub MCP `list_issues` tool with `state: OPEN`. Page through results until all open issues are collected (respect the 200-issue cap unless the user overrides). For each issue capture: + +- Issue number, title, body (first 300 chars), labels, assignees, created_at, updated_at, comment count, reactions count + +If the user specified a label filter, apply it during the fetch. + +### 3. Build Semantic Clusters + +Group issues by **theme** using the following signals (in priority order): + +1. **Explicit labels** — issues sharing the same label are strong candidates for the same cluster +2. **Title keyword overlap** — extract noun phrases; issues sharing 2+ key terms belong together +3. **Body reference overlap** — issues mentioning the same file paths, component names, API endpoints, or error messages +4. **Cross-references** — issues that link to each other (`#NNN` mentions) are in the same cluster + +Produce clusters with: +- A short **cluster name** (3–5 words, title case) +- A one-sentence **theme description** +- The list of issue numbers belonging to the cluster +- A `SINGLETON` marker for issues that don't cluster with anything + +Limit clusters to a maximum of 15 (merge the smallest clusters into an `Other` bucket if needed). + +### 4. Score Each Issue for Priority + +Assign a **priority score** (1–10, higher = more urgent) using this weighted rubric: + +| Signal | Weight | Notes | +|--------|--------|-------| +| Age (days open) | 20% | >90 days = max signal | +| Reaction count (👍, ❤️, 🚀) | 25% | Proxy for user demand | +| Comment count | 15% | High engagement = high interest | +| Recency of last update | 15% | Recently touched = in-flight work | +| Label severity | 25% | `bug` > `enhancement` > `question`; `critical`/`P0` labels = max | + +Round score to one decimal. Break ties by reaction count. + +Within each cluster, rank issues by score descending. + +### 5. Identify Cross-Cutting Concerns + +After clustering, scan for these patterns and call them out explicitly: + +- **Blocking relationships**: Issues where one references another as a dependency or blocker +- **Duplicates**: Issues with near-identical titles or bodies (>80% semantic overlap) — flag as `POSSIBLE DUPLICATE of #NNN` +- **Stale issues**: Open >180 days with 0 comments and 0 reactions — flag as `STALE` +- **Needs triage**: Issues with no labels assigned + +### 6. Produce the Triage Report + +Output a Markdown report with the following structure: + +--- + +## Issue Triage Report — `owner/repo` + +**Generated**: [timestamp] +**Open Issues Analyzed**: N +**Clusters Identified**: N + +--- + +### Priority Queue (Top 10 Issues Across All Clusters) + +| Rank | # | Title | Cluster | Score | Age | 👍 | Labels | +|------|---|-------|---------|-------|-----|----|--------| + +--- + +### Clusters + +For each cluster (sorted by highest issue score descending): + +#### [Cluster Name] +> [Theme description] + +| # | Title | Score | Age | 👍 | Labels | Assignee | +|---|-------|-------|-----|----|--------|----------| + +--- + +### Cross-Cutting Concerns + +**Blocking Chains** (if any): +- #NNN → #NNN → #NNN + +**Possible Duplicates** (if any): +- #NNN may duplicate #NNN — [reason] + +**Stale Issues** (if any): +- #NNN — [title] (open NNN days, 0 activity) + +**Needs Triage** (no labels): +- #NNN — [title] + +--- + +### Metrics Summary + +| Metric | Value | +|--------|-------| +| Total open issues | N | +| Clusters | N | +| Singletons | N | +| Possible duplicates | N | +| Stale (>180d, no activity) | N | +| Needs triage (no labels) | N | +| Avg issue age (days) | N | + +--- + +### Recommended Next Actions + +Based on the analysis, suggest 3–5 concrete, prioritized actions such as: +- "Close or merge duplicates #NNN and #NNN" +- "Add labels to N unlabeled issues before next sprint planning" +- "Cluster `[name]` has N high-priority bugs — consider a dedicated bug bash" +- "Issues #NNN and #NNN should be linked as a blocking dependency" + +--- + +## Operating Principles + +- **Never modify issues** — this is a read-only analysis +- **Cite evidence** for every grouping decision (e.g., "shares label `auth`, mentions `session.ts`") +- **Prefer signal over volume** — a short, accurate report beats an exhaustive one +- **Graceful degradation** — if the repo has <5 open issues, skip clustering and just produce a prioritized list +- **Transparency** — if pagination was cut off at the 200-issue cap, note how many were not analyzed diff --git a/.github/agents/speckit.analyze.agent.md b/.github/agents/speckit.analyze.agent.md index 98b04b0..a25affc 100644 --- a/.github/agents/speckit.analyze.agent.md +++ b/.github/agents/speckit.analyze.agent.md @@ -22,7 +22,17 @@ Identify inconsistencies, duplications, ambiguities, and underspecified items ac ## Execution Steps -### 1. Initialize Analysis Context +### 1. Context Loading (run at startup before any output) + +1. Read `.specify/memory/constitution.md` +2. Read `.specify/memory/stack.md` — if absent, warn: "⚠️ stack.md missing — stack-specific command validation will be skipped" +3. If stack.md is present, extract `regression_tests` section to use for command validation: + - `lint_cmd`, `test_cmd`, `e2e_cmd` — verify these exact commands appear in tasks.md regression steps + - If tasks.md uses different (hardcoded) commands, flag as a finding in the analysis report +4. Read current spec's `spec.md`, `plan.md`, `tasks.md` +5. Output one-line summary: `Loaded: [spec name] | Status: [status] | Stack: [packaging tool]` + +### 2. Initialize Analysis Context Run `.specify/scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks` once from repo root and parse JSON for FEATURE_DIR and AVAILABLE_DOCS. Derive absolute paths: @@ -158,10 +168,60 @@ At end of report, output a concise Next Actions block: - 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'" -### 8. Offer Remediation +### 8. Drift Detection + +Before advancing spec status, detect whether the implementation diverged from the original plan: + +1. **Find branch-creation commit** (approximate base): + ```bash + BASE_COMMIT=$(git log --oneline --reverse HEAD | head -1 | cut -d' ' -f1) + ``` + +2. **Compare spec artifacts against base**: + ```bash + BRANCH=$(git rev-parse --abbrev-ref HEAD) + git diff $BASE_COMMIT..HEAD -- specs/$BRANCH/spec.md specs/$BRANCH/plan.md specs/$BRANCH/tasks.md + ``` + +3. **If diff is empty**: Report "No spec artifact drift detected — artifacts match initial plan." + +4. **If diff is non-empty**: For each changed artifact, show a structured summary: + - Sections added / removed / changed (by heading) + - Prompt: "Update [artifact] to reflect implementation changes? (yes/skip)" + - On **yes**: Assist developer in editing the artifact (note: analyze is otherwise read-only, + but user-approved drift corrections are the exception) + - On **skip**: Continue to next artifact + +5. **Unresolved task check**: Scan `tasks.md` for unchecked items (`- [ ]`): + ```bash + grep -n "^- \[ \]" specs/$BRANCH/tasks.md + ``` + Cross-reference unchecked tasks with `git log --oneline` for task-related commits. + Flag any tasks with no corresponding commit as: + > "⚠️ Task [TID] has no corresponding commit — possibly unimplemented or not formally + > deferred. Confirm status before marking spec Analyzed." + +### 9. Status Advancement + +After drift detection and analysis report are complete: +- Update `spec.md` `**Status**:` line → `Analyzed` +- Include this status change in the phase-end commit + +### 10. Offer Remediation Ask the user: "Would you like me to suggest concrete remediation edits for the top N issues?" (Do NOT apply them automatically.) +## Phase-End Commit + +1. Run `git status --short` scoped to `specs/$BRANCH/` to check for changes +2. If no changes: report "No changes to commit" and skip +3. If changes exist (analysis report + status advancement): invoke the `conventional-commit` skill with: + - type: `docs` + - scope: `analyze` + - description: `add consistency report for NNN-feature-name` + - footer: issue numbers from spec.md `GitHub Issue` field (e.g., `Refs: #31`) +4. Await developer confirmation before committing (per `conventional-commit` skill workflow) + ## Operating Principles ### Context Efficiency @@ -173,7 +233,7 @@ Ask the user: "Would you like me to suggest concrete remediation edits for the t ### Analysis Guidelines -- **NEVER modify files** (this is read-only analysis) +- **NEVER modify files** (this is read-only analysis, except user-approved drift corrections) - **NEVER hallucinate missing sections** (if absent, report them accurately) - **Prioritize constitution violations** (these are always CRITICAL) - **Use examples over exhaustive rules** (cite specific instances, not generic patterns) diff --git a/.github/agents/speckit.checklist.agent.md b/.github/agents/speckit.checklist.agent.md index b7624e2..d1f8b6e 100644 --- a/.github/agents/speckit.checklist.agent.md +++ b/.github/agents/speckit.checklist.agent.md @@ -293,3 +293,15 @@ Sample items: - Correct: Validation of requirement quality - Wrong: "Does it do X?" - Correct: "Is X clearly specified?" + + +## Phase-End Commit + +1. Run `git status --short` scoped to `specs/$BRANCH/` to check for changes +2. If no changes: report "No changes to commit" and skip +3. If changes exist: invoke the `conventional-commit` skill with: + - type: `docs` + - scope: `checklist` + - description: `add [domain] checklist for NNN-feature-name` + - footer: issue numbers from spec.md `GitHub Issue` field (e.g., `Refs: #31`) +4. Await developer confirmation before committing (per `conventional-commit` skill workflow) diff --git a/.github/agents/speckit.clarify.agent.md b/.github/agents/speckit.clarify.agent.md index 657439f..599702a 100644 --- a/.github/agents/speckit.clarify.agent.md +++ b/.github/agents/speckit.clarify.agent.md @@ -22,7 +22,14 @@ Note: This clarification workflow is expected to run (and be completed) BEFORE i Execution steps: -1. Run `.specify/scripts/bash/check-prerequisites.sh --json --paths-only` from repo root **once** (combined `--json --paths-only` mode / `-Json -PathsOnly`). Parse minimal JSON payload fields: +1. **Context Loading** (run at startup before any output): + 1. Read `.specify/memory/constitution.md` + 2. Read `.specify/memory/stack.md` — if absent, warn: "⚠️ stack.md missing — run `/speckit.specify` first to initialize stack context" + 3. Read current spec's `spec.md` + 4. Read any existing phase artifacts (`plan.md`) if present + 5. Output one-line summary: `Loaded: [spec name] | Status: [status] | Stack: [packaging tool]` + +2. Run `.specify/scripts/bash/check-prerequisites.sh --json --paths-only` from repo root **once** (combined `--json --paths-only` mode / `-Json -PathsOnly`). Parse minimal JSON payload fields: - `FEATURE_DIR` - `FEATURE_SPEC` - (Optionally capture `IMPL_PLAN`, `TASKS` for future chained flows.) @@ -168,6 +175,25 @@ Execution steps: - If any Outstanding or Deferred remain, recommend whether to proceed to `/speckit.plan` or run `/speckit.clarify` again later post-plan. - Suggested next command. +9. **Paradigm Shift Check**: If any clarification answers reveal changes to core architecture + (project type, ORM, packaging tool, authentication model, test framework), surface this before + the phase-end commit: + > "⚠️ Clarification answers indicate a potential stack or architecture change. `.specify/memory/stack.md` + > and/or `.specify/templates/stack-template.md` may need updating. Flag this before proceeding? (yes/skip)" + - On **yes**: note the items in the spec's Assumptions section and add a TODO for the developer + - On **skip**: continue to phase-end commit + +10. **Phase-End Commit**: + + 1. Run `git status --short` scoped to `specs/$BRANCH/` to check for changes + 2. If no changes: report "No changes to commit" and skip + 3. If changes exist: invoke the `conventional-commit` skill with: + - type: `docs` + - scope: `clarify` + - description: `update NNN-feature-name spec with clarifications` + - footer: issue numbers from spec.md `GitHub Issue` field (e.g., `Refs: #31`) + 4. Await developer confirmation before committing (per `conventional-commit` skill workflow) + 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. diff --git a/.github/agents/speckit.implement.agent.md b/.github/agents/speckit.implement.agent.md index c556691..1730112 100644 --- a/.github/agents/speckit.implement.agent.md +++ b/.github/agents/speckit.implement.agent.md @@ -12,7 +12,19 @@ You **MUST** consider the user input before proceeding (if not empty). ## Outline -1. Run `.specify/scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. 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"). +1. **Context Loading** (run at startup before any output): + 1. Read `.specify/memory/constitution.md` + 2. Read `.specify/memory/stack.md` — if absent, warn: "⚠️ stack.md missing — regression test commands will fall back to defaults; run `/speckit.specify` to initialize stack context" + 3. If stack.md is present, extract for use throughout implementation: + - `regression_tests.lint_cmd` → use for post-phase lint validation + - `regression_tests.test_cmd` → use for post-phase test validation + - `regression_tests.e2e_cmd` + `regression_tests.e2e_requires` → include only if UI changes + - `packaging.install_cmd` → use for dependency install instructions in setup tasks + 4. Read current spec's `spec.md`, `plan.md`, `tasks.md` + 5. Update spec.md `**Status**:` line → `In Progress` + 6. Output one-line summary: `Loaded: [spec name] | Status: In Progress | Stack: [packaging tool]` + +2. Run `.specify/scripts/bash/check-prerequisites.sh --json --require-tasks --include-tasks` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. 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. **Check checklists status** (if FEATURE_DIR/checklists/ exists): - Scan all checklist files in the checklists/ directory @@ -109,6 +121,7 @@ You **MUST** consider the user input before proceeding (if not empty). - **Follow TDD approach**: Execute test tasks before their corresponding implementation tasks - **File-based coordination**: Tasks affecting the same files must run sequentially - **Validation checkpoints**: Verify each phase completion before proceeding + - **Context map before each task group**: Before editing any files in a task group, invoke the `context-map` skill to identify all files to modify, their dependencies, related tests, and risk areas for that group. Use the output to guide which files to open and in what order. 7. Implementation execution rules: - **Setup first**: Initialize project structure, dependencies, configuration @@ -168,4 +181,39 @@ You **MUST** consider the user input before proceeding (if not empty). - If a central doc does not exist yet and is needed, create it - If no central docs need updating (e.g. the spec was a pure refactor with no public-facing changes), skip this step and note that in the completion summary +11. **Status Advancement & PR Creation**: + + After all tasks are complete and central docs are updated: + + a. Update spec.md `**Status**:` line → `Implemented` + + b. Invoke the `github-issues` skill to create a Pull Request: + - PR title: `feat(NNN): implement NNN-feature-name` + - PR body: Include `Closes #N` for every issue number in the spec.md `GitHub Issue` field + - PR description: Brief summary of what was implemented, linking to the spec + - Example body: + ``` + Implements [NNN-feature-name](specs/NNN-feature-name/spec.md). + + Closes #N + Closes #M + ``` + + c. For each linked issue, post the PR URL as a comment: + - Comment: "🚀 PR created: [PR title](PR URL) — implementation complete" + +12. **Phase-End Commit**: + + 1. Run `git status --short` scoped to `specs/$BRANCH/` to check for changes + 2. If no changes to spec artifacts: report "No spec changes to commit" and skip + 3. If changes exist (status update, tasks marked complete): invoke the `conventional-commit` + skill with: + - type: `docs` + - scope: `implement` + - description: `mark NNN-feature-name implemented` + - footer: issue numbers (e.g., `Closes: #31`) + 4. Await developer confirmation before committing (per `conventional-commit` skill workflow) + 5. Note: Task-level commits (feat/fix/chore for each implementation task group) are separate + from this spec-level commit — each task group should already have its own commit + 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. diff --git a/.github/agents/speckit.plan.agent.md b/.github/agents/speckit.plan.agent.md index 0ffb929..2be9638 100644 --- a/.github/agents/speckit.plan.agent.md +++ b/.github/agents/speckit.plan.agent.md @@ -20,7 +20,20 @@ You **MUST** consider the user input before proceeding (if not empty). ## Outline -1. **Setup**: Run `.specify/scripts/bash/setup-plan.sh --json` from repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH. 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"). +1. **Context Loading** (run at startup before any output): + 1. Read `.specify/memory/constitution.md` + 2. Read `.specify/memory/stack.md` — if absent, warn: "⚠️ stack.md missing — run `/speckit.specify` first to initialize stack context" + 3. If stack.md is present, surface tech-specific notes: + - If `testing.backend_framework = vitest`: reference Vitest patterns in the plan (e.g., `vi.mock()`, `describe/it` blocks) + - If `database.orm = prisma`: note migration steps (`prisma migrate dev`) and client import conventions + - If `packaging.tool = pnpm`: use pnpm workspace commands for dependency install + - Apply similar tech-specific guidance for other detected stack values + 4. Read current spec's `spec.md` + 5. Read existing `plan.md` if present (for incremental updates) + 6. Output one-line summary: `Loaded: [spec name] | Status: [status] | Stack: [packaging tool]` + 7. Invoke the `context-map` skill to produce a map of all files relevant to this feature before generating any design artifacts — use the resulting file list to ensure plan.md references accurate paths and dependencies. + +2. **Setup**: Run `.specify/scripts/bash/setup-plan.sh --json` from repo root and parse JSON for FEATURE_SPEC, IMPL_PLAN, SPECS_DIR, BRANCH. 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 context**: Read FEATURE_SPEC and `.specify/memory/constitution.md`. Load IMPL_PLAN template (already copied). @@ -88,3 +101,14 @@ You **MUST** consider the user input before proceeding (if not empty). - Use absolute paths - ERROR on gate failures or unresolved clarifications + +## Phase-End Commit + +1. Run `git status --short` scoped to `specs/$BRANCH/` to check for changes +2. If no changes: report "No changes to commit" and skip +3. If changes exist: invoke the `conventional-commit` skill with: + - type: `docs` + - scope: `plan` + - description: `add implementation plan for NNN-feature-name` + - footer: issue numbers from spec.md `GitHub Issue` field (e.g., `Refs: #31`) +4. Await developer confirmation before committing (per `conventional-commit` skill workflow) diff --git a/.github/agents/speckit.specify.agent.md b/.github/agents/speckit.specify.agent.md index ae62bc6..91644eb 100644 --- a/.github/agents/speckit.specify.agent.md +++ b/.github/agents/speckit.specify.agent.md @@ -20,6 +20,10 @@ You **MUST** consider the user input before proceeding (if not empty). ## Outline +> ⚠️ **New session recommended**: This agent should run in a new Copilot CLI session. If you +> have prior conversation history from a different task, start a new session before proceeding to +> ensure clean context loading. (Non-blocking — continue if already in a fresh session.) + 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 `$ARGUMENTS` appears literally below. Do not ask the user to repeat it unless they provided an empty command. Given that feature description, do this: @@ -68,7 +72,56 @@ Given that feature description, do this: - The JSON output will contain BRANCH_NAME and SPEC_FILE paths - 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") -3. Load `.specify/templates/spec-template.md` to understand required sections. +3. **GitHub Issue Triage & Backlog Refresh**: + + a. **Check GitHub MCP connectivity**: Attempt to list open GitHub issues via the `github-issues` + skill or MCP tool. This is a connectivity probe only. + + b. **If MCP is reachable**: + - Invoke the `issue-triage` agent to fetch current open issues and allow developer to select + related issue(s) for this feature + - Overwrite `.specify/memory/issues-backlog.md` with a current snapshot of open issues, + appending a `last_refreshed: YYYY-MM-DDTHH:MM:SSZ` timestamp at the top + - Write all selected issue numbers to the `spec.md` `GitHub Issue` front-matter field as a + comma-separated list: `#N, #N` + + c. **If MCP is unreachable**: + - Warn: "⚠️ GitHub MCP not reachable — cannot auto-link issues. Enter issue number(s) + manually (e.g., `#31, #32`) or leave blank." + - Read existing `.specify/memory/issues-backlog.md` if present and note its age + (`last_refreshed` timestamp) + - Accept any manually entered issue numbers for the `GitHub Issue` field + +4. **Stack Check**: + + a. Check if `.specify/memory/stack.md` exists: + - **If absent**: Read `.specify/templates/stack-template.md`; present two options: + 1. **"Auto-detect from repo"** — inspect lock files (`pnpm-lock.yaml`, `yarn.lock`, + `package-lock.json`), `package.json` scripts, config files (`eslint.config.*`, + `tsconfig.json`, `docker-compose.yml`, `prisma/schema.prisma`) to populate all + auto-detectable fields; prompt developer only for non-detectable fields + 2. **"Manual entry"** — prompt developer for each required field in section order + - Write the populated `stack.md` using template field order with `schema_version: "1.0"` + - **If present but `schema_version` < template version**: Prompt only for missing fields + (never overwrite existing fields); append missing fields and update `schema_version` + - **If present and up-to-date**: No action needed — proceed + +5. **Paradigm Shift Detection**: Before writing spec.md, scan the feature description and any + collected spec content for signals of stack or workflow changes: + - New primary language, framework, or runtime + - Authentication model change (e.g., sessions → JWT) + - New test framework or ORM + - New deployment/infrastructure strategy + - New integration pattern (REST → GraphQL, polling → webhooks) + + If any signals are detected, present this prompt before proceeding: + > "⚠️ This spec may require updating `.specify/templates/stack-template.md` or + > `.specify/memory/constitution.md` — review before proceeding. Continue? (yes/no)" + + On **NO**: halt and instruct developer to run `/speckit.constitution` first. + On **YES**: proceed with spec writing, noting the paradigm shift in the spec's Assumptions section. + +6. Load `.specify/templates/spec-template.md` to understand required sections. 4. Follow this execution flow: @@ -96,9 +149,9 @@ Given that feature description, do this: 7. Identify Key Entities (if data involved) 8. Return: SUCCESS (spec ready for planning) -5. Write the specification to SPEC_FILE using the template structure, replacing placeholders with concrete details derived from the feature description (arguments) while preserving section order and headings. +7. Write the specification to SPEC_FILE using the template structure, replacing placeholders with concrete details derived from the feature description (arguments) while preserving section order and headings. -6. **Specification Quality Validation**: After writing the initial spec, validate it against quality criteria: +8. **Specification Quality Validation**: After writing the initial spec, validate it against quality criteria: a. **Create Spec Quality Checklist**: Generate a checklist file at `FEATURE_DIR/checklists/requirements.md` using the checklist template structure with these validation items: @@ -190,7 +243,26 @@ Given that feature description, do this: 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`). +9. **Issue Traceability Comments**: After spec.md is written and committed, for each issue number + in the `GitHub Issue` field: + - Invoke the `github-issues` skill to post a traceability comment on the issue containing: + - Branch name + - Spec title + - Link to `spec.md` in the repository + - Example comment: "📋 Spec initialized: [NNN-feature-name](link/to/spec.md) on branch `NNN-feature-name`" + +10. **Phase-End Commit**: + + 1. Run `git status --short` scoped to `specs/$BRANCH/` to check for changes + 2. If no changes: report "No changes to commit" and skip + 3. If changes exist: invoke the `conventional-commit` skill with: + - type: `docs` + - scope: `spec` + - description: `initialize NNN-feature-name spec` + - footer: issue numbers (e.g., `Refs: #31`) + 4. Await developer confirmation before committing (per `conventional-commit` skill workflow) + +11. 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. diff --git a/.github/agents/speckit.tasks.agent.md b/.github/agents/speckit.tasks.agent.md index e09112b..4fc5851 100644 --- a/.github/agents/speckit.tasks.agent.md +++ b/.github/agents/speckit.tasks.agent.md @@ -21,7 +21,18 @@ You **MUST** consider the user input before proceeding (if not empty). ## Outline -1. **Setup**: Run `.specify/scripts/bash/check-prerequisites.sh --json` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. 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"). +1. **Context Loading** (run at startup before any output): + 1. Read `.specify/memory/constitution.md` + 2. Read `.specify/memory/stack.md` — if absent, warn: "⚠️ stack.md missing — run `/speckit.specify` first to initialize stack context" + 3. If stack.md is present, extract `regression_tests` section values: + - `lint_cmd` → use this exact command in regression test tasks (not hardcoded `pnpm lint`) + - `test_cmd` → use this exact command in regression test tasks (not hardcoded `pnpm test`) + - `e2e_cmd` + `e2e_requires` → include in tasks only if UI changes are in scope + 4. Read current spec's `spec.md` and `plan.md` + 5. Read existing `tasks.md` if present (for incremental regeneration) + 6. Output one-line summary: `Loaded: [spec name] | Status: [status] | Stack: [packaging tool]` + +2. **Setup**: Run `.specify/scripts/bash/check-prerequisites.sh --json` from repo root and parse FEATURE_DIR and AVAILABLE_DOCS list. All paths must be absolute. 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 design documents**: Read from FEATURE_DIR: - **Required**: plan.md (tech stack, libraries, structure), spec.md (user stories with priorities) @@ -135,3 +146,14 @@ Every task MUST strictly follow this format: - Within each story: Tests (if requested) → Models → Services → Endpoints → Integration - Each phase should be a complete, independently testable increment - **Final Phase**: Polish & Cross-Cutting Concerns + +## Phase-End Commit + +1. Run `git status --short` scoped to `specs/$BRANCH/` to check for changes +2. If no changes: report "No changes to commit" and skip +3. If changes exist: invoke the `conventional-commit` skill with: + - type: `docs` + - scope: `tasks` + - description: `generate task breakdown for NNN-feature-name` + - footer: issue numbers from spec.md `GitHub Issue` field (e.g., `Refs: #31`) +4. Await developer confirmation before committing (per `conventional-commit` skill workflow) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..f2f24a9 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,134 @@ +# Copilot Instructions for ordrctrl + +ordrctrl is a unified productivity aggregator that consolidates tasks, reminders, calendar events, and flagged emails from Gmail, Microsoft Tasks, and Apple Calendar into a single chronological feed. It's a pnpm monorepo with a Fastify 4 REST API backend and a Vite 5 + React 18 SPA frontend. + +--- + +## Commands + +### Root (runs both workspaces) +```bash +pnpm dev # start backend + frontend concurrently +pnpm build # build both +pnpm lint # lint both +pnpm lint:fix # auto-fix both +pnpm test # backend unit tests +pnpm test:e2e # frontend Playwright e2e +``` + +### Backend (`cd backend`) +```bash +pnpm dev # tsx watch src/server.ts +pnpm test # vitest run (all) +pnpm test:contract # vitest run tests/contract (API contract tests only) +pnpm test:watch # vitest (watch mode) +pnpm lint # eslint src +pnpm prisma:migrate # prisma migrate dev +pnpm prisma:generate # prisma generate +``` + +### Frontend (`cd frontend`) +```bash +pnpm dev # vite +pnpm test # vitest run +pnpm test:watch # vitest +pnpm test:e2e # playwright test (requires both servers running) +pnpm lint # eslint src +pnpm dev:android # vite --mode android --host +pnpm dev:ios # vite --mode ios --host +``` + +### Run a single test file +```bash +# Backend +cd backend && pnpm vitest run tests/unit/feed.service.test.ts + +# Frontend e2e +cd frontend && pnpm playwright test tests/e2e/feed.spec.ts +``` + +### Infrastructure +```bash +docker compose up -d # start PostgreSQL 16 + Redis 7 (required for local dev) +``` + +--- + +## Architecture + +``` +ordrctrl/ +├── backend/ # Fastify 4 REST API (port 4000) +│ ├── src/ +│ │ ├── api/ # Route handlers (one file per domain) +│ │ ├── services/ # Business logic +│ │ ├── integrations/ # Third-party adapters (Gmail, Microsoft, Apple) +│ │ │ └── _adapter/ # IntegrationAdapter interface + types +│ │ ├── lib/ # db.ts (Prisma), encryption.ts, redis.ts +│ │ ├── jobs/ # BullMQ queue workers (15-min sync) +│ │ ├── app.ts # Fastify app setup + route registration +│ │ └── server.ts # Entry point + env validation +│ └── prisma/ +│ ├── schema.prisma +│ └── migrations/ +└── frontend/ # Vite 5 + React 18 SPA (port 3000) + └── src/ + ├── app/ # Route pages (app/{feature}/page.tsx) + ├── components/ # UI components (components/{domain}/Name.tsx) + ├── hooks/ # State + actions (useFeatureName.ts) + ├── services/ # API client wrappers (feature.service.ts) + ├── plugins/ # Capacitor/Tauri platform abstractions + └── utils/ +``` + +Frontend communicates with backend via REST + session cookies (`credentials: 'include'`). Auth is session-based (Fastify session + Redis), not JWT. + +--- + +## Key Conventions + +### Backend: Routes +Each `backend/src/api/*.routes.ts` exports a `register*Routes(app: FastifyInstance)` function. All route files are registered in `app.ts`. Validate input with Zod at the route entry point. Auth guard: check `request.session.userId`. Errors: `reply.status(code).send({ error, message })`. + +### Backend: Integrations +Every integration implements the `IntegrationAdapter` interface (`backend/src/integrations/_adapter/types.ts`). New integrations must implement `connect`, `disconnect`, `sync`, `refreshToken`, and `getAuthorizationUrl`. The `sync()` method returns `NormalizedItem[]`. Core application code must not be modified to add a new integration — adapters are self-contained. + +### Backend: Database +- ORM: Prisma — import client from `backend/src/lib/db.ts` (`import { prisma } from '../lib/db'`) +- Never edit existing migrations — always `pnpm prisma migrate dev --name ` followed by `pnpm prisma generate` +- OAuth tokens are stored encrypted (AES-256-GCM) via `backend/src/lib/encryption.ts` — never store or log plaintext tokens +- `SyncCacheItem.rawPayload` must never appear in API responses (PII) + +### Frontend: State management +No Redux/Zustand/Context. State lives in custom hooks (`hooks/useFeatureName.ts`). Hooks own `useState`, call service functions, and return both data and action callbacks. Pages/components call hooks and pass data down via props. + +### Frontend: Services +`services/*.service.ts` are pure API wrappers with no state. They use the helpers in `services/api-client.ts` (`apiGet`, `apiPost`, `apiPatch`, `apiDelete`) which set `credentials: 'include'` and throw `ApiError` on non-2xx responses. + +### Frontend: Styling +Tailwind utility classes in `className` only. Do not use `@apply` or `@layer components` in CSS. Do not use inline `style={{}}` props except for runtime-dynamic values that Tailwind cannot express (e.g., a per-record hex color). + +### Branching +Branches follow `NNN-short-name` format where `NNN` is the zero-padded spec number (e.g., `023-todoist-integration`). Each spec has design documents in `specs/NNN-*/`. + +### Commit messages +Conventional Commits: `(): `. Types: `feat`, `fix`, `chore`, `docs`, `refactor`, `test`, `perf`. + +### Testing expectations +| Change | Tests required | +|--------|----------------| +| New API endpoint | Contract test in `backend/tests/contract/` | +| New service method | Unit test in `backend/tests/unit/` | +| New integration adapter | Unit tests for `sync()` normalization | +| New UI flow | Playwright e2e in `frontend/tests/e2e/` | + +### Environment variables +New env vars must be added to `backend/.env.example` (placeholder value, never a real secret), documented in `docs/development.md`, and validated at startup in `backend/src/server.ts`. Never commit `.env` or `.env.device.local` files. + +--- + +## Spec-Kit Workflow + +> Full constitution and workflow principles: [`.specify/memory/constitution.md`](.specify/memory/constitution.md) + +All features follow the spec-kit lifecycle: `/speckit.specify` → `/speckit.plan` → `/speckit.tasks` → `/speckit.implement` → `/speckit.analyze`. Agents and workflow steps are in `.github/agents/`. diff --git a/.github/hooks/session-logger/log-prompt.sh b/.github/hooks/session-logger/log-prompt.sh index 5875bfb..1b83c5f 100644 --- a/.github/hooks/session-logger/log-prompt.sh +++ b/.github/hooks/session-logger/log-prompt.sh @@ -1,25 +1,35 @@ #!/bin/bash -# Log session end event +# Log userPromptSubmitted event set -euo pipefail +# .gitignore guard — session logging requires logs/ to be ignored +if ! grep -q '^logs/' .gitignore 2>/dev/null; then + echo "⚠️ speckit-setup: add 'logs/' to .gitignore before session logging is active" + exit 0 +fi +mkdir -p logs/copilot + # Skip if logging disabled if [[ "${SKIP_LOGGING:-}" == "true" ]]; then exit 0 fi -# Read input from Copilot -INPUT=$(cat) - -# Create logs directory if it doesn't exist -mkdir -p logs/copilot +# Capture prompt text from first argument (passed by hook caller) +PROMPT="${1:-}" -# Extract timestamp +# Extract timestamp and branch TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") -# Log session end -echo "{\"timestamp\":\"$TIMESTAMP\",\"event\":\"sessionEnd\"}" >> logs/copilot/session.log +# Log prompt event as JSON +jq -Rn \ + --arg event "userPromptSubmitted" \ + --arg prompt "$PROMPT" \ + --arg timestamp "$TIMESTAMP" \ + --arg branch "$BRANCH" \ + '{"event":$event,"prompt":$prompt,"timestamp":$timestamp,"branch":$branch}' \ + >> logs/copilot/session.log -echo "📝 Session end logged" exit 0 \ No newline at end of file diff --git a/.github/hooks/session-logger/log-session-end.sh b/.github/hooks/session-logger/log-session-end.sh index 5875bfb..9b827e3 100644 --- a/.github/hooks/session-logger/log-session-end.sh +++ b/.github/hooks/session-logger/log-session-end.sh @@ -4,6 +4,13 @@ set -euo pipefail +# .gitignore guard — session logging requires logs/ to be ignored +if ! grep -q '^logs/' .gitignore 2>/dev/null; then + echo "⚠️ speckit-setup: add 'logs/' to .gitignore before session logging is active" + exit 0 +fi +mkdir -p logs/copilot + # Skip if logging disabled if [[ "${SKIP_LOGGING:-}" == "true" ]]; then exit 0 @@ -12,14 +19,28 @@ fi # Read input from Copilot INPUT=$(cat) -# Create logs directory if it doesn't exist -mkdir -p logs/copilot - -# Extract timestamp +# Extract timestamp and branch TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") + +# Build base log entry +LOG_ENTRY=$(jq -Rn \ + --arg timestamp "$TIMESTAMP" \ + --arg event "sessionEnd" \ + --arg branch "$BRANCH" \ + '{"timestamp":$timestamp,"event":$event,"branch":$branch}') + +# Speckit branch detection — check for uncommitted spec artifacts +if [[ "$BRANCH" =~ ^[0-9]{3,}- ]]; then + UNCOMMITTED=$(git status --short "specs/$BRANCH/" 2>/dev/null | grep -v '^$' || true) + if [[ -n "$UNCOMMITTED" ]]; then + WARNING="⚠️ uncommitted spec artifacts detected in specs/$BRANCH/ — consider running phase-end commit" + LOG_ENTRY=$(echo "$LOG_ENTRY" | jq --arg w "$WARNING" '. + {"warning":$w}') + echo "$WARNING" + fi +fi -# Log session end -echo "{\"timestamp\":\"$TIMESTAMP\",\"event\":\"sessionEnd\"}" >> logs/copilot/session.log +echo "$LOG_ENTRY" >> logs/copilot/session.log echo "📝 Session end logged" exit 0 \ No newline at end of file diff --git a/.github/hooks/session-logger/log-session-start.sh b/.github/hooks/session-logger/log-session-start.sh index 11c4109..3c377dd 100644 --- a/.github/hooks/session-logger/log-session-start.sh +++ b/.github/hooks/session-logger/log-session-start.sh @@ -4,6 +4,13 @@ set -euo pipefail +# .gitignore guard — session logging requires logs/ to be ignored +if ! grep -q '^logs/' .gitignore 2>/dev/null; then + echo "⚠️ speckit-setup: add 'logs/' to .gitignore before session logging is active" + exit 0 +fi +mkdir -p logs/copilot + # Skip if logging disabled if [[ "${SKIP_LOGGING:-}" == "true" ]]; then exit 0 @@ -12,15 +19,36 @@ fi # Read input from Copilot INPUT=$(cat) -# Create logs directory if it doesn't exist -mkdir -p logs/copilot - # Extract timestamp and session info TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") CWD=$(pwd) +# Speckit branch detection — check if we're on a NNN-* feature branch +BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") +SPECKIT_CONTEXT=null + +if [[ "$BRANCH" =~ ^[0-9]{3,}- ]]; then + SPEC_FILE="specs/$BRANCH/spec.md" + if [[ -f "$SPEC_FILE" ]]; then + # Extract Status line from spec.md + SPEC_STATUS=$(grep -E '^\*\*Status\*\*:' "$SPEC_FILE" 2>/dev/null | sed 's/\*\*Status\*\*:[[:space:]]*//' | tr -d '\r' | head -1 || echo "unknown") + SPEC_NAME=$(grep -E '^# ' "$SPEC_FILE" 2>/dev/null | sed 's/^# //' | head -1 || echo "unknown") + SPECKIT_CONTEXT=$(jq -Rn \ + --arg spec "$SPEC_NAME" \ + --arg status "$SPEC_STATUS" \ + --arg last_phase "$(echo "$SPEC_STATUS" | tr '[:upper:]' '[:lower:]')" \ + '{"spec":$spec,"status":$status,"last_phase":$last_phase}') + fi +fi + # Log session start (use jq for proper JSON encoding) -jq -Rn --arg timestamp "$TIMESTAMP" --arg cwd "$CWD" '{"timestamp":$timestamp,"event":"sessionStart","cwd":$cwd}' >> logs/copilot/session.log +jq -Rn \ + --arg timestamp "$TIMESTAMP" \ + --arg cwd "$CWD" \ + --arg branch "$BRANCH" \ + --argjson speckit_context "$SPECKIT_CONTEXT" \ + '{"timestamp":$timestamp,"event":"sessionStart","cwd":$cwd,"branch":$branch,"speckit_context":$speckit_context}' \ + >> logs/copilot/session.log echo "📝 Session logged" exit 0 \ No newline at end of file diff --git a/.github/prompts/issue-triage.prompt.md b/.github/prompts/issue-triage.prompt.md new file mode 100644 index 0000000..ec8b00a --- /dev/null +++ b/.github/prompts/issue-triage.prompt.md @@ -0,0 +1,3 @@ +--- +agent: issue-triage +--- diff --git a/.github/skills/conventional-commit/SKILL.md b/.github/skills/conventional-commit/SKILL.md index 3465092..bd929af 100644 --- a/.github/skills/conventional-commit/SKILL.md +++ b/.github/skills/conventional-commit/SKILL.md @@ -17,13 +17,16 @@ description: 'Prompt and workflow for generating conventional commit messages us 2. Run `git diff` or `git diff --cached` to inspect changes. 3. Stage your changes with `git add `. 4. Construct your commit message using the following XML structure. -5. After generating your commit message, Copilot will automatically run the following command in your integrated terminal (no confirmation needed): +5. Present a pre-commit summary before committing: -```bash -git commit -m "type(scope): description" -``` + a. Run `git status --short` and list the staged files + b. Show the generated commit message + c. Ask: **"Proceed with this commit? (yes/no)"** + d. Wait for the developer's response: + - On **YES**: run `git commit -m "type(scope): description"` + - On **NO**: do **not** commit; inform the developer that the phase is not marked complete and no changes have been committed -6. Just execute this prompt and Copilot will handle the commit for you in the terminal. +6. Only after receiving explicit confirmation will the commit be executed. ### Commit Message Structure diff --git a/.gitignore b/.gitignore index b9c9de1..8b10607 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,7 @@ out/ .env.production # Logs +logs/ *.log npm-debug.log* yarn-debug.log* diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md index be8a2ed..9678ecc 100644 --- a/.specify/memory/constitution.md +++ b/.specify/memory/constitution.md @@ -1,18 +1,26 @@ @@ -98,6 +106,41 @@ deferred until there is sufficient evidence to make them well. **Rationale**: Premature architectural decisions create lock-in and debt. ordrctrl is early-stage; optionality is more valuable than consistency with a decision that may prove wrong. +### VI. Spec-Kit Workflow (NON-NEGOTIABLE) + +All features MUST follow the spec-kit lifecycle in order: +**Specify → Plan → Tasks → Implement → Analyze** + +- `/speckit.specify`: Draft spec.md, link GitHub issue, commit `docs(spec): initialize NNN-feature-name` +- `/speckit.plan`: Generate plan.md + design artifacts, commit `docs(plan): add plan for NNN-feature-name` +- `/speckit.tasks`: Generate tasks.md, commit `docs(tasks): generate tasks for NNN-feature-name` +- `/speckit.implement`: Execute tasks, run regression tests after each phase, update spec status, + commit per phase, push branch, open PR referencing linked GitHub issues +- `/speckit.analyze`: Cross-artifact consistency check, update spec status, commit report + +The `spec.md` **Status** field MUST be updated at each phase gate: +`Draft → Planned → Tasked → In Progress → Implemented → Analyzed` + +GitHub issues MUST be: +1. Linked in `spec.md` header as `**GitHub Issue**: #N` +2. Tracked in `.specify/memory/issues-backlog.md` +3. Referenced in the PR body with `Closes #N` syntax + +Regression tests MUST be run after every implementation phase. A phase MUST NOT be committed if +tests fail. Stack-specific test commands are defined in `.specify/memory/stack.md`. + +Every speckit phase MUST end with a `conventional-commit` skill invocation. Phase commits follow +the format `docs(): (#)` where `` is the speckit phase name +(e.g., `spec`, `plan`, `tasks`, `implement`, `analyze`). + +Project tooling (packaging, linting, test library, version constraints) MUST be documented in +`.specify/memory/stack.md` and kept current. All spec-kit agents MUST read `stack.md` before +generating technical plans or tasks. + +**Rationale**: Spec traceability from GitHub issue to merged PR is the audit trail for every +feature. Without enforced phase commits, status tracking, and test gates, the workflow degrades +silently. Stack documentation prevents agents from hallucinating commands or tool names. + ## Architecture & Platform ordrctrl is a web and mobile application providing a consolidated view of tasks, emails, and @@ -128,7 +171,13 @@ TODO(TECH_STACK): Finalize language, framework, and database choices after proof - Security-sensitive changes (token handling, auth flows, data retention) MUST receive explicit review attention before approval. - Commit messages SHOULD follow Conventional Commits format - (`feat:`, `fix:`, `docs:`, `chore:`, etc.). + (`feat:`, `fix:`, `docs:`, `chore:`, etc.) — see `.specify/memory/stack.md` for spec-kit + phase-specific commit message formats. +- **Regression tests MUST run after every implementation phase** before committing. Failures + block the commit. +- Project tooling and commands are documented in `.specify/memory/stack.md`. This file is the + authoritative reference for packaging tool, version constraints, linting, and test commands. +- GitHub issues MUST be tracked in `.specify/memory/issues-backlog.md` and closed via PRs. ## Governance @@ -149,4 +198,23 @@ conflicts with the constitution, the constitution wins or must be formally amend **Compliance**: All pull request reviews MUST verify compliance with this constitution. Complexity violations must be justified; unjustified complexity MUST block merge. -**Version**: 1.0.0 | **Ratified**: 2026-03-05 | **Last Amended**: 2026-03-05 +### Paradigm Shift Triggers + +The constitution MUST be reviewed and potentially amended (run `/speckit.constitution`) when +any of the following occur: + +- A new primary programming language, runtime, or framework is adopted +- The authentication/authorization model changes fundamentally (e.g., sessions → JWT) +- The deployment or infrastructure strategy changes significantly +- A new integration pattern is mandated (e.g., REST → GraphQL, polling → webhooks) +- The test strategy changes (e.g., new test framework, adopting strict TDD) +- The packaging tool changes (e.g., npm → pnpm, pip → uv) +- A new platform target is added (e.g., desktop app, CLI, browser extension) +- The data persistence layer changes (e.g., new ORM, new database engine) + +When a paradigm shift occurs: +1. Run `/speckit.constitution` to amend the constitution **before** creating any new spec +2. Update `.specify/memory/stack.md` with new tooling information +3. Propagate changes to affected templates and agent files per the amendment procedure + +**Version**: 1.1.0 | **Ratified**: 2026-03-05 | **Last Amended**: 2026-03-22 diff --git a/.specify/memory/issues-backlog.md b/.specify/memory/issues-backlog.md new file mode 100644 index 0000000..91d0a9a --- /dev/null +++ b/.specify/memory/issues-backlog.md @@ -0,0 +1,66 @@ +# Issues Backlog: Upcoming Feature Specs + +**Purpose**: Track GitHub issues that represent upcoming or in-progress feature work. +**Updated By**: `/speckit.specify` (on issue linkage), manually, or via `gh issue list` + +This file maps GitHub issues to spec branches so the spec-kit workflow stays aligned +with the project's GitHub backlog. It is the single source of truth for "what's next." + +--- + +## How to Use + +1. **During `/speckit.specify`**: When a GitHub issue is provided, the agent adds a row here + linking the issue to the created branch and updates the spec.md header. + +2. **Reviewing the backlog**: Run `gh issue list --repo jwill824/ordrctrl --state open` to + sync open issues with this file. + +3. **Closing issues**: When a PR is merged for a spec, update the Status to "Merged" and + move the row to the Archived section below. + +4. **Issue format in branch names**: Branches are named `NNN-short-name` and the issue + reference lives in `spec.md` header as `**GitHub Issue**: #N`. + PRs MUST close the linked issue with `Closes #N` in the PR body. + +--- + +## Active Issues → Specs + +| Issue # | Title | Priority | Spec Branch | Status | GitHub URL | +|---------|-------|----------|-------------|--------|------------| +| #31 | Update our spec-kit workflow | High | `021-speckit-workflow` | In Progress | https://github.com/jwill824/ordrctrl/issues/31 | + +--- + +## Unassigned Open Issues + +> Issues that have not yet been converted to a spec branch. +> Review periodically and promote to Active when work begins. + +Run to refresh: +```bash +gh issue list --repo jwill824/ordrctrl --state open --json number,title,labels,createdAt +``` + +--- + +## Speckit Refinement Ideas + +> Logged design improvements for future speckit specs. Promote to a GitHub issue when ready to implement. + +| ID | Area | Description | Logged | +|----|------|-------------|--------| +| R1 | Session lifecycle (US8/FR-034) | New-session boundary should apply per-spec only (at `speckit.specify`), not per phase. Subsequent phases (`plan`, `tasks`, `implement`, `analyze`) should rely on FR-035 context loading from files instead of a session boundary reminder. Consider a formal "session checkpoint" concept: capture a structured summary at phase-end so resuming agents load a clean artifact snapshot rather than raw conversation history — functionally equivalent to a fresh session without manual overhead. | 2026-03-24 | +| R2 | Token efficiency — constitution digest | `.specify/memory/constitution.md` is loaded in full at the start of every phase. Create a compact `constitution-digest.md` containing only the key MUST rules (one line each), and have agents load the digest instead. Full constitution only loaded on explicit constitution-check tasks. Estimated savings: medium. | 2026-03-24 | +| R3 | Token efficiency — context-map frequency | FR-027 mandates `context-map` skill before each task group in `speckit.implement`. For large specs (40+ tasks) this causes redundant file discovery across the session. Change to: run `context-map` once per phase (not per task group), or cache the output in a temp session file and skip re-runs if the file list hasn't changed since the last call. Estimated savings: high. | 2026-03-24 | +| R4 | Token efficiency — agent instruction lazy-loading | Each `.agent.md` file loads its full instruction set on every invocation. Split into a lean header (goal + operating constraints + quick-path steps) and an `## Extended Instructions` section loaded only when the agent explicitly needs it (e.g., drift detection, constitution alignment). Reduces cold-start token cost for simple invocations. Estimated savings: medium. | 2026-03-24 | +| R5 | Token efficiency — drift detection summary | `git diff BASE..HEAD` in `speckit.analyze` can return thousands of lines fed directly to the model. Replace with a structured diff summary: extract only changed headings/sections (using `grep "^##"` on before/after), not full content. Present a headings-only summary to the model; full diff only retrieved for sections the user confirms need updating. Estimated savings: high. | 2026-03-24 | + +--- + +## Archived (Merged / Closed) + +| Issue # | Title | Spec Branch | PR # | Closed Date | +|---------|-------|-------------|------|-------------| +| - | - | - | - | - | diff --git a/.specify/memory/stack.md b/.specify/memory/stack.md new file mode 100644 index 0000000..b5df08f --- /dev/null +++ b/.specify/memory/stack.md @@ -0,0 +1,127 @@ +# ordrctrl Project Stack + +**Last Updated**: 2026-03-22 +**Updated By**: speckit.constitution (v1.1.0 amendment) + +> This file is the authoritative reference for project tooling and stack constraints. +> All spec-kit agents MUST read this file when generating technical tasks or plans. +> Update this file via `/speckit.constitution` when the stack changes (paradigm shift). + +## Packaging + +- **Tool**: pnpm (workspace monorepo) +- **Workspace file**: `pnpm-workspace.yaml` +- **Lock file**: `pnpm-lock.yaml` +- **Install command**: `pnpm install` +- **Add dependency**: `pnpm add --filter ` (e.g., `--filter backend`) + +## Version Constraints + +- **Node.js**: See `engines` field in root `package.json` or `.nvmrc` +- **pnpm**: See `packageManager` field in root `package.json` +- **TypeScript**: See `tsconfig.json` in each workspace + +## Linting + +- **Tool**: ESLint +- **Config**: `eslint.config.*` per workspace +- **Lint command (root)**: `pnpm lint` +- **Auto-fix (root)**: `pnpm lint:fix` +- **Lint backend only**: `cd backend && pnpm lint` +- **Lint frontend only**: `cd frontend && pnpm lint` + +## Testing + +### Backend + +- **Framework**: Vitest +- **Run all**: `cd backend && pnpm test` +- **Watch mode**: `cd backend && pnpm test:watch` +- **Contract tests only**: `cd backend && pnpm test:contract` +- **Single file**: `cd backend && pnpm vitest run ` +- **Test directories**: + - Contract: `backend/tests/contract/` + - Unit: `backend/tests/unit/` + +### Frontend + +- **Unit**: Vitest — `cd frontend && pnpm test` +- **E2E**: Playwright — `cd frontend && pnpm test:e2e` + - ⚠️ Requires both servers running (`pnpm dev` from root) +- **Single e2e file**: `cd frontend && pnpm playwright test ` + +### Root (Both Workspaces) + +```bash +pnpm test # backend unit + contract tests +pnpm test:e2e # frontend Playwright e2e (requires servers) +``` + +## Regression Test Commands + +Run these after every implementation phase to verify no regressions: + +```bash +# Step 1: Lint check +pnpm lint + +# Step 2: Backend unit + contract tests +pnpm test + +# Step 3: E2E (only if UI changes; requires `docker compose up -d` + `pnpm dev`) +# cd frontend && pnpm test:e2e +``` + +## Build + +- **Build all (root)**: `pnpm build` +- **Backend build**: `cd backend && pnpm build` +- **Frontend build**: `cd frontend && pnpm build` + +## Infrastructure (Local Dev) + +```bash +docker compose up -d # Start PostgreSQL 16 + Redis 7 (required) +pnpm dev # Start backend (port 4000) + frontend (port 3000) concurrently +``` + +## Database + +- **ORM**: Prisma (client imported from `backend/src/lib/db.ts`) +- **Schema**: `backend/prisma/schema.prisma` +- **Migrate**: `cd backend && pnpm prisma:migrate` +- **Generate client**: `cd backend && pnpm prisma:generate` +- ⚠️ Never edit existing migrations — always create new ones + +## Best Practices Reference + +- Constitution: `.specify/memory/constitution.md` +- Copilot Instructions: `.github/copilot-instructions.md` +- Development Guide: `docs/development.md` +- Architecture Reference: `docs/architecture.md` + +## Commit Message Convention + +Format: `(): ` + +| Type | Usage | +|------|-------| +| `feat` | New feature or capability | +| `fix` | Bug fix | +| `chore` | Maintenance, tooling, deps | +| `docs` | Documentation only | +| `refactor` | Code restructure, no behavior change | +| `test` | Test-only changes | +| `perf` | Performance improvement | + +**Spec-Kit Phase Commit Messages**: + +| Phase | Message format | +|-------|---------------| +| `/speckit.specify` | `docs(spec): initialize NNN-feature-name spec` | +| `/speckit.plan` | `docs(plan): add implementation plan for NNN-feature-name` | +| `/speckit.tasks` | `docs(tasks): generate task breakdown for NNN-feature-name` | +| `/speckit.implement` (per phase) | `feat(NNN): implement ` | +| `/speckit.implement` (final) | `feat(NNN): complete NNN-feature-name implementation` | +| `/speckit.analyze` | `docs(analyze): add consistency report for NNN-feature-name` | +| `/speckit.constitution` | `docs: amend constitution to vX.Y.Z ()` | diff --git a/.specify/scripts/bash/bootstrap.sh b/.specify/scripts/bash/bootstrap.sh new file mode 100755 index 0000000..be91cdc --- /dev/null +++ b/.specify/scripts/bash/bootstrap.sh @@ -0,0 +1,203 @@ +#!/bin/bash +# bootstrap.sh — Initialize speckit workflow structure in a target repository +# +# Usage: bash .specify/scripts/bash/bootstrap.sh [TARGET_DIR] +# TARGET_DIR: optional path to target repo (defaults to current directory) +# +# This script creates the full speckit directory structure, copies generic +# speckit files, adds logs/ to .gitignore, and prints next-steps instructions. + +set -euo pipefail + +# --------------------------------------------------------------------------- +# Configuration +# --------------------------------------------------------------------------- + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SOURCE_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +TARGET_DIR="${1:-$(pwd)}" + +# Directories to create +DIRS=( + ".github/agents" + ".github/skills/conventional-commit" + ".github/skills/context-map" + ".github/skills/github-issues" + ".github/hooks/session-logger" + ".specify/memory" + ".specify/templates" + ".specify/scripts/bash" + "specs" +) + +# Generic files to copy (source → destination relative to repo root) +GENERIC_FILES=( + ".github/agents/speckit.specify.agent.md" + ".github/agents/speckit.clarify.agent.md" + ".github/agents/speckit.plan.agent.md" + ".github/agents/speckit.tasks.agent.md" + ".github/agents/speckit.checklist.agent.md" + ".github/agents/speckit.analyze.agent.md" + ".github/agents/speckit.implement.agent.md" + ".github/agents/speckit.constitution.agent.md" + ".github/agents/speckit.taskstoissues.agent.md" + ".github/agents/issue-triage.agent.md" + ".github/hooks/session-logger/log-session-start.sh" + ".github/hooks/session-logger/log-session-end.sh" + ".github/hooks/session-logger/log-prompt.sh" + ".specify/templates/spec-template.md" + ".specify/templates/plan-template.md" + ".specify/templates/tasks-template.md" + ".specify/templates/checklist-template.md" + ".specify/templates/stack-template.md" + ".specify/scripts/bash/check-prerequisites.sh" + ".specify/scripts/bash/create-new-feature.sh" + ".specify/scripts/bash/setup-plan.sh" + ".specify/scripts/bash/update-agent-context.sh" + ".specify/scripts/bash/common.sh" + ".specify/scripts/bash/bootstrap.sh" +) + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +log() { echo " ✓ $*"; } +warn() { echo " ⚠️ $*"; } +info() { echo "$*"; } + +# --------------------------------------------------------------------------- +# Validate source +# --------------------------------------------------------------------------- + +if [[ ! -d "$SOURCE_ROOT/.github/agents" ]]; then + echo "❌ Cannot locate speckit source at: $SOURCE_ROOT" + echo " Run this script from a repo that already has speckit installed." + exit 1 +fi + +if [[ "$SOURCE_ROOT" == "$TARGET_DIR" ]]; then + echo "❌ Source and target are the same directory: $TARGET_DIR" + echo " Run this script in a different (target) repo." + exit 1 +fi + +# --------------------------------------------------------------------------- +# Banner +# --------------------------------------------------------------------------- + +echo "" +echo "╔══════════════════════════════════════════════════════════╗" +echo "║ speckit bootstrap — initializing repo ║" +echo "╚══════════════════════════════════════════════════════════╝" +echo "" +echo "Source : $SOURCE_ROOT" +echo "Target : $TARGET_DIR" +echo "" + +# --------------------------------------------------------------------------- +# Step 1: Create directory structure +# --------------------------------------------------------------------------- + +info "📁 Creating directory structure..." +for dir in "${DIRS[@]}"; do + mkdir -p "$TARGET_DIR/$dir" + log "$dir/" +done +echo "" + +# --------------------------------------------------------------------------- +# Step 2: Copy generic files +# --------------------------------------------------------------------------- + +info "📄 Copying generic speckit files..." +COPIED=0 +SKIPPED=0 + +for file in "${GENERIC_FILES[@]}"; do + src="$SOURCE_ROOT/$file" + dst="$TARGET_DIR/$file" + + if [[ ! -f "$src" ]]; then + warn "Source not found, skipping: $file" + SKIPPED=$((SKIPPED + 1)) + continue + fi + + # Don't overwrite project-specific files that may already exist + if [[ -f "$dst" ]]; then + warn "Already exists, skipping: $file" + SKIPPED=$((SKIPPED + 1)) + continue + fi + + cp "$src" "$dst" + log "$file" + COPIED=$((COPIED + 1)) +done + +# Make hook scripts executable +for hook in log-session-start.sh log-session-end.sh log-prompt.sh; do + hook_path="$TARGET_DIR/.github/hooks/session-logger/$hook" + [[ -f "$hook_path" ]] && chmod +x "$hook_path" +done +# Make scripts executable +for script in check-prerequisites.sh create-new-feature.sh setup-plan.sh update-agent-context.sh common.sh bootstrap.sh; do + script_path="$TARGET_DIR/.specify/scripts/bash/$script" + [[ -f "$script_path" ]] && chmod +x "$script_path" +done + +echo "" +echo " Copied: $COPIED files | Skipped: $SKIPPED files" +echo "" + +# --------------------------------------------------------------------------- +# Step 3: Add logs/ to .gitignore +# --------------------------------------------------------------------------- + +info "🔒 Updating .gitignore..." +GITIGNORE="$TARGET_DIR/.gitignore" + +if [[ ! -f "$GITIGNORE" ]]; then + echo "# Session logs (speckit)" > "$GITIGNORE" + echo "logs/" >> "$GITIGNORE" + log "Created .gitignore with logs/ entry" +elif grep -q '^logs/' "$GITIGNORE"; then + warn "logs/ already present in .gitignore — no change" +else + echo "" >> "$GITIGNORE" + echo "# Session logs (speckit)" >> "$GITIGNORE" + echo "logs/" >> "$GITIGNORE" + log "Added logs/ to existing .gitignore" +fi +echo "" + +# --------------------------------------------------------------------------- +# Step 4: Next steps +# --------------------------------------------------------------------------- + +echo "╔══════════════════════════════════════════════════════════╗" +echo "║ bootstrap complete! ║" +echo "╚══════════════════════════════════════════════════════════╝" +echo "" +echo "📋 Next steps:" +echo "" +echo " 1. Create your project constitution:" +echo " → Open GitHub Copilot CLI and run: /speckit.constitution" +echo " → This creates .specify/memory/constitution.md" +echo "" +echo " 2. Create your Copilot instructions:" +echo " → Create .github/copilot-instructions.md with your project context" +echo " → Reference: .specify/memory/constitution.md" +echo "" +echo " 3. Start your first feature spec:" +echo " → Run: /speckit.specify " +echo " → This will auto-create .specify/memory/stack.md" +echo "" +echo " 4. Set up session logging (optional):" +echo " → Configure .github/hooks/hooks.json in your Copilot CLI settings" +echo " → See .github/hooks/session-logger/README.md for instructions" +echo "" +echo " 📖 Full portability guide: docs/speckit-portability.md" +echo " 📖 Constitution: .specify/memory/constitution.md (after step 1)" +echo "" diff --git a/.specify/scripts/bash/create-new-feature.sh b/.specify/scripts/bash/create-new-feature.sh index 5469702..a09949d 100755 --- a/.specify/scripts/bash/create-new-feature.sh +++ b/.specify/scripts/bash/create-new-feature.sh @@ -5,6 +5,7 @@ set -e JSON_MODE=false SHORT_NAME="" BRANCH_NUMBER="" +ISSUE_NUMBER="" ARGS=() i=1 while [ $i -le $# ]; do @@ -40,18 +41,34 @@ while [ $i -le $# ]; do fi BRANCH_NUMBER="$next_arg" ;; + --issue) + if [ $((i + 1)) -gt $# ]; then + echo 'Error: --issue requires a value' >&2 + exit 1 + fi + i=$((i + 1)) + next_arg="${!i}" + if [[ "$next_arg" == --* ]]; then + echo 'Error: --issue requires a value' >&2 + exit 1 + fi + # Strip leading # if present (e.g., --issue "#31" → "31") + ISSUE_NUMBER="${next_arg#\#}" + ;; --help|-h) - echo "Usage: $0 [--json] [--short-name ] [--number N] " + echo "Usage: $0 [--json] [--short-name ] [--number N] [--issue N] " echo "" echo "Options:" echo " --json Output in JSON format" echo " --short-name Provide a custom short name (2-4 words) for the branch" echo " --number N Specify branch number manually (overrides auto-detection)" + echo " --issue N Link a GitHub issue number (appended to branch name as -ghN)" echo " --help, -h Show this help message" echo "" echo "Examples:" echo " $0 'Add user authentication system' --short-name 'user-auth'" echo " $0 'Implement OAuth2 integration for API' --number 5" + echo " $0 'Fix payment timeout' --issue 42" exit 0 ;; *) @@ -241,6 +258,11 @@ else BRANCH_SUFFIX=$(generate_branch_name "$FEATURE_DESCRIPTION") fi +# Append GitHub issue number to branch suffix if provided +if [ -n "$ISSUE_NUMBER" ]; then + BRANCH_SUFFIX="${BRANCH_SUFFIX}-gh${ISSUE_NUMBER}" +fi + # Determine branch number if [ -z "$BRANCH_NUMBER" ]; then if [ "$HAS_GIT" = true ]; then @@ -304,10 +326,11 @@ if [ -f "$TEMPLATE" ]; then cp "$TEMPLATE" "$SPEC_FILE"; else touch "$SPEC_FILE" export SPECIFY_FEATURE="$BRANCH_NAME" if $JSON_MODE; then - printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s"}\n' "$BRANCH_NAME" "$SPEC_FILE" "$FEATURE_NUM" + printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_NUM":"%s","ISSUE_NUMBER":"%s"}\n' "$BRANCH_NAME" "$SPEC_FILE" "$FEATURE_NUM" "$ISSUE_NUMBER" else echo "BRANCH_NAME: $BRANCH_NAME" echo "SPEC_FILE: $SPEC_FILE" echo "FEATURE_NUM: $FEATURE_NUM" + [ -n "$ISSUE_NUMBER" ] && echo "ISSUE_NUMBER: $ISSUE_NUMBER" echo "SPECIFY_FEATURE environment variable set to: $BRANCH_NAME" fi diff --git a/.specify/templates/spec-template.md b/.specify/templates/spec-template.md index c67d914..f7cdbde 100644 --- a/.specify/templates/spec-template.md +++ b/.specify/templates/spec-template.md @@ -3,8 +3,19 @@ **Feature Branch**: `[###-feature-name]` **Created**: [DATE] **Status**: Draft +**GitHub Issue**: N/A **Input**: User description: "$ARGUMENTS" + + ## User Scenarios & Testing *(mandatory)* + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - GitHub Issue Linkage During Specify (Priority: P1) + +Before a developer runs `/speckit.specify`, the workflow first executes the issue-triage agent to surface open issue candidates, giving the developer a prioritized view of what to specify next. The developer may then confirm one or more issues (e.g., when related issues are consolidated into a single spec) to associate with the new feature. The spec file records all linked issue numbers. When the feature is eventually implemented, the PR is automatically created and linked to every associated issue. + +**Why this priority**: Issue traceability is foundational — every subsequent phase (plan, tasks, implement) benefits from knowing which issue(s) a spec addresses. Consolidating related issues into one spec prevents duplicated work, and the triage guard ensures the developer is always working from a prioritized backlog view rather than guessing. + +**Independent Test**: Run `/speckit.specify`. Verify the triage report appears first, confirm two related issues as consolidated, and verify spec.md contains a `GitHub Issue` field listing both issue numbers and URLs. Run `/speckit.implement` to completion and verify the PR references both issues. + +**Acceptance Scenarios**: + +1. **Given** GitHub MCP is configured and reachable, **When** `/speckit.specify` is invoked, **Then** the issue-triage agent runs first and presents a prioritized list of open issues before spec creation begins. +2. **Given** the triage report is shown, **When** the developer selects multiple issues to consolidate into one spec, **Then** spec.md `GitHub Issue` field lists all selected issue numbers and URLs. +3. **Given** GitHub MCP is not reachable, **When** `/speckit.specify` runs, **Then** the workflow surfaces a clear warning, skips the triage guard, and continues without blocking spec creation. +4. **Given** a spec branch is created with one or more linked issues, **When** `/speckit.implement` completes all tasks, **Then** a PR is automatically opened referencing all linked GitHub issues. + +--- + +### User Story 2 - Per-Phase Commits with Conventional Commit Skill (Priority: P2) + +A developer progresses through any speckit phase (`/speckit.specify`, `/speckit.clarify`, `/speckit.plan`, `/speckit.tasks`, `/speckit.checklist`, `/speckit.implement`, `/speckit.analyze`). At the end of each phase, the workflow invokes the `conventional-commit` skill to generate a properly structured commit message, then commits all relevant artifact changes (spec.md, plan.md, tasks.md, checklists, memory files) to the feature branch. The constitution is also updated to document that the `conventional-commit` skill is required for all speckit-phase commits. + +**Why this priority**: Without automatic commits per phase, artifact history is lost and it's unclear what state the spec was in at each decision point. Using the conventional-commit skill ensures commit messages are consistently formatted and carry correct type/scope metadata, which enables changelog generation and traceability. + +**Independent Test**: Run `/speckit.plan` on a feature with a complete spec. Verify that a commit appears on the branch containing plan.md and any updated artifacts, with a message conforming to Conventional Commits format (type, scope, description) as produced by the conventional-commit skill. + +**Acceptance Scenarios**: + +1. **Given** `/speckit.specify` completes successfully, **When** the phase ends, **Then** the conventional-commit skill generates a commit message and a commit is made containing spec.md and checklist files. +2. **Given** `/speckit.clarify` updates the spec with resolved answers, **When** the phase ends, **Then** a conventional-commit-formatted commit is made with the updated spec.md. +3. **Given** `/speckit.plan` completes, **When** the phase ends, **Then** a conventional-commit-formatted commit is made containing plan.md and any updated spec artifacts. +4. **Given** `/speckit.tasks` generates tasks.md, **When** the phase ends, **Then** a conventional-commit-formatted commit is made containing tasks.md. +5. **Given** `/speckit.checklist` generates a checklist file, **When** the phase ends, **Then** a conventional-commit-formatted commit is made containing the checklist. +6. **Given** `/speckit.analyze` completes, **When** the phase ends, **Then** a conventional-commit-formatted commit is made with any updated artifacts and the advanced spec status. +7. **Given** `/speckit.implement` completes all tasks, **When** the phase ends, **Then** a conventional-commit-formatted commit is made, the branch is pushed, and a PR is created referencing all linked GitHub issues. +8. **Given** a phase produces no artifact changes, **When** the phase ends, **Then** no commit is created (duplicate empty commits are prevented). +9. **Given** YOLO/auto-approve permissions are active and a speckit phase ends, **When** the `conventional-commit` skill is invoked, **Then** the skill still presents a pre-commit summary (list of staged files and generated commit message) and requires explicit developer confirmation before running `git commit` — auto-approve does not bypass this conversational prompt. +10. **Given** the pre-commit summary is presented and the developer declines, **When** the decision is recorded, **Then** the commit is aborted, the phase is not marked complete, and the developer is informed they can re-run the phase or commit manually. +11. **Given** the `/speckit.implement` agent determines during execution that a task requires sub-steps not captured in `tasks.md`, **When** those sub-steps are identified, **Then** `tasks.md` is updated with the new tasks and committed via the `conventional-commit` skill before implementation of that task group continues. + +--- + +### User Story 3 - Spec Status Lifecycle Enforcement (Priority: P2) + +A developer completes the `/speckit.implement` phase. The workflow automatically updates the `Status` field in spec.md from `In Progress` to `Implemented`. After `/speckit.analyze` confirms cross-artifact consistency, the status advances to `Analyzed`. + +**Why this priority**: Stale or incorrect status fields cause confusion about which specs are ready to plan, implement, or review. Automating status updates removes a manual step that is frequently forgotten. + +**Independent Test**: Run `/speckit.implement` to completion on a spec with `Status: In Progress`. Verify spec.md is updated to `Status: Implemented` and committed. + +**Acceptance Scenarios**: + +1. **Given** all tasks in tasks.md are marked done, **When** `/speckit.implement` finishes, **Then** spec.md `Status` is updated to `Implemented`. +2. **Given** `/speckit.analyze` reports no consistency issues, **When** the analysis phase ends, **Then** spec.md `Status` is updated to `Analyzed`. +3. **Given** `/speckit.analyze` reports unresolved issues, **When** the analysis phase ends, **Then** spec.md `Status` remains `Implemented` and issues are listed. + +--- + +### User Story 4 - Stack-Aware Agents with stack.md Template (Priority: P3) + +A developer runs any speckit command on a new project. The workflow checks for a `stack.md` file in `.specify/memory/`. If absent, it offers two modes: **auto-detect** (the model inspects the repository and infers stack fields from project files) or **manual entry** (the developer fills in a prompted form based on a standard template). Either mode produces a fully populated `stack.md` that is committed to the repo. Subsequent speckit commands read `stack.md` to generate accurate tasks, commands, and recommendations without re-prompting. The `stack.md` template is designed to be extensible — new fields can be added to the template without breaking existing entries. + +**Why this priority**: Stack-unaware task generation produces incorrect commands (wrong test runner, wrong package manager). A template-driven `stack.md` makes the stack profile visible, version-controllable, and easily extendable as the project evolves or new speckit capabilities are added. + +**Independent Test**: Delete `stack.md` from `.specify/memory/`. Run `/speckit.specify`. Verify the workflow offers both auto-detect and manual entry modes. Select auto-detect; verify `stack.md` is created with all standard template fields populated. Run `/speckit.tasks` and verify the generated tasks reference the tooling recorded in `stack.md`. + +**Acceptance Scenarios**: + +1. **Given** `stack.md` does not exist, **When** any speckit command runs, **Then** the workflow presents the developer with a choice: auto-detect stack or manually enter stack details. +2. **Given** the developer selects auto-detect, **When** the model inspects the repository, **Then** `stack.md` is created with all standard template fields inferred from project files (e.g., package manager detected from lock file, test library detected from config or dependencies). +3. **Given** the developer selects manual entry, **When** they complete the prompted form, **Then** `stack.md` is created with the developer-provided values filling all standard template fields. +4. **Given** `stack.md` already exists, **When** any speckit command runs, **Then** the workflow reads the file and uses its values without prompting. +5. **Given** new fields are added to the `stack.md` template (e.g., for a future speckit extension), **When** an existing `stack.md` is loaded, **Then** the workflow detects missing fields, prompts only for the new fields, and appends them without overwriting existing values. +6. **Given** a stack field changes (e.g., migration from npm to pnpm), **When** `stack.md` is updated and committed, **Then** all subsequent speckit commands use the new values. + +--- + +### User Story 5 - GitHub Issues Backlog Tracking in Memory (Priority: P3) + +A developer wants to know which GitHub issues are candidates for the next spec. The speckit workflow maintains a memory file (`.specify/memory/`) that lists open GitHub issues not yet associated with a spec, updated automatically during the specify phase. + +**Why this priority**: Without a persistent view of unspecified issues, developers must manually cross-reference GitHub and the specs directory to find the next thing to work on. + +**Independent Test**: After `/speckit.specify` runs and links issue #31, verify the memory file no longer lists #31 as unspecified, and that remaining open issues are still listed. + +**Acceptance Scenarios**: + +1. **Given** open GitHub issues exist with no corresponding spec branch, **When** the memory file is read, **Then** those issues are listed as candidates for the next spec. +2. **Given** `/speckit.specify` completes for an issue, **When** the phase ends, **Then** that issue is removed from the unspecified list in the memory file. +3. **Given** new issues are opened in GitHub, **When** the next speckit command runs, **Then** the memory file is refreshed to include newly opened issues. + +--- + +### User Story 6 - Constitution Auto-Update (Priority: P3) + +A developer introduces a paradigm shift (e.g., new architecture pattern, new tooling decision). The speckit workflow detects that the constitution is out of date and prompts the developer to review and update it. The constitution update is committed and all dependent templates are synchronized. + +**Why this priority**: A stale constitution causes speckit to generate artifacts inconsistent with current project standards. + +**Independent Test**: Manually edit the constitution to mark a section as outdated. Run `/speckit.specify`. Verify the workflow detects the change and prompts for a constitution review before proceeding. + +**Acceptance Scenarios**: + +1. **Given** a significant project change is detected (new dependency, new pattern), **When** any speckit phase starts, **Then** the workflow prompts the developer to confirm the constitution is current. +2. **Given** the constitution is updated, **When** the update is committed, **Then** all dependent templates (spec-template.md, plan-template.md, etc.) are checked for consistency and updated if needed. + +--- + +### User Story 7 - Spec Artifact Drift Detection and Auto-Update (Priority: P3) + +During or after implementation, a developer steers the feature in a direction that diverges from what was originally specified. The speckit workflow — at the end of the implement or analyze phase — diffs the feature branch artifacts (spec.md, plan.md, tasks.md) against what they contained at the point of branch creation. Where it detects a meaningful mismatch between the written spec and the actual code changes, it prompts the developer to confirm whether the spec should be updated to reflect reality. If confirmed, the affected artifact(s) are updated and committed. + +**Why this priority**: Specs and implementation routinely drift during development. Without a drift-detection pass, the spec becomes a historical artifact rather than a living document. Keeping spec.md, plan.md, and tasks.md synchronized with what was actually built ensures the analyze phase is meaningful and future maintainers understand the true intent of each feature. + +**Independent Test**: Create a feature branch with a spec. Implement a change that differs from the spec (e.g., add a capability not listed in functional requirements). Run `/speckit.analyze`. Verify the workflow reports the drift, presents the diff, and offers to update spec.md to include the new capability. Confirm the update and verify a commit is made containing the revised spec.md. + +**Acceptance Scenarios**: + +1. **Given** implementation code changes exist on the branch since branch creation, **When** `/speckit.analyze` runs, **Then** the workflow diffs spec.md, plan.md, and tasks.md against their state at branch-creation commit and reports any detected mismatches. +2. **Given** a mismatch is detected (e.g., a requirement in spec.md was not implemented, or code was added beyond the spec scope), **When** the drift report is shown, **Then** the developer is prompted to confirm whether each artifact should be updated. +3. **Given** the developer confirms an artifact update, **When** the update is applied, **Then** the artifact is revised to reflect the actual implementation direction and a conventional-commit-formatted commit is made. +4. **Given** the developer declines an artifact update, **When** the decision is recorded, **Then** the mismatch is noted in the analysis output but the artifact is left unchanged. +5. **Given** no drift is detected (implementation matches spec), **When** `/speckit.analyze` completes, **Then** no drift report is generated and the phase proceeds normally. +6. **Given** plan.md or tasks.md contain items that were never addressed (skipped or descoped), **When** drift detection runs, **Then** those items are flagged as unresolved and the developer is prompted to either remove them or mark them as deferred. + +--- + +### User Story 8 - Session Lifecycle Management (Priority: P2) + +When a developer starts a new spec, the workflow enforces that it begins in a fresh Copilot CLI session to prevent context bleed from a prior spec's conversation history. When a session starts on an existing speckit feature branch, the workflow automatically surfaces a context summary (spec name, current status, last committed phase) so the developer knows where they left off. Each speckit agent that can resume a workflow (plan, tasks, implement, analyze) explicitly loads all relevant context files at startup — constitution, stack.md, spec.md, and any existing phase artifacts — since only `copilot-instructions.md` is loaded automatically by the CLI. The session-logger hooks are guarded: they check prerequisites (logs directory, `.gitignore` entry) before writing, and emit a one-time setup reminder rather than silently failing or writing untracked files. + +**Why this priority**: Without session boundaries, context from a prior spec's plan or implementation bleeds into a new feature, leading to hallucinated file paths or incorrect assumptions. Without explicit context loading in resuming agents, the model may lack the constitution, stack constraints, or prior decisions needed to produce consistent artifacts. + +**Independent Test**: Start a fresh session, run `/speckit.specify` — verify a session boundary reminder appears. Close the session, reopen in the same branch, run `/speckit.plan` — verify the agent outputs a context summary showing the spec name and status before proceeding. Verify `logs/copilot/session.log` is not committed (present in `.gitignore`) and hook scripts emit a reminder if setup is incomplete. + +**Acceptance Scenarios**: + +1. **Given** a developer invokes `/speckit.specify` in a session that already has prior conversation history, **When** the phase begins, **Then** the agent surfaces a session boundary reminder recommending a fresh session for a new spec. +2. **Given** a developer starts a new session on a speckit feature branch, **When** `sessionStart` fires, **Then** the hook logs and surfaces a context summary: active spec name, current status, and last committed phase. +3. **Given** a developer resumes `/speckit.plan`, `/speckit.tasks`, `/speckit.implement`, or `/speckit.analyze` in a new session, **When** the agent starts, **Then** it explicitly reads constitution, stack.md, spec.md, and any existing phase artifacts before generating output. +4. **Given** the session-logger hook fires and `logs/` is not in `.gitignore`, **When** the hook runs, **Then** it emits a one-time setup reminder and skips writing logs rather than creating untracked files. +5. **Given** session-logger setup is complete (`logs/` in `.gitignore`, directory present), **When** any session lifecycle event fires, **Then** the hook writes structured JSON to `logs/copilot/session.log` without any warnings. + +--- + +### Edge Cases + +- What happens when GitHub MCP is unavailable during the specify phase — spec creation must not be blocked; triage guard is skipped with a warning. +- What happens when the developer selects multiple issues to consolidate and then deselects one mid-way — only confirmed issues are written to spec.md. +- What happens when the developer declines to provide stack information or skips auto-detect — workflow proceeds with defaults and marks stack fields as unset in stack.md. +- What happens when auto-detect cannot determine a stack field — that field is left blank in stack.md and flagged for manual entry on the next speckit run. +- What happens when a spec branch already has a commit for the current phase — no duplicate commit is created; the workflow checks for unstaged changes before committing. +- What happens when tasks.md has partially completed tasks when `/speckit.implement` is re-run — already-done tasks are skipped, status is not regressed. +- What happens when two issues could plausibly map to the same spec — the triage guard presents both candidates and the developer explicitly confirms which to link. +- What happens when drift detection finds changes in a binary or non-text asset — drift detection is scoped to tracked spec artifacts (spec.md, plan.md, tasks.md) only. +- What happens when the developer confirms a spec update but the artifact has conflicting changes — the workflow presents the diff and requires explicit resolution before committing. +- What happens when a developer runs `/speckit.specify` in a resumed session — the agent warns about session contamination risk but does not hard-block if the developer confirms they want to proceed. +- What happens when session-logger setup is partial (directory exists but logs/ not in .gitignore) — the hook emits a specific warning identifying the missing step and skips writing. +- What happens when the bootstrap script is run on a repo that already has some speckit artifacts — the script is idempotent and skips existing files without overwriting. +- What happens when a user-level agent and a project-level agent share the same name — the project-level definition always wins (local-over-global override). +- What happens when the developer declines the pre-commit summary — the commit is aborted, the phase is not marked complete, and the developer is prompted to re-run the phase or commit manually. + +--- + +### User Story 9 - Portability and Cross-Repo Bootstrap (Priority: P3) + +A developer who has refined their speckit workflow in one project wants to use the same agents, skills, templates, and scripts globally — available in every repository without manual copying. They publish all generic, project-agnostic tooling (agents, skills, templates, scripts) to a standalone speckit repository and install it once into the Copilot CLI's user-level extensions directory. For each new project, a bootstrap script from the speckit repository scaffolds the project-specific layer: constitution stub, `stack.md` prompt, hooks setup, `logs/` added to `.gitignore`, and a `copilot-instructions.md` reference block. Project-specific context (constitution, stack.md, specs) always lives in the target repo and is never stored globally. Local project overrides always take precedence over user-level defaults. + +**Why this priority**: Once the workflow is trusted, copying agents and skills into every new repo manually is error-prone and prevents consistent updates. A single global install point with a per-project bootstrap separates versioned generic tooling from always-local project context. P3 because it depends on all prior stories being stable and validated. + +**Independent Test**: Create a blank repository with no `.github/` directory. Run the bootstrap script from the speckit repo. Verify that `/speckit.specify` and all skills are available without any files copied into `.github/`, that the constitution stub and `copilot-instructions.md` reference block were created, and that running the bootstrap script a second time changes nothing. + +**Acceptance Scenarios**: + +1. **Given** the speckit repo's agents and skills are installed in the user-level extensions directory, **When** a developer opens any repository in Copilot CLI, **Then** all `/speckit.*` commands and skills (`conventional-commit`, `context-map`, `github-issues`) are available without project-level `.github/` files. +2. **Given** a new blank repository, **When** the developer runs the speckit bootstrap script, **Then** the project-specific scaffold is created: constitution stub, empty `stack.md`, hooks wired up, `logs/` in `.gitignore`, and a `copilot-instructions.md` pointer to the constitution. +3. **Given** a speckit repo update ships a new agent version, **When** the developer updates the user-level extensions install, **Then** all repos immediately benefit with no per-project changes required. +4. **Given** a project defines its own `.github/agents/speckit.specify.agent.md`, **When** `/speckit.specify` runs in that project, **Then** the project-level definition takes precedence over the user-level global one. +5. **Given** the bootstrap script has already been run on a project, **When** it is run again, **Then** it skips all existing files without overwriting them and reports which artifacts were already present. + +## Requirements *(mandatory)* + +### Functional Requirements + +- **FR-001**: The workflow MUST run the issue-triage agent before `/speckit.specify` begins when GitHub MCP is reachable, presenting a prioritized list of open issues for the developer to select from. +- **FR-002**: The workflow MUST support selecting one or more GitHub issues to associate with a single spec, recording all linked issue numbers and URLs in spec.md's `GitHub Issue` field. +- **FR-003**: The workflow MUST verify GitHub MCP connectivity at the start of the `/speckit.specify` phase and skip the triage guard with a warning when unavailable. +- **FR-004**: The workflow MUST invoke the `conventional-commit` skill to generate commit messages at the end of every speckit phase that produces artifact changes (`/speckit.specify`, `/speckit.clarify`, `/speckit.plan`, `/speckit.tasks`, `/speckit.checklist`, `/speckit.implement`, `/speckit.analyze`). +- **FR-005**: The workflow MUST NOT create a commit at the end of a phase if no artifact changes were made. +- **FR-006**: The workflow MUST update the `Status` field in spec.md to `Implemented` when all tasks in tasks.md are complete. +- **FR-007**: The workflow MUST update the `Status` field in spec.md to `Analyzed` when `/speckit.analyze` confirms consistency with no unresolved issues. +- **FR-008**: The workflow MUST create a GitHub PR and attach all linked issue(s) from spec.md when `/speckit.implement` completes and pushes the branch. +- **FR-009**: The workflow MUST check for a `stack.md` file in `.specify/memory/` at the start of any speckit command; if absent, present the developer with a choice between auto-detect and manual entry modes. +- **FR-010**: In auto-detect mode, the workflow MUST inspect the repository and populate all standard `stack.md` template fields from project files, leaving blank only fields that cannot be inferred. +- **FR-011**: In manual entry mode, the workflow MUST prompt the developer for each standard `stack.md` template field and write the provided values to `stack.md`. +- **FR-012**: The `stack.md` template MUST define a versioned set of standard fields (package manager, test library, linting tool, language version constraints, build tool, project type) that can be extended by adding new fields without invalidating existing entries. +- **FR-013**: The workflow MUST use values from `stack.md` in all task and command generation without re-prompting when the file is present and all required fields are populated. +- **FR-014**: The workflow MUST maintain a memory file listing open GitHub issues not yet associated with a spec branch, refreshed on each `/speckit.specify` run. +- **FR-015**: The workflow MUST prompt the developer to review the constitution when a paradigm shift is detected, and synchronize dependent templates after an update. +- **FR-016**: The constitution MUST be updated to document that the `conventional-commit` skill is required for all speckit-phase commits. +- **FR-017**: The workflow MUST add regression test execution as a step in the `/speckit.implement` phase; a failing test prevents the phase from being marked complete. +- **FR-018**: The workflow MUST update tasks.md with any additional tasks discovered during implementation and commit the update. +- **FR-019**: At the end of `/speckit.analyze` (or `/speckit.implement`), the workflow MUST diff spec.md, plan.md, and tasks.md against their state at branch-creation commit and report any detected mismatches between the written spec and actual changes. +- **FR-020**: When drift is detected, the workflow MUST present the diff to the developer and prompt for confirmation before updating any artifact. +- **FR-021**: When the developer confirms an artifact update due to drift, the workflow MUST revise the artifact to reflect the actual implementation direction and commit it using the `conventional-commit` skill. +- **FR-022**: When plan.md or tasks.md contain unresolved items (never implemented, not deferred), the workflow MUST flag them during drift detection and prompt the developer to remove or defer them. +- **FR-023**: The issue-triage invocation in `/speckit.specify` MUST be embedded directly in the `speckit.specify` agent definition (agent-to-agent call), not routed via an instructions file. +- **FR-024**: The `.github/instructions/issue-triage.instructions.md` file MUST be removed; its routing trigger ("when user says triage, invoke issue-triage agent") MUST be consolidated into `copilot-instructions.md` as a single reference line. +- **FR-025**: The "Constitution Principles (abridged)" section in `copilot-instructions.md` MUST be replaced with a single pointer to `.specify/memory/constitution.md`, eliminating the drift risk between the two files. A brief "Spec-Kit Workflow" reference MUST also be added to `copilot-instructions.md` pointing to the agent definitions. +- **FR-026**: The `context-map` skill MUST be invoked at the start of `/speckit.plan` to produce a map of files relevant to the feature before design artifacts are generated, ensuring plan.md references accurate file paths and dependencies. +- **FR-027**: The `context-map` skill MUST be invoked at the start of each task group in `/speckit.implement` before any code changes are made, identifying files to modify, dependent files, related tests, and risk areas for that task group. +- **FR-028**: The `github-issues` skill MUST be used during `/speckit.specify` to post a comment on each linked GitHub issue confirming the spec branch name and linking to the spec file, creating a visible trail from issue to spec. +- **FR-029**: The `github-issues` skill MUST be used during `/speckit.implement` to create the PR with `Closes #N` references for all linked issues, and to post a comment on each issue with the PR URL when opened. +- **FR-030**: The `sessionEnd` hook MUST be updated to detect uncommitted speckit artifacts (spec.md, plan.md, tasks.md with unstaged or staged-but-uncommitted changes on a speckit branch) and emit a warning before the session closes. +- **FR-031**: The `userPromptSubmitted` hook MUST be fixed to log the submitted prompt content (currently a copy-paste of `log-session-end.sh` — logs nothing useful) and extended to detect and log speckit phase command invocations for session-level audit trail. +- **FR-032**: The `sessionStart` hook MUST be updated to detect if the current branch is a speckit feature branch, and if so, log the active spec name, current status from spec.md, and the last committed speckit phase. +- **FR-033**: Each session-logger hook script MUST guard against prerequisites before writing: verify `logs/` is present in `.gitignore` and the `logs/copilot/` directory exists; emit a one-time actionable setup reminder and exit cleanly if either check fails. +- **FR-034**: The `/speckit.specify` agent MUST include a session boundary reminder at startup — informing the developer that new specs should begin in a fresh session — and surface the reminder as a non-blocking prompt if the session appears to have prior speckit conversation history. +- **FR-035**: Every speckit agent that resumes a workflow (`/speckit.plan`, `/speckit.tasks`, `/speckit.implement`, `/speckit.analyze`) MUST explicitly load the following context files at startup before generating any output: `.specify/memory/constitution.md`, `.specify/memory/stack.md` (if present), the current spec's `spec.md`, and any existing phase artifacts (`plan.md`, `tasks.md`). Only `copilot-instructions.md` is loaded automatically by the CLI; all other speckit context must be loaded explicitly. +- **FR-036**: The speckit repository MUST separate generic tooling (agents, skills, templates, scripts) from project-specific context (constitution, stack.md, specs) so that generic artifacts can be installed globally and project context always lives in the target repo. +- **FR-037**: A bootstrap script MUST be provided that, when run in a new repository, scaffolds the project-specific layer: creates a constitution stub at `.specify/memory/constitution.md`, creates an empty `.specify/memory/stack.md`, wires up session-logger hooks, adds `logs/` to `.gitignore`, and appends a speckit reference block to `copilot-instructions.md`. +- **FR-038**: The bootstrap script MUST be idempotent — running it multiple times on the same repository MUST skip all already-present artifacts without overwriting them and MUST report which items were skipped. +- **FR-039**: When a project defines a local agent or skill with the same name as a user-level global definition, the project-level definition MUST take precedence, enabling per-project overrides of the global speckit workflow. +- **FR-040**: Before executing any phase commit, the `conventional-commit` skill MUST present a pre-commit summary to the developer showing: (1) the list of files staged for commit, (2) the generated commit message with type, scope, and description. The commit MUST NOT proceed until the developer explicitly confirms. This guard applies regardless of session permission mode, including YOLO/auto-approve, because it is a conversational prompt, not a tool execution request. + +### Key Entities + +- **Constitution**: The project's authoritative source of standards, patterns, and conventions. Updated when paradigm shifts occur; drives template consistency. Documents the requirement to use the conventional-commit skill for all speckit-phase commits. +- **stack.md**: A versioned memory file in `.specify/memory/` containing a standardized, extensible set of project stack fields (package manager, test library, linter, language version constraints, build tool, project type). Populated via auto-detect or manual entry; read by all speckit agents for task and command generation. +- **Stack Template**: The canonical definition of all standard `stack.md` fields, versioned so new fields can be appended without invalidating existing entries. Lives alongside the other speckit templates. +- **Memory File (GitHub Issues)**: A file in `.specify/memory/` that tracks open GitHub issues not yet associated with a spec. Refreshed during the specify phase. +- **Phase Commit**: A git commit made at the end of each speckit phase capturing all artifact changes for that phase, with message generated by the conventional-commit skill. +- **Spec Status**: The lifecycle state of a spec (Draft → Planned → Tasked → In Progress → Implemented → Analyzed), automatically advanced by the workflow. +- **Drift Report**: The output of comparing spec artifacts (spec.md, plan.md, tasks.md) against their branch-creation state, identifying mismatches between what was specified and what was implemented. +- **Context Map**: The output of the `context-map` skill — a structured table of files to modify, dependencies, related tests, and risk areas. Produced before planning and before each implementation task group. +- **Session Audit Log**: The log written by the session-logger hooks to `logs/copilot/session.log`, capturing session start/end events, active speckit branch/phase, and prompt-level speckit command invocations. +- **Speckit Repo**: A standalone repository containing all generic, project-agnostic speckit tooling (agents, skills, templates, scripts, bootstrap script). Installed once at the user-level extensions directory; updated independently of any project repo. +- **Bootstrap Script**: A one-time idempotent setup script shipped in the speckit repo that scaffolds the project-specific speckit layer into a target repository (constitution stub, stack.md, hooks, .gitignore entry, copilot-instructions.md reference). +- **User-Level Extensions**: The Copilot CLI's personal extensions directory where agents and skills installed globally are available across all repositories, with project-level definitions taking precedence when names conflict. + +## Success Criteria *(mandatory)* + +### Measurable Outcomes + +- **SC-001**: 100% of `/speckit.specify` runs where GitHub MCP is reachable present the issue-triage report before spec creation begins. +- **SC-002**: 100% of spec.md files include a `GitHub Issue` field with at least one linked issue when the developer confirms issue selection during specify. +- **SC-003**: 100% of speckit phases that produce artifact changes result in a git commit on the feature branch generated by the conventional-commit skill before the phase ends. +- **SC-004**: spec.md `Status` is automatically advanced to `Implemented` within the same session that `/speckit.implement` completes — no manual update required. +- **SC-005**: `stack.md` is created on first speckit use of a project; subsequent speckit commands across all features require zero re-prompting for stack fields already present in the file. +- **SC-006**: The GitHub issues memory file is accurate within one speckit session — issues linked to a spec are removed, and new open issues appear, after each specify phase run. +- **SC-007**: PRs created by the workflow reference all linked GitHub issues from spec.md 100% of the time when issue linkage data is available. +- **SC-008**: Regression tests are executed as part of every `/speckit.implement` run; a failing test prevents the phase from being marked complete. +- **SC-009**: 100% of `/speckit.analyze` runs on a feature branch with code changes produce a drift report comparing spec artifacts against their branch-creation state. +- **SC-010**: When the developer confirms a drift-based artifact update, the updated artifact is committed in the same session with no manual git steps required. + +## Assumptions + +- GitHub MCP server is already configured in the developer's Copilot CLI environment; this feature adds verification, triage integration, and usage, not initial setup. +- The `conventional-commit` skill is available in the project's `.github/extensions/` or the user extensions directory and can be invoked by speckit agents. +- The constitution file exists at `.specify/memory/constitution.md` and is the authoritative source for project standards. +- The speckit scripts (`.specify/scripts/`) are bash-based and can be extended to include git commit and push operations. +- "Paradigm shift" detection is heuristic — the workflow prompts for constitution review on significant dependency additions or pattern-level changes rather than detecting it automatically. +- `stack.md` is project-scoped, not per-feature; it is created once and shared across all features in the repository. +- The `stack.md` template is versioned with a schema version field so future extensions (new standard fields) can be detected and incrementally prompted without full re-entry. +- Drift detection uses `git diff ..HEAD` scoped to spec artifact paths (spec.md, plan.md, tasks.md); it does not analyze source code semantics. +- `copilot-instructions.md` and `constitution.md` serve different purposes and MUST coexist: `copilot-instructions.md` is the model's operational quick reference (commands, import paths, code patterns); the constitution is the governance document (principles, rationale, amendment procedure). The "abridged" principles section in `copilot-instructions.md` creates a drift risk and will be replaced with a pointer to the constitution as part of this feature. +- `.github/instructions/issue-triage.instructions.md` with `applyTo: "**"` is a global context file that duplicates routing logic better placed in `copilot-instructions.md`. It has no role in agent-to-agent invocation and will be removed. +- The `context-map` and `github-issues` skills are already present in `.github/skills/`; this feature wires them into speckit agent definitions rather than creating new skills. +- The `github-issues` skill handles both MCP-based operations (preferred) and `gh api` fallback; internet/MCP connectivity is required for issue commenting and PR creation. +- Session-logger hooks are shell scripts fired by the Copilot CLI runtime and cannot influence model behavior; they serve only as observability and safety-net tooling. Hook improvements in FR-030–FR-032 are low-risk shell changes. +- The `userPromptSubmitted` hook (`log-prompt.sh`) is currently a copy-paste of `log-session-end.sh` and logs nothing meaningful. FR-031 treats this as a bug fix as well as an enhancement. +- Hooks operate at session boundaries; they cannot detect speckit phase transitions mid-session except via prompt text pattern matching in `userPromptSubmitted`. +- `copilot-instructions.md` is the only file loaded automatically by the Copilot CLI into every session context. Constitution, stack.md, and spec artifacts are NOT automatically injected — speckit agents must read them explicitly (FR-035). +- Session boundary enforcement (FR-034) is advisory, not hard-blocking: the CLI has no API to terminate a session mid-conversation; the agent can warn but not force a new session. +- The session-logger hook setup (chmod +x, logs/ in .gitignore) is a one-time project-level task. Because the hooks.json uses a `bash` prefix for invocation, the execute bit may not be strictly required at runtime, but the `.gitignore` guard (FR-033) is critical to prevent session log data from being committed. +- The Copilot CLI's local-over-global override behavior (FR-039) is a platform convention, not something this feature implements; the FR documents the expected behavior to rely on, not something to build. +- The speckit repo is out of scope for implementation in this feature; FR-036–FR-039 define the portability contract so that the current implementation is structured to support extraction. The actual standalone repo creation is a future activity. +- The exact location of the user-level Copilot CLI extensions directory is platform-dependent; the bootstrap script must handle this gracefully (document the path, fail with a clear error if not found). +- YOLO/auto-approve mode in the Copilot CLI bypasses tool execution permission prompts ("do you want to run this command?") but does NOT bypass conversational questions directed at the developer. The pre-commit summary in FR-040 is a conversational prompt and is therefore unaffected by YOLO mode. diff --git a/specs/020-speckit-workflow/tasks.md b/specs/020-speckit-workflow/tasks.md new file mode 100644 index 0000000..22cca3e --- /dev/null +++ b/specs/020-speckit-workflow/tasks.md @@ -0,0 +1,258 @@ +# Tasks: Spec-Kit Workflow Update + +**Input**: Design documents from `/specs/020-speckit-workflow/` +**Branch**: `020-speckit-workflow` | **Spec**: [spec.md](./spec.md) | **Plan**: [plan.md](./plan.md) +**Prerequisites**: ✅ spec.md, ✅ plan.md, ✅ research.md, ✅ data-model.md + +> **Scope**: Pure tooling — zero application source code changes. All changes target `.github/`, `.specify/`, and `.gitignore`. + +--- + +## Format: `[ID] [P?] [Story] Description` + +- **[P]**: Can run in parallel (independent files, no blocking deps) +- **[USN]**: User story this task belongs to +- All file paths are repo-root-relative + +--- + +## Phase 1 — Setup + +> Initialize shared infrastructure required by all phases. + +**Goal**: `.gitignore` guards session logs; `stack-template.md` schema exists for stack-aware agents. + +- [x] T001 Add `logs/` entry to `.gitignore` (repo root) so session-logger output is never committed +- [x] T002 [P] Create `.specify/templates/stack-template.md` with schema v1.0 per `data-model.md` — include all 11 sections (Meta, Packaging, Version Constraints, Linting, Testing, Build, Infrastructure, Database, Project Type, Commit Convention, Regression Tests), `schema_version: "1.0"` header, auto-detectable flags, and version upgrade procedure note + +--- + +## Phase 2 — Foundational + +> Changes that all user-story phases depend on. Must complete before Phases 3+. + +**Goal**: Pre-commit summary guard active; `copilot-instructions.md` no longer contains stale abridged constitution. + +- [x] T003 Update `.github/skills/conventional-commit/SKILL.md` — replace step 5 "no confirmation needed / auto-runs git commit" with a conversational pre-commit summary step: show staged files + generated message, ask "Proceed with this commit? (yes/no)"; on YES run `git commit`; on NO abort and inform developer phase is not marked complete +- [x] T004 [P] Update `.github/copilot-instructions.md` — remove the "Constitution Principles (abridged)" section (I–V only, stale); replace with a pointer: `> Full constitution and workflow principles: [`.specify/memory/constitution.md`](.specify/memory/constitution.md)`; add one-line speckit workflow reference below it + +--- + +## Phase 3 — US1 · GitHub Issue Traceability (P1) + +> **Story**: As a developer, when I run `/speckit.specify`, the agent automatically runs issue triage, links one or more GitHub issues to the spec, and posts traceability comments back to those issues. + +**Independent test criteria**: +- Running `/speckit.specify` on a fresh branch triggers issue-triage guard before writing spec.md +- Multiple issue numbers are written to spec.md `GitHub Issue` field as `#N, #N` comma list +- After spec.md is saved, a comment appears on each linked GitHub issue + +**Tasks**: + +- [x] T005 [US1] Update `.github/agents/speckit.specify.agent.md` — add session boundary check at top: if prior session history detected, surface a non-blocking reminder ("New session recommended per US8 — continue or start fresh?") +- [x] T006 [US1] Update `.github/agents/speckit.specify.agent.md` — add issue-triage guard block: (1) attempt GitHub MCP connectivity check; (2) if reachable, invoke `issue-triage` agent and await selection; (3) if unreachable, warn and allow manual issue number entry; (4) write all selected issue numbers to spec.md `GitHub Issue` front-matter field as comma-separated `#N, #N` list +- [x] T007 [P] [US1] Update `.github/agents/speckit.specify.agent.md` — after spec.md is written and committed, invoke `github-issues` skill to post a traceability comment on **each** linked issue: include branch name, spec title, and link to spec.md in the repo +- [x] T008 [P] [US1] Update `.github/agents/speckit.implement.agent.md` — after all tasks complete, invoke `github-issues` skill to: (1) create PR with `Closes #N` for every issue in spec.md `GitHub Issue` field; (2) post PR URL as comment on each linked issue + +--- + +## Phase 4 — US2 · Per-Phase Conventional Commits (P2) + +> **Story**: As a developer, every speckit phase agent commits its output artifact(s) using the `conventional-commit` skill with a pre-commit summary, so the git history is always in sync with spec progress. + +**Independent test criteria**: +- After each speckit agent completes its phase, `git log --oneline` shows a new commit scoped to that phase +- Running any agent with no artifact changes produces "No changes to commit" (no empty commits) +- The conventional-commit skill presents staged files + message before committing + +**Tasks**: + +- [x] T009 [US2] Update `.specify/memory/constitution.md` — add to Principle VI (Spec-Kit Workflow): "Every speckit phase MUST end with a `conventional-commit` skill invocation. Phase commits follow the format `docs(): (#)`." +- [x] T010 [US2] Update `.github/agents/speckit.specify.agent.md` — add Phase-End Commit Block: (1) run `git status --short` scoped to `specs/$BRANCH/`; (2) if no changes skip with "No changes to commit"; (3) if changes, invoke `conventional-commit` skill; type=`docs`, scope=`spec`, include issue numbers in footer +- [x] T011 [P] [US2] Update `.github/agents/speckit.clarify.agent.md` — add Phase-End Commit Block (same pattern as T010; scope=`clarify`) +- [x] T012 [P] [US2] Update `.github/agents/speckit.plan.agent.md` — add Phase-End Commit Block (scope=`plan`) +- [x] T013 [P] [US2] Update `.github/agents/speckit.tasks.agent.md` — add Phase-End Commit Block (scope=`tasks`) +- [x] T014 [P] [US2] Update `.github/agents/speckit.checklist.agent.md` — add Phase-End Commit Block (scope=`checklist`) +- [x] T015 [P] [US2] Update `.github/agents/speckit.analyze.agent.md` — add Phase-End Commit Block (scope=`analyze`) +- [x] T016 [P] [US2] Update `.github/agents/speckit.implement.agent.md` — add Phase-End Commit Block (scope=`implement`); note: implement may produce multiple commits per task group — each group gets its own commit via `conventional-commit` skill + +--- + +## Phase 5 — US3 · Automatic Spec Status Advancement (P2) + +> **Story**: As a developer, spec.md `Status` is automatically advanced at the end of each phase so it always reflects the true workflow state without manual edits. + +**Independent test criteria**: +- After `/speckit.implement` completes, spec.md `Status` reads `Implemented` +- After `/speckit.analyze` completes, spec.md `Status` reads `Analyzed` +- Status advancement is included in the phase-end commit (not a separate commit) + +**Tasks**: + +- [x] T017 [US3] Update `.github/agents/speckit.implement.agent.md` — before Phase-End Commit Block (T016), add status advancement step: update spec.md `**Status**:` line from `Tasked` → `In Progress` at start, then → `Implemented` on completion; include status change in the phase-end commit +- [x] T018 [P] [US3] Update `.github/agents/speckit.analyze.agent.md` — before Phase-End Commit Block (T015), add status advancement step: update spec.md `**Status**:` line → `Analyzed` on completion; include in phase-end commit + +--- + +## Phase 6 — US8 · Session Lifecycle Management (P2) + +> **Story**: As a developer, the session-logger hooks are reliable and self-guarding, each speckit phase explicitly loads prior context at startup, and the workflow enforces fresh sessions for new specs. + +**Independent test criteria**: +- Running any hook script when `logs/` is absent from `.gitignore` prints a setup warning and exits cleanly (exit 0) +- `log-prompt.sh` writes prompt content to `logs/copilot/` (not a "sessionEnd" event) +- On a speckit branch, `log-session-start.sh` logs spec name + status in the session JSON +- Each resuming agent (plan, tasks, implement, analyze) prints a one-line context summary at startup + +**Tasks**: + +- [x] T019 [US8] Fix `.github/hooks/session-logger/log-prompt.sh` — rewrite to actually capture prompt content: log a JSON object with `event: "userPromptSubmitted"`, `prompt` text, `timestamp`, and `branch`; remove copy-pasted "sessionEnd" event code +- [x] T020 [US8] Update `.github/hooks/session-logger/log-session-start.sh` — add `.gitignore` guard at top (check `grep -q '^logs/' .gitignore`); if missing, print `⚠️ speckit-setup: add 'logs/' to .gitignore` and `exit 0`; add `mkdir -p logs/copilot` guard +- [x] T021 [P] [US8] Update `.github/hooks/session-logger/log-session-end.sh` — add same `.gitignore` guard (T020 pattern); add speckit branch detection: if on a `NNN-*` branch with uncommitted changes to `specs/$BRANCH/`, append `⚠️ uncommitted spec artifacts detected` warning to session log +- [x] T022 [P] [US8] Update `.github/hooks/session-logger/log-session-start.sh` — add speckit branch detection block: extract `BRANCH`, check for `specs/$BRANCH/spec.md`, read `**Status**:` line, log `speckit_context: { spec, status, last_phase }` into session-start JSON +- [x] T023 [US8] Update `.github/agents/speckit.specify.agent.md` — add fresh-session enforcement reminder at very top (above issue-triage guard): "This agent should run in a new Copilot CLI session. If you have prior conversation history, start a new session before proceeding." +- [x] T024 [P] [US8] Update `.github/agents/speckit.plan.agent.md` — add Context Loading Preamble as first numbered section: (1) Read `.specify/memory/constitution.md`; (2) Read `.specify/memory/stack.md` — warn if absent; (3) Read `spec.md`; (4) Output one-line summary: `Loaded: [spec name] | Status: [status] | Stack: [tool]` +- [x] T025 [P] [US8] Update `.github/agents/speckit.tasks.agent.md` — add same Context Loading Preamble (T024 pattern): read constitution.md, stack.md, spec.md, plan.md; output context summary line +- [x] T026 [P] [US8] Update `.github/agents/speckit.implement.agent.md` — add Context Loading Preamble (T024 pattern): read constitution.md, stack.md, spec.md, plan.md, tasks.md; output context summary line + +--- + +## Phase 7 — US4 · Stack-Aware Agents (P3) + +> **Story**: As a developer, speckit agents read a versioned `stack-template.md` schema to auto-detect or prompt for the project stack, storing results in `.specify/memory/stack.md` so all subsequent agents have consistent stack context. + +**Independent test criteria**: +- `/speckit.specify` run on a project without `stack.md` offers auto-detect or manual-entry flow +- `.specify/memory/stack.md` written by specify contains all required v1.0 fields +- plan, tasks, implement, analyze agents each print the stack context line at startup + +**Tasks**: + +- [x] T027 [US4] Update `.github/agents/speckit.specify.agent.md` — add stack check block after issue-triage guard: (1) check if `.specify/memory/stack.md` exists; (2) if absent, read `.specify/templates/stack-template.md` schema; (3) offer two options — "Auto-detect from repo" (inspect lock files, package.json, config files) or "Manual entry"; (4) write populated `stack.md` using template field order and `schema_version: "1.0"`; (5) if present but `schema_version` < template version, prompt only for missing fields +- [x] T028 [P] [US4] Update `.github/agents/speckit.plan.agent.md` — in Context Loading Preamble (T024), after reading stack.md, surface tech-specific notes: e.g. if `testing.backend_framework = vitest`, reference vitest patterns in plan; if `database.orm = prisma`, note migration steps +- [x] T029 [P] [US4] Update `.github/agents/speckit.tasks.agent.md` — in Context Loading Preamble (T025), after reading stack.md, use `regression_tests` section to populate task descriptions with correct lint/test commands from stack.md rather than hardcoded values +- [x] T030 [P] [US4] Update `.github/agents/speckit.implement.agent.md` — in Context Loading Preamble (T026), after reading stack.md, use `regression_tests.lint_cmd` and `regression_tests.test_cmd` for post-implementation validation steps; use `packaging.install_cmd` for dependency install instructions +- [x] T031 [P] [US4] Update `.github/agents/speckit.analyze.agent.md` — add Context Loading Preamble (T024 pattern): read constitution.md, stack.md, spec.md, plan.md, tasks.md; output context summary; use stack.md `regression_tests` section to validate that implementation tasks referenced correct commands + +--- + +## Phase 8 — US5 · Issues Backlog Refresh (P3) + +> **Story**: As a developer, running `/speckit.specify` on an existing branch refreshes the `issues-backlog.md` snapshot so issue selection is always up-to-date. + +**Independent test criteria**: +- Running `/speckit.specify` on a branch that already has a spec.md updates `issues-backlog.md` timestamp +- If GitHub MCP is unreachable, the existing `issues-backlog.md` is preserved and a warning is shown + +**Tasks**: + +- [x] T032 [US5] Update `.github/agents/speckit.specify.agent.md` — after MCP connectivity check (T006), add backlog refresh step: if MCP reachable, invoke `issue-triage` agent in refresh mode and overwrite `.specify/memory/issues-backlog.md` with current open issues snapshot + `last_refreshed` timestamp; if MCP unreachable, read existing `issues-backlog.md` and note its age + +--- + +## Phase 9 — US6 · Paradigm Shift Detection (P3) + +> **Story**: As a developer, speckit agents detect when a proposed change would require updating foundational templates (constitution, stack template, agent templates) and prompt explicitly before proceeding. + +**Independent test criteria**: +- Specifying a feature that adds a new required stack field triggers a template-sync prompt +- speckit.clarify detects constitution-level changes and surfaces them before writing + +**Tasks**: + +- [x] T033 [US6] Update `.github/agents/speckit.specify.agent.md` — add paradigm shift detection block before writing spec.md: scan spec content for signals of stack changes (new language/framework, new ORM, new test runner) or workflow changes (new phase, new commit format); if detected, present: "This spec may require updating `.specify/templates/stack-template.md` or `.specify/memory/constitution.md` — review before proceeding (yes/no)" +- [x] T034 [P] [US6] Update `.github/agents/speckit.clarify.agent.md` — add Context Loading Preamble (T024 pattern) and paradigm shift check: if clarification answers change core architecture (project type, ORM, packaging tool), flag that `stack.md` and `stack-template.md` may need updating; surface this as a clarification output item before phase-end commit + +--- + +## Phase 10 — US7 · Spec Artifact Drift Detection (P3) + +> **Story**: As a developer, `/speckit.analyze` compares spec.md, plan.md, and tasks.md against the branch-creation baseline and surfaces any mismatches so docs stay in sync with what was actually built. + +**Independent test criteria**: +- `/speckit.analyze` run after implementation changes shows a diff of spec artifacts vs branch-creation commit +- Unresolved tasks (in tasks.md, never referenced in a commit) are flagged +- Developer is prompted per artifact to accept or skip each update + +**Tasks**: + +- [x] T035 [US7] Update `.github/agents/speckit.analyze.agent.md` — add drift detection block before status advancement (T018): (1) find branch-creation commit with `git log --oneline --reverse HEAD | head -1 | cut -d' ' -f1`; (2) run `git diff $BASE_COMMIT..HEAD -- specs/$BRANCH/spec.md specs/$BRANCH/plan.md specs/$BRANCH/tasks.md`; (3) if diff is empty, report "No spec artifact drift detected" +- [x] T036 [US7] Update `.github/agents/speckit.analyze.agent.md` — add drift report presentation: for each artifact with changes, show a structured diff summary (sections added/removed/changed) and prompt: "Update [artifact] to reflect implementation changes? (yes/skip)"; on yes, assist developer in editing that artifact; on skip, continue +- [x] T037 [P] [US7] Update `.github/agents/speckit.analyze.agent.md` — add unresolved-item check: scan tasks.md for unchecked items (`- [ ]`); cross-reference with `git log --oneline` for task-related commits; flag any tasks with no corresponding commit as "possibly unimplemented or not deferred — confirm status" + +--- + +## Phase 11 — US9 · Portability Contract (P3) + +> **Story**: As a developer, speckit tooling is cleanly separated from project-specific context so it can be bootstrapped into any new repository with a single script. + +**Independent test criteria**: +- `bootstrap.sh` runs on a fresh repo and creates the full `.github/` and `.specify/` directory structure +- `PORTABILITY.md` clearly lists which files are generic tooling vs project-specific + +**Tasks**: + +- [x] T038 [US9] Create `docs/speckit-portability.md` — document the portability contract: list all generic speckit files (agents, skills, hooks, templates) vs project-specific files (constitution.md, stack.md, copilot-instructions.md); include instructions for applying speckit to a new repo; note that user-level Copilot CLI extensions directory enables global installation without per-repo file copies +- [x] T039 [P] [US9] Create `.specify/scripts/bash/bootstrap.sh` — script that initializes the full speckit structure in a target repo: creates `.github/agents/`, `.github/skills/`, `.github/hooks/`, `.specify/memory/`, `.specify/templates/`, `.specify/scripts/`; copies generic agent/skill/hook files; adds `logs/` to `.gitignore`; prints next-steps instructions for creating constitution.md and stack.md + +--- + +## Phase 12 — Polish & Verification + +> Verify all changed files are consistent, no broken references, and the workflow is end-to-end coherent. + +- [x] T040 Run `pnpm lint` from repo root and confirm zero new errors introduced by any markdown/shell changes +- [x] T041 [P] Verify all 7 speckit agent files contain: (1) Context Loading Preamble, (2) Phase-End Commit Block referencing `conventional-commit` skill — open each `.github/agents/speckit.*.agent.md` and confirm presence +- [x] T042 [P] Verify all 3 hook scripts contain `.gitignore` guard — open each `.github/hooks/session-logger/*.sh` and confirm `grep -q '^logs/' .gitignore` guard is present at top of each; verify `log-prompt.sh` logs `userPromptSubmitted` event (not `sessionEnd`) + +--- + +## Dependency Graph + +``` +Phase 1 (T001, T002) + └─► Phase 2 (T003, T004) ← blocks all user-story phases + ├─► Phase 3 US1 (T005–T008) P1 — start here + ├─► Phase 4 US2 (T009–T016) P2 + │ └─ T009 (constitution) must precede T010–T016 (agents) + ├─► Phase 5 US3 (T017–T018) P2 — depends on Phase 4 (commit blocks must exist) + ├─► Phase 6 US8 (T019–T026) P2 + │ └─ T019–T022 (hooks) parallelizable; T023–T026 (agents) parallelizable + ├─► Phase 7 US4 (T027–T031) P3 — T027 (specify) must precede T028–T031 + ├─► Phase 8 US5 (T032) P3 — depends on Phase 3 (MCP guard in specify) + ├─► Phase 9 US6 (T033–T034) P3 + ├─► Phase 10 US7 (T035–T037) P3 — T035 must precede T036, T037 + ├─► Phase 11 US9 (T038–T039) P3 — independent + └─► Phase 12 Polish (T040–T042) — after all phases complete +``` + +**US priority execution order**: US1 → US2 → US3 → US8 → US4 → US5 → US6 → US7 → US9 + +--- + +## Parallel Execution Opportunities + +| Phase | Parallelizable tasks | +|-------|----------------------| +| Phase 1 | T001 ‖ T002 | +| Phase 4 | T011 ‖ T012 ‖ T013 ‖ T014 ‖ T015 ‖ T016 (after T009–T010) | +| Phase 6 | T020 ‖ T021 ‖ T022 (hooks); T024 ‖ T025 ‖ T026 (agents) | +| Phase 7 | T028 ‖ T029 ‖ T030 ‖ T031 (after T027) | +| Phase 11 | T038 ‖ T039 | +| Phase 12 | T041 ‖ T042 | + +--- + +## Implementation Strategy + +**MVP scope (Phase 1–3 only)**: Deliver US1 (issue triage guard + multi-issue + traceability comments). This is the highest-value P1 story and validates the GitHub MCP integration path for all downstream stories. + +**Incremental delivery order**: +1. Phases 1–2 (Setup + Foundational) — unblock everything; fix the conventional-commit auto-commit bug +2. Phase 3 (US1) — P1, highest priority +3. Phases 4–6 (US2, US3, US8) — P2 stories; all agent-level changes +4. Phases 7–11 (US4–US9) — P3 stories; can be deferred without blocking P1/P2 value +5. Phase 12 (Polish) — final gate before marking spec Implemented + +**Total tasks**: 42 +**By user story**: US1=4, US2=8, US3=2, US4=5, US5=1, US6=2, US7=3, US8=8, US9=2 | Setup=2, Foundational=2, Polish=3