diff --git a/.cursor/rules/automatic-openspec-workflow.mdc b/.cursor/rules/automatic-openspec-workflow.mdc index 30047c1f..854eb0ef 100644 --- a/.cursor/rules/automatic-openspec-workflow.mdc +++ b/.cursor/rules/automatic-openspec-workflow.mdc @@ -219,6 +219,11 @@ When user requests a change, **do not write or modify any application code yet** - **If an issue already exists:** Sync the issue with the change (e.g. update issue body/status from proposal, ensure Development link to branch/PR). Update `proposal.md` Source Tracking section with current status (e.g. "Last Synced Status: in-progress" or "done"). - Ensure backlog and code stay in sync: proposal Source Tracking and the GitHub issue reflect the current state of the change. +4. **Archive completed changes (mandatory):** + - When implementation is merged or the change is otherwise complete, run `openspec archive ` from the repository root so delta specs merge into `openspec/specs/` and the change moves under `openspec/changes/archive/`. + - **Do not** manually move `openspec/changes//` into `archive/` or hand-edit archive paths. + - Use `-y` / `--yes` only for non-interactive flows; reserve `--skip-specs` and `--no-validate` for explicit user-confirmed exceptions (see `docs/agent-rules/40-openspec-and-tdd.md`). + ## Implementation Pattern **Example workflow:** @@ -238,7 +243,7 @@ AI (automatically): 6. Applies: /opsx:apply - Implements the change (TDD: tests first, then code) - Updates tasks -7. After implementation: Verify (quality gates), validate (openspec validate), sync backlog +7. After implementation: Verify (quality gates), validate (openspec validate), sync backlog, then archive with `openspec archive ` when done - Create or update GitHub issue if public repo; update proposal Source Tracking ``` @@ -284,7 +289,7 @@ This rule works alongside: - This rule applies to the **specfact-cli** codebase specifically - **Mandatory before any code modification:** Read and apply `openspec/config.yaml` (project context and per-artifact rules). Ensure an OpenSpec change (new or delta) exists and is validated. No code change without a corresponding change proposal unless the user explicitly opts out. -- **Mandatory after each implementation:** Verify (quality gates), validate (`openspec validate --strict`), and sync backlog (create or update GitHub issue, update proposal Source Tracking) so backlog and code stay in sync. +- **Mandatory after each implementation:** Verify (quality gates), validate (`openspec validate --strict`), and sync backlog (create or update GitHub issue, update proposal Source Tracking) so backlog and code stay in sync. **When the change is complete and merged,** run `openspec archive ` (never manual `mv` of change folders). - For other repositories, this rule may not apply (check repository-specific rules) - **Important workspace distinction:** - **OpenSpec changes** are stored in `specfact-cli/openspec/changes/` diff --git a/.cursor/rules/session_startup_instructions.mdc b/.cursor/rules/session_startup_instructions.mdc index 689b0598..8ff08f91 100644 --- a/.cursor/rules/session_startup_instructions.mdc +++ b/.cursor/rules/session_startup_instructions.mdc @@ -1,45 +1,17 @@ --- -description: This rule provides essential instructions for testing and building the project correctly, avoiding common pitfalls with test environment management. +description: Bootstrap Cursor sessions through the canonical agent governance docs instead of relying on stale inline reminders. globs: alwaysApply: true --- # Session Startup Instructions -## Core Workflow Reminders +1. Read `AGENTS.md`. +2. Read `docs/agent-rules/INDEX.md`. +3. Read `docs/agent-rules/05-non-negotiable-checklist.md`. +4. Detect repository root, branch, and worktree state before implementation. +5. Load any additional rule files selected by the applicability matrix in `docs/agent-rules/INDEX.md`. +6. If a sibling internal checkout (for example `../specfact-cli-internal/`) exists and you are designing or scoping an OpenSpec change, follow the internal wiki guidance in `docs/agent-rules/40-openspec-and-tdd.md` (section **Internal wiki and strategic context**): read `wiki/hot.md`, `wiki/graph.md`, and relevant `wiki/concepts/*.md` via absolute paths; do not import wiki text into this repo. When you materially change an active `openspec/changes//` artifact, mirror those semantics to `wiki/sources/.md` in the sibling repo (then `cd` there and run `python3 scripts/wiki_rebuild_graph.py`); if the sibling checkout is absent, leave a merge checklist or follow-up instead of claiming the wiki is updated. +7. After merges that ship OpenSpec- or GitHub-related work, when a sibling `specfact-cli-internal` checkout exists, run wiki scripts only with that repo as the **current working directory** (for example `cd ../specfact-cli-internal && python3 scripts/wiki_openspec_gh_status.py`); running from `specfact-cli` or other cwd breaks script logic. If many `docs/agent-rules/` frontmatter files changed, also `cd ../specfact-cli-internal && python3 scripts/wiki_rebuild_graph.py` (details under **Internal wiki maintenance** in `docs/agent-rules/40-openspec-and-tdd.md`). -- **Testing:** Run `hatch run smart-test`. Ensure total coverage is >= 80%. -- **Formatting:** Apply `black .`, `isort .`, `mypy .`, and `pylint src tests` before committing. -- **Commits:** Use the [Conventional Commits](https://www.conventionalcommits.org/) format. -- **Versioning:** When releasing, update `CHANGELOG.md`, `pyproject.toml`, `setup.py`, and `src/__init__.py`. -- **Docstrings:** Follow the [Google Python Style Guide](https://google.github.io/styleguide/pyguide.html). - -## Initial Context Checklist - -1. **Review: -1.1. `GEMINI.md`: Check for any session-specific goals or instructions (applies to GEMINI CLI only). -1.2. `CLAUDE.md`: Check for any session-specific goals or instructions (applies to Claude CLI only). -2. `docs/README.md` to get the latest project status and priorities and see which plan is referenced as being worked on: Understand the current development phase and tasks based on the mentioned plan in the README.md file. -3. Outline your understanding of the current development phase and tasks based on the mentioned plan in the README.md file, before proceeding with any work. Ask the user for confirmation before proceeding. - -## Documentation and Planning Guidelines - -**CRITICAL**: When working with planning and documentation: - -- **Work directly with major artifacts**: Update strategic plans, implementation plans, and analysis documents directly. Do NOT create plans for plans, tracking documents for tracking documents, or status artifacts for status artifacts. -- **Update existing artifacts**: Add status annotations (βœ… Complete, ⏳ In Progress, 🟑 Pending) directly to existing plan documents rather than creating separate status files. -- **Consolidate, don't multiply**: Only create new documentation artifacts when they add clear, unique value that cannot be captured in existing artifacts. -- **Performance metrics**: Record timing and performance data directly in implementation status documents, not in separate performance tracking files. -- **Test results**: Include test results and validation outcomes in the relevant implementation status or quality analysis documents. - -**Examples of what NOT to do**: - -- ❌ Creating `PHASE0_TRACKING.md` when `CODE2SPEC_STRATEGIC_PLAN.md` already exists -- ❌ Creating `STEP1_1_TEST_RESULTS.md` when `PHASE1_IMPLEMENTATION_STATUS.md` can be updated -- ❌ Creating `PERFORMANCE_METRICS.md` when performance data can go in implementation status - -**Examples of what TO do**: - -- βœ… Update `CODE2SPEC_STRATEGIC_PLAN.md` with status annotations (βœ… Complete, ⏳ Next) -- βœ… Add test results and performance metrics to `PHASE1_IMPLEMENTATION_STATUS.md` -- βœ… Update `QUALITY_GAP_ANALYSIS.md` with measurement results and progress +Do not treat this file as a complete workflow handbook. The canonical repository governance now lives in `docs/agent-rules/`. diff --git a/.cursorrules b/.cursorrules index 7350666b..d357be5e 100644 --- a/.cursorrules +++ b/.cursorrules @@ -112,6 +112,6 @@ The OPSX commands (`/opsx:ff`, `/opsx:apply`, etc.) are provided by OpenSpec CLI | `/opsx:ff` | Change scaffolding | Worktree setup, TDD, docs | **Add these to tasks.md explicitly** | | `/opsx:apply` | Task iteration | Worktree context, TDD evidence | **Verify before each code change** | | `/opsx:continue` | Resume progress | Worktree verification | **Check worktree exists before implementing** | -| `/opsx:archive` | Archive change | Module signing, cleanup | **Include in final tasks** | +| `/opsx:archive` | Archive change | Module signing, cleanup | **Include in final tasks**; agents MUST run `openspec archive ` from repo root (no manual `mv` under `openspec/changes/archive/`) | **Remember**: When in doubt, AGENTS.md wins. State this explicitly in your output. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ad6cb5f3..094242a1 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,23 +1,11 @@ # GitHub Copilot Instructions β€” specfact-cli -## Clean-Code Charter +Use [AGENTS.md](../AGENTS.md) as the mandatory bootstrap surface and [docs/agent-rules/INDEX.md](../docs/agent-rules/INDEX.md) as the canonical governance dispatcher. -This repository enforces the **7-principle clean-code charter** defined in: -- `skills/specfact-code-review/SKILL.md` (`nold-ai/specfact-cli-modules`) -- Policy-pack: `specfact/clean-code-principles` +## Minimal reminders -Review categories checked on every PR: **naming Β· kiss Β· yagni Β· dry Β· solid** - -Phase A KISS thresholds: LOC > 80 warning / > 120 error per function. -Nesting-depth and parameter-count checks are active. Phase B (>40/80) is deferred. - -Run `hatch run specfact code review run --json --out .specfact/code-review.json` before submitting. - -## Key conventions - -- Python 3.11+, Typer CLI, Pydantic models, `@icontract` + `@beartype` on all public APIs -- No `print()` in `src/` β€” use `get_bridge_logger()` -- Branch protection: work on `feature/*`, `bugfix/*`, `hotfix/*` branches; PRs to `dev` -- Pre-commit checklist: `hatch run format` β†’ `type-check` β†’ `lint` β†’ `yaml-lint` β†’ `contract-test` β†’ `smart-test` - -See `AGENTS.md` and `.cursor/rules/` for the full contributor guide. +- When a sibling internal repository (for example `../specfact-cli-internal/`) exists, read its `wiki/` files by absolute path before designing an OpenSpec change; see **Strategic context** in `AGENTS.md` and `docs/agent-rules/40-openspec-and-tdd.md` (section **Internal wiki and strategic context**). Do not copy wiki content into this repository. +- This repository enforces the clean-code review gate through `hatch run specfact code review run --json --out .specfact/code-review.json`. +- Public APIs require `@icontract` and `@beartype`. +- Work belongs on `feature/*`, `bugfix/*`, `hotfix/*`, or `chore/*` branches, normally in a worktree. +- The full governance rules live in `docs/agent-rules/`; do not treat this file as a complete standalone handbook. diff --git a/.github/workflows/docs-review.yml b/.github/workflows/docs-review.yml index 3afa28db..59a42c68 100644 --- a/.github/workflows/docs-review.yml +++ b/.github/workflows/docs-review.yml @@ -19,6 +19,9 @@ on: - "scripts/check-docs-commands.py" - "scripts/check-cross-site-links.py" - "scripts/check_doc_frontmatter.py" + - "scripts/validate_agent_rule_applies_when.py" + - "scripts/check_version_sources.py" + - "docs/agent-rules/INDEX.md" - "pyproject.toml" - ".github/workflows/docs-review.yml" push: @@ -37,6 +40,9 @@ on: - "scripts/check-docs-commands.py" - "scripts/check-cross-site-links.py" - "scripts/check_doc_frontmatter.py" + - "scripts/validate_agent_rule_applies_when.py" + - "scripts/check_version_sources.py" + - "docs/agent-rules/INDEX.md" - "pyproject.toml" - ".github/workflows/docs-review.yml" workflow_dispatch: @@ -79,6 +85,9 @@ jobs: - name: Validate documentation frontmatter (enforced paths; aligns with local docs-validate / pre-commit) run: hatch run doc-frontmatter-check + - name: Validate agent-rules applies_when canonical task signals + run: hatch run validate-agent-rule-signals + - name: Run docs review test suites run: | mkdir -p logs/docs-review diff --git a/.github/workflows/pr-orchestrator.yml b/.github/workflows/pr-orchestrator.yml index b865862b..c7f022a1 100644 --- a/.github/workflows/pr-orchestrator.yml +++ b/.github/workflows/pr-orchestrator.yml @@ -212,6 +212,10 @@ jobs: pip install "hatch" "virtualenv<21" coverage "coverage[toml]" pytest pytest-cov pytest-mock pytest-asyncio pytest-xdist pytest-timeout pip install -e ".[dev]" + - name: Verify version strings are synchronized + if: needs.changes.outputs.skip_tests_dev_to_main != 'true' + run: python scripts/check_version_sources.py + - name: Cache hatch environments if: needs.changes.outputs.skip_tests_dev_to_main != 'true' uses: actions/cache@v4 diff --git a/AGENTS.md b/AGENTS.md index 24c320fb..281ecfc1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,411 +1,57 @@ # AGENTS.md -This file provides guidance to coding agents when working with code in this repository. - -## Project Overview - -SpecFact CLI is a Python CLI tool for agile DevOps teams. It keeps backlogs, specs, tests, and code in sync with contract-driven development, validation, and enforcement. Built with Typer + Rich, using Hatch as the build system. Python 3.11+. - -## Essential Commands - -```bash -# Development environment -pip install -e ".[dev]" -hatch shell - -# Format & lint (run after every code change, in this order) -hatch run format # ruff format + fix -hatch run type-check # basedpyright strict mode -hatch run contract-test # contract-first validation (primary) -hatch test --cover -v # full pytest suite - -# Contract-first testing layers -hatch run contract-test-contracts # runtime contract validation only -hatch run contract-test-exploration # CrossHair symbolic execution -hatch run contract-test-scenarios # integration/E2E with contract refs -hatch run contract-test-full # all layers -hatch run contract-test-status # coverage status report - -# Run a single test file -hatch test -- tests/unit/specfact_cli/test_example.py -v - -# Lint subsystems -hatch run lint # full lint suite -hatch run governance # pylint detailed analysis -hatch run yaml-lint # YAML validation -hatch run lint-workflows # GitHub Actions actionlint - -# Code scanning -hatch run scan-all # semgrep analysis - -# SpecFact code review JSON (dogfood; see "SpecFact Code Review JSON" below and openspec/config.yaml) -hatch run specfact code review run --json --out .specfact/code-review.json -``` - -## Architecture - -### Modular Command Registry with Lazy Loading - -The CLI uses a module package system in `src/specfact_cli/modules/`. Each module is a self-contained package: - -``` -modules/{name}/ - module-package.yaml # metadata: name, version, commands, dependencies - src/{name}/ - __init__.py - main.py # typer.Typer app with command definitions -``` - -The registry (`src/specfact_cli/registry/`) discovers modules at startup but defers imports until a command is actually invoked. `bootstrap.py` registers all modules; `registry.py` manages lazy loading; `module_packages.py` handles discovery from `module-package.yaml` files. - -**Entry flow**: `cli.py:cli_main()` β†’ Typer app with global options β†’ `ProgressiveDisclosureGroup` for help β†’ lazy-loaded command groups from registry. - -### Contract-First Development - -All public APIs must use `@icontract` decorators (`@require`, `@ensure`, `@invariant`) and `@beartype` for runtime type checking. CrossHair discovers counterexamples via symbolic execution. Contracts are the primary validation mechanism; traditional unit tests are secondary. - -### Key Subsystems - -- **`models/`** - Pydantic BaseModel classes for all data structures -- **`parsers/`**, **`analyzers/`** - Code analysis -- **`generators/`** - Code/spec generation using Jinja2 templates from `resources/templates/` -- **`validators/`** - Schema, contract, FSM validation -- **`adapters/`** - Bridge pattern for tool integrations (GitHub, Azure DevOps, Jira, Linear) -- **`modes/`** - Operational modes: CICD (fast, deterministic, non-interactive) vs Copilot (interactive, IDE-aware). Auto-detected from environment. -- **`resources/`** - Bundled prompts, templates, schemas, mappings (force-included in wheel) - -### Logging - -Use `from specfact_cli.common import get_bridge_logger` and avoid `print()` in production command paths. Debug logs go to `~/.specfact/logs/specfact-debug.log` when `--debug` is passed. - -## Development Workflow - -### Branch Protection - -`dev` and `main` are protected. Always work on feature/bugfix/hotfix branches and submit PRs: -- `feature/your-feature-name` -- `bugfix/your-bugfix-name` -- `hotfix/your-hotfix-name` - -### Git Worktree Policy (Parallel Development) - -Use git worktrees for parallel development branches only. - -- Allowed branch types in worktrees: `feature/*`, `bugfix/*`, `hotfix/*`, `chore/*` -- Forbidden in worktrees: `dev`, `main` -- The primary checkout remains the canonical `dev` workspace - -Canonical layout: - -- Primary checkout: `.../specfact-cli` (tracks `dev`) -- Worktrees root: `.../specfact-cli-worktrees//` -- Worktree folder name MUST reflect the branch slug - -Preferred helper commands (from repository root): - -```bash -scripts/worktree.sh create feature/ -scripts/worktree.sh list -scripts/worktree.sh cleanup feature/ -``` - -Create a new worktree from `origin/dev`: - -```bash -git fetch origin -git worktree add ../specfact-cli-worktrees/feature/ -b feature/ origin/dev -``` - -Attach an existing local branch to a worktree: - -```bash -git fetch origin -git worktree add ../specfact-cli-worktrees/feature/ feature/ -``` - -Operational rules: - -- Never create a worktree for `dev` or `main` -- One branch maps to exactly one worktree path at a time -- Keep branch naming consistent: `/-` -- Keep one active OpenSpec change scope per branch where possible -- Create a separate virtual environment inside each worktree (for example, `.venv/`) -- Bootstrap Hatch once per new worktree before running quality gates: `hatch env create` -- Run quick pre-flight checks from the worktree root: `hatch run smart-test-status` and `hatch run contract-test-status` -- If Hatch cannot write to default home/cache paths, set writable overrides (for example `HATCH_DATA_DIR=/tmp/hatch-data` and `HATCH_CACHE_DIR=/tmp/hatch-cache`) -- Run all quality gates from inside the active worktree before commit/PR - -Conflict avoidance: - -- Check `openspec/CHANGE_ORDER.md` before creating new parallel branches -- Avoid concurrent branches editing the same `openspec/changes//` directory -- Rebase frequently on `origin/dev` in each worktree -- Use `git worktree list` daily to detect stale or incorrect branch/path attachments - -Local cleanup after merge to `dev`: - -```bash -git fetch origin -git worktree remove ../specfact-cli-worktrees/feature/ -git branch -d feature/ -git worktree prune -``` - -If remote cleanup is needed: - -```bash -git push origin --delete feature/ -``` - -### Developing specfact-cli-modules (IDE dependencies) - -Bundle code in **specfact-cli-modules** imports from `specfact_cli` (models, runtime, validators, etc.). That repo uses **Hatch**: a `pyproject.toml` with optional dependency `.[dev]` pulls in `specfact-cli` from a sibling path (`file://../specfact-cli`). When opening the modules repo in Cursor/VS Code: - -- In **specfact-cli-modules**: run `hatch env create` (with specfact-cli at `../specfact-cli`, or symlink / edit path in pyproject), then in the IDE select **Python: Select Interpreter** β†’ `.venv` in that repo. -- See **specfact-cli-modules** `README.md` β†’ "Local development (IDE / Cursor)" for sibling layout and worktree/symlink options. - -### Pre-Commit Checklist - -Run all steps in order before committing. Every step must pass with no errors. - -1. `hatch run format` # ruff format + autofix -2. `hatch run type-check` # basedpyright strict -3. `hatch run lint` # full lint suite -4. `hatch run yaml-lint` # YAML + markdown validation -5. `hatch run contract-test` # contract-first validation -6. `hatch run smart-test` # targeted test run (use `smart-test-full` for larger modifications) - -With `pre-commit` installed (`pre-commit install`), staged `*.py` / `*.pyi` files also run the local code review gate (`scripts/pre_commit_code_review.py`), which writes the same `.specfact/code-review.json` path. That hook is fast feedback; it does not replace the **PR / change-completion** review rules in the next section when your OpenSpec tasks require a full-scope run. - -### SpecFact Code Review JSON (Dogfood, Quality Gate) - -This matches **`openspec/config.yaml`** (project `context` and **`rules.tasks`** for code review): treat **`.specfact/code-review.json`** as mandatory evidence before an OpenSpec change is considered complete and before you rely on β€œall gates green” for a PR. - -**When to (re)run the review** - -- The file is **missing**, or -- It is **stale**: the report’s last-modified time is older than any file you changed for this work under `src/`, `scripts/`, `tools/`, `tests/`, or under `openspec/changes//` **except** `openspec/changes//TDD_EVIDENCE.md` β€” evidence-only edits there do **not** by themselves invalidate the review; re-run when proposal, specs, tasks, design, or code change. - -**Command** - -```bash -hatch run specfact code review run --json --out .specfact/code-review.json -``` - -- While iterating on a branch, prefer a **changed-files scope** when available (e.g. `--scope changed`) so feedback stays fast. -- Before the **final PR** for a change, run a **full** (or equivalent) scope so the report covers the whole quality surface your tasks expect (e.g. `--scope full`). - -**Remediation** - -- Read the JSON report and fix **every** finding at any severity (warning, advisory, error, or equivalent in the schema) unless the change proposal documents a **rare, explicit, justified** exception. -- After substantive edits, re-run until the report shows a **passing** outcome from the review module (e.g. overall verdict PASS / CI exit 0 per schema). -- Record the review command(s) and timestamp in `openspec/changes//TDD_EVIDENCE.md` or in the PR description when the change touches behavior or quality gates. - -**Consistency** - -- OpenSpec change **`tasks.md`** should include explicit tasks for generating/updating this file and clearing findings (see `openspec/config.yaml` β†’ `rules.tasks` β†’ β€œSpecFact code review JSON”). Agent runs should treat those tasks and this section as the same bar. - -### Clean-Code Review Gate - -specfact-cli enforces the 7-principle clean-code charter through the `specfact code review run` gate. The canonical charter lives in `skills/specfact-code-review/SKILL.md` (in `nold-ai/specfact-cli-modules`). This repo consumes the expanded clean-code categories from that review module: - -| Category | Principle covered | -|----------|-------------------| -| `naming` | Meaningful naming, exception-pattern rules | -| `kiss` | Keep It Simple: LOC, nesting-depth, parameter-count (Phase A: >80 warning / >120 error) | -| `yagni` | You Aren't Gonna Need It: unused-abstraction detection | -| `dry` | Don't Repeat Yourself: clone-detection and duplication checks | -| `solid` | SOLID principles: dependency-role and single-responsibility checks | - -Zero regressions in any of these categories are required before merge. Run the review gate with: - -```bash -hatch run specfact code review run --json --out .specfact/code-review.json -``` - -**Phase A thresholds are active.** Phase B thresholds (>40 / >80 LOC) are deferred to a later cleanup change and are not yet enforced. - -### Module Signature Gate (Required for Change Finalization) - -Before PR creation, every change MUST pass bundled module signature verification: - -1. Run `hatch run ./scripts/verify-modules-signature.py --require-signature`. -2. If verification fails because module contents changed, re-sign affected manifests: - - `hatch run python scripts/sign-modules.py --key-file ` -3. Re-run verification until green. - -Rules: - -- Do not merge/PR with stale or missing integrity metadata for bundled modules. -- Treat signature verification as a quality gate equal to lint/type-check/tests. -- Module version bump is mandatory before signing changed module contents. Do not keep the same module version when module files or signatures change. -- For any module re-sign/sign operation, increment module version using semver (major/minor/patch) so published/registered versions are immutable. -- Use signer/verifier enforcement paths: - - signer rejects changed modules with unchanged version by default; - - verifier/CI enforces version-bump checks for changed manifests. - -### OpenSpec Workflow - -Before modifying application code, **always** verify that an active OpenSpec change in `openspec/changes/` **explicitly covers the requested modification**. This is the spec-driven workflow defined in `openspec/config.yaml`. Skip only when the user explicitly says `"skip openspec"` or `"implement without openspec change"`. - -**Agent MUST NOT apply any code edits** when a fix, change, modification, or edit to any codebase file is requested unless an active OpenSpec change exists that explicitly covers the requested scope. If no such change exists, ask for clarification: - -- **a) New change** β€” create a new OpenSpec change proposal (`/opsx:new`) -- **b) Modify existing** β€” select and continue an existing change in `openspec/changes/` -- **c) Delta** β€” add a targeted delta to an existing change's specs - -The existence of *any* open change is not sufficient β€” the change must specifically address the requested modification. Do not proceed until one of the above is resolved. - -### Hard Gate: Strict TDD Order (Non-Negotiable) - -For any behavior change, the implementation order is mandatory and must be auditable: - -1. Update or add spec deltas first. -2. Add/modify tests next, mapped to spec scenarios. -3. Run tests and capture a **failing** result before implementation. -4. Only then modify production code. -5. Re-run tests and quality gates until passing. - -Required evidence: - -- Create/update `openspec/changes//TDD_EVIDENCE.md` with: - - test command(s) and timestamp for the pre-implementation failing run - - short failure summary - - test command(s) and timestamp for the post-implementation passing run - -Agent enforcement: - -- Agents MUST NOT edit production code for new/changed behavior until failing-test evidence is recorded. -- If this order cannot be followed, stop and ask the user for explicit override before proceeding. - -#### Change Order (`openspec/CHANGE_ORDER.md`) - -`openspec/CHANGE_ORDER.md` is the **single source of truth** for change sequencing, module grouping, and inter-change dependencies. Always use it to avoid redundant analysis of `openspec/changes/` folders. - -**Read it first** β€” before creating, implementing, or archiving any change, consult `CHANGE_ORDER.md` to: -- Check which changes are already archived (implemented) and their dates -- Verify hard blockers are resolved before starting implementation -- Understand where a new change fits in module order and wave sequencing - -**Keep it updated** β€” whenever a change lifecycle event occurs, update `CHANGE_ORDER.md` in the same commit: -- **New change created**: add a row to the correct module group table with folder name, GitHub issue link, and blocked-by dependencies -- **Change archived**: move the entry from "Pending" to "Implemented (archived)" with the archive date; update wave status if a wave is now complete -- **Change modified/renamed**: update the folder name and any affected dependency references -- **Blocker resolved**: update the "Blocked by" column (append βœ… to resolved blockers) - -Use the `specfact-openspec-workflows` skill as the default execution path for OpenSpec lifecycle work. - -- When a Markdown plan exists and the intent is to create a change from that plan, use `.cursor/commands/wf-create-change-from-plan.md` (`/wf-change-from-plan`) to generate the proposal/tasks/spec deltas. -- For plans targeting an internal repository, still run the same workflow but follow its repo rules (for example, skip public GitHub issue creation where required). -- After any change is created or modified, run `.cursor/commands/wf-validate-change.md` (`/wf-validate-change`) and capture its output in `openspec/changes//CHANGE_VALIDATION.md`. -- Treat validation output as required context for dependency and interface impact, including any workflow-provided GitHub issue sync context. - -### Version Updates - -When bumping version, sync across: `pyproject.toml`, `setup.py`, `src/specfact_cli/__init__.py`. CI/CD auto-publishes to PyPI on merge to `main` only if version exceeds the published one. - -**Version semantics (SemVer):** -- `feature/*` branches β†’ **minor** increment (e.g. `0.5.0 β†’ 0.6.0`) -- `bugfix/*` / `hotfix/*` branches β†’ **patch** increment (e.g. `0.5.0 β†’ 0.5.1`) -- Breaking changes or major milestones β†’ **major** increment (requires explicit confirmation) - -Always propose the increment type based on the branch name and ask for confirmation before applying the bump. - -### Changelog - -Keep `CHANGELOG.md` updated with every meaningful change. Update it in the same commit that bumps the version and do not let them diverge. - -- Follow [Keep a Changelog](https://keepachangelog.com/) format: `Added`, `Changed`, `Fixed`, `Removed`, `Security` -- Each version entry must match the version in `pyproject.toml` -- Unreleased changes accumulate under `## [Unreleased]` until a version bump - -### Commits - -Follow Conventional Commits: `feat:`, `fix:`, `docs:`, `test:`, `refactor:`. - -#### Commit Signing (GPG) - -- This repository may enforce signed commits (`commit.gpgsign=true`). -- If an agent-run commit fails with `gpg failed to sign the data` in a non-interactive shell, the agent MUST: - 1. Stage all intended files. - 2. Provide the exact `git commit -S -m ""` command for the user to run locally. - 3. Continue with push/PR steps after the user confirms the signed commit exists. -- Agents MUST NOT bypass signing with `--no-gpg-sign` unless the user explicitly requests that override. - -### Documentation - -Keep docs current with every code change that affects user-facing behaviour. - -- Docs source lives in `docs/` and is published to [docs.specfact.io](https://docs.specfact.io) via GitHub Pages (Jekyll) -- **Preserve all front-matter** on every edit (`title`, `layout`, `nav_order`, `permalink`, etc.) and check `docs/_layouts/default.html` and `docs/index.md` before adding or removing front-matter keys -- When a command, option, or behaviour changes, update the corresponding doc page in the same PR -- Broken or outdated docs for users are P0; prefer a small doc fix over shipping undocumented changes - -### README Maintenance - -`README.md` (repo root) and the docs landing page (`docs/index.md` or `docs/README.md`) must stay in sync with what SpecFact actually does. - -- On larger refactorings or feature additions, reconsider the README from an **external/new-user perspective** and lead with value and USP, not internal architecture -- A first-time reader should understand what SpecFact does, why they'd use it, and how to get started within the first screen -- Do not let the README drift from the actual CLI interface or command list - -## Code Conventions - -- Python 3.11+, line length 120, Google-style docstrings -- `snake_case` for files/modules, `PascalCase` for classes, `UPPER_SNAKE_CASE` for constants -- All data structures use Pydantic `BaseModel` with `Field(...)` and descriptions -- CLI commands use `typer.Typer()` + `rich.console.Console()` -- Only write high-value comments and avoid verbose or redundant commentary -- `rich~=13.5.2` is pinned for semgrep compatibility and should not be upgraded without validation - -## CLI Command Pattern - -```python -import typer -from beartype import beartype -from icontract import require, ensure -from rich.console import Console - -app = typer.Typer() -console = Console() - -@app.command() -@require(lambda repo_path: repo_path.exists(), "Repository path must exist") -@beartype -def my_command( - repo_path: Path = typer.Argument(..., help="Path to repository"), -) -> None: - """Command docstring.""" - console.print("[bold]Processing...[/bold]") -``` - -## Backlog Command Topology - -Keep backlog functionality grouped under the common top-level `backlog` command: - -- `specfact backlog ceremony standup` -- `specfact backlog ceremony refinement` -- `specfact backlog analyze-deps` -- `specfact backlog delta status|impact|cost-estimate|rollback-analysis` -- `specfact backlog verify-readiness` - -Project-scoped orchestration belongs under `project`: - -- `specfact project link-backlog` -- `specfact project health-check` -- `specfact project devops-flow --stage --action <...>` -- `specfact project snapshot|regenerate|export-roadmap` - -## Testing - -**Contract-first approach**: `@icontract` contracts on public APIs are the primary coverage mechanism (target 80%+ API coverage). Redundant unit tests that only assert input validation or type checks should be removed because contracts and beartype already cover them. - -Test structure mirrors source: `tests/unit/`, `tests/integration/`, `tests/e2e/`. Use `@pytest.mark.asyncio` for async tests. Guard environment-sensitive logic with `os.environ.get("TEST_MODE") == "true"`. - -## CI/CD - -Key workflows in `.github/workflows/`: -- `tests.yml` β€” contract-first test execution -- `specfact.yml` β€” contract validation on PR/push (`hatch run specfact repro --verbose`) -- `pr-orchestrator.yml` β€” coordinates PR workflows -- `build-and-push.yml` β€” Docker image building (depends on all above passing) +This file is the mandatory bootstrap governance surface for coding agents working in this repository. It is intentionally compact. The detailed rules that used to live here have been preserved in `docs/agent-rules/` so new sessions do not pay the full context cost up front. + +## Mandatory bootstrap + +1. Read this file. +2. Read [docs/agent-rules/INDEX.md](docs/agent-rules/INDEX.md). +3. Read [docs/agent-rules/05-non-negotiable-checklist.md](docs/agent-rules/05-non-negotiable-checklist.md). +4. Detect repository root, active branch, and worktree state. +5. Reject implementation from the `dev` or `main` checkout unless the user explicitly overrides that rule. +6. If GitHub hierarchy metadata is needed and `.specfact/backlog/github_hierarchy_cache.md` is missing or stale, refresh it with `python scripts/sync_github_hierarchy_cache.py`. +7. Load any additional rule files required by the applicability matrix in [docs/agent-rules/INDEX.md](docs/agent-rules/INDEX.md) before implementation. + +## Precedence + +1. Direct system and developer instructions +2. Explicit user override where repository governance allows it +3. This file +4. [docs/agent-rules/05-non-negotiable-checklist.md](docs/agent-rules/05-non-negotiable-checklist.md) +5. Other selected files under `docs/agent-rules/` +6. Change-local OpenSpec artifacts and workflow notes + +## Non-negotiable gates + +- Work in a git worktree unless the user explicitly overrides that rule. +- Do not implement from the `dev` or `main` checkout by default. +- Treat a provided OpenSpec change id as candidate scope, not automatic permission to proceed. +- Verify the selected change against current repository reality and dependency state before implementation. +- Do not auto-refine stale or ambiguous changes without the user. +- Perform `spec -> tests -> failing evidence -> code -> passing evidence` in that order for behavior changes. +- Require public GitHub metadata completeness before implementation when linked issue workflow applies: parent, labels, project assignment, blockers, and blocked-by relationships. +- If a linked GitHub issue is already `in progress`, pause and ask for clarification before implementation. +- Run the required verification and quality gates for the touched scope before finalization. +- Fix SpecFact code review findings, including warnings, unless a rare explicit exception is documented. +- Treat the clean-code compliance gate as mandatory: the review surface enforces `naming`, `kiss`, `yagni`, `dry`, and `solid` categories and blocks regressions. +- Enforce module signatures and version bumps when signed module assets or manifests are affected. +- Finalize completed OpenSpec changes with `openspec archive ` (see [docs/agent-rules/40-openspec-and-tdd.md](docs/agent-rules/40-openspec-and-tdd.md)); do not manually move change folders under `openspec/changes/archive/`. + +## Strategic context + +Design and dependency context may live in a **sibling internal repository** (for example a checkout of `specfact-cli-internal` beside this repo, with wiki pages under `../specfact-cli-internal/wiki/`). Before designing or scoping a new OpenSpec change, read the wiki paths listed in [docs/agent-rules/40-openspec-and-tdd.md](docs/agent-rules/40-openspec-and-tdd.md#internal-wiki-and-strategic-context) using **absolute paths** to those files. Treat the wiki as read-only context; do not paste or commit wiki bodies into this public repository. Material edits to an **active** OpenSpec change (scope, design, tasks, dependencies) require updating the matching `wiki/sources/.md` in the sibling internal repo when present, then running `wiki_rebuild_graph.py` from that repo’s root; if the sibling checkout is missing, record a follow-up instead of assuming the wiki was updated. After merges that ship OpenSpec or related work, run the internal wiki scripts only after `cd` into that sibling checkout (for example `cd ../specfact-cli-internal && python3 scripts/wiki_openspec_gh_status.py`); do not run them from an arbitrary working directory. Use `wiki_rebuild_graph.py` when frontmatter churn is large, as described in the same section. + +## Canonical rule docs + +- [docs/agent-rules/INDEX.md](docs/agent-rules/INDEX.md) +- [docs/agent-rules/05-non-negotiable-checklist.md](docs/agent-rules/05-non-negotiable-checklist.md) +- [docs/agent-rules/10-session-bootstrap.md](docs/agent-rules/10-session-bootstrap.md) +- [docs/agent-rules/20-repository-context.md](docs/agent-rules/20-repository-context.md) +- [docs/agent-rules/30-worktrees-and-branching.md](docs/agent-rules/30-worktrees-and-branching.md) +- [docs/agent-rules/40-openspec-and-tdd.md](docs/agent-rules/40-openspec-and-tdd.md) +- [docs/agent-rules/50-quality-gates-and-review.md](docs/agent-rules/50-quality-gates-and-review.md) +- [docs/agent-rules/60-github-change-governance.md](docs/agent-rules/60-github-change-governance.md) +- [docs/agent-rules/70-release-commit-and-docs.md](docs/agent-rules/70-release-commit-and-docs.md) +- [docs/agent-rules/80-current-guidance-catalog.md](docs/agent-rules/80-current-guidance-catalog.md) + +Detailed guidance was moved by reference, not removed. If a rule seems missing here, consult the canonical rule docs before assuming the instruction was dropped. diff --git a/CLAUDE.md b/CLAUDE.md index 971f9f99..c4149fbc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,344 +1,13 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +This file is an alias surface for Claude Code. Follow [AGENTS.md](AGENTS.md) as the primary bootstrap contract, then load the canonical governance docs in [docs/agent-rules/INDEX.md](docs/agent-rules/INDEX.md). -## Project Overview +## Claude-specific note -SpecFact CLI is a Python CLI tool for agile DevOps teams. It keeps backlogs, specs, tests, and code in sync with contract-driven development, validation, and enforcement. Built with Typer + Rich, using Hatch as the build system. Python 3.11+. +Claude must treat the canonical rule docs as the source of truth for worktree policy, OpenSpec gating, GitHub completeness checks, TDD order, quality gates, versioning, and documentation rules. Do not rely on this file as a standalone governance handbook. -## Essential Commands +When a sibling internal repository with a `wiki/` tree is present (see **Strategic context** in `AGENTS.md` and [Internal wiki and strategic context](docs/agent-rules/40-openspec-and-tdd.md#internal-wiki-and-strategic-context) in the OpenSpec rule), read those wiki files by absolute path before designing a new OpenSpec change. Keep wiki content out of the public repository. When you materially change an active OpenSpec change, mirror it to `wiki/sources/.md` in that sibling repo (then rebuild the graph per the same rule); if the sibling checkout is missing, record a follow-up instead of assuming the wiki is current. -```bash -# Development environment -pip install -e ".[dev]" -hatch shell +## Clean-code alias -# Format & lint (run after every code change, in this order) -hatch run format # ruff format + fix -hatch run type-check # basedpyright strict mode -hatch run contract-test # contract-first validation (primary) -hatch test --cover -v # full pytest suite - -# Contract-first testing layers -hatch run contract-test-contracts # runtime contract validation only -hatch run contract-test-exploration # CrossHair symbolic execution -hatch run contract-test-scenarios # integration/E2E with contract refs -hatch run contract-test-full # all layers -hatch run contract-test-status # coverage status report - -# Run a single test file -hatch test -- tests/unit/specfact_cli/test_example.py -v - -# Lint subsystems -hatch run lint # full lint suite -hatch run governance # pylint detailed analysis -hatch run yaml-lint # YAML validation -hatch run lint-workflows # GitHub Actions actionlint - -# Code scanning -hatch run scan-all # semgrep analysis -``` - -## Architecture - -### Modular Command Registry with Lazy Loading - -The CLI uses a module package system in `src/specfact_cli/modules/`. Each module is a self-contained package: - -``` -modules/{name}/ - module-package.yaml # metadata: name, version, commands, dependencies - src/{name}/ - __init__.py - main.py # typer.Typer app with command definitions -``` - -The registry (`src/specfact_cli/registry/`) discovers modules at startup but defers imports until a command is actually invoked. `bootstrap.py` registers all modules; `registry.py` manages lazy loading; `module_packages.py` handles discovery from `module-package.yaml` files. - -**Entry flow**: `cli.py:cli_main()` β†’ Typer app with global options β†’ `ProgressiveDisclosureGroup` for help β†’ lazy-loaded command groups from registry. - -### Contract-First Development - -All public APIs must use `@icontract` decorators (`@require`, `@ensure`, `@invariant`) and `@beartype` for runtime type checking. CrossHair discovers counterexamples via symbolic execution. Contracts are the primary validation mechanism; traditional unit tests are secondary. - -### Key Subsystems - -- **`models/`** - Pydantic BaseModel classes for all data structures -- **`parsers/`**, **`analyzers/`** - Code analysis -- **`generators/`** - Code/spec generation using Jinja2 templates from `resources/templates/` -- **`validators/`** - Schema, contract, FSM validation -- **`adapters/`** - Bridge pattern for tool integrations (GitHub, Azure DevOps, Jira, Linear) -- **`modes/`** - Operational modes: CICD (fast, deterministic, non-interactive) vs Copilot (interactive, IDE-aware). Auto-detected from environment. -- **`resources/`** - Bundled prompts, templates, schemas, mappings (force-included in wheel) - -### Logging - -Use `from specfact_cli.common import get_bridge_logger` and avoid `print()` in production command paths. Debug logs go to `~/.specfact/logs/specfact-debug.log` when `--debug` is passed. - -## Development Workflow - -### Branch Protection - -`dev` and `main` are protected. Always work on feature/bugfix/hotfix branches and submit PRs: -- `feature/your-feature-name` -- `bugfix/your-bugfix-name` -- `hotfix/your-hotfix-name` - -### Git Worktree Policy (Parallel Development) - -Use git worktrees for parallel development branches only. - -- Allowed branch types in worktrees: `feature/*`, `bugfix/*`, `hotfix/*`, `chore/*` -- Forbidden in worktrees: `dev`, `main` -- The primary checkout remains the canonical `dev` workspace - -Canonical layout: - -- Primary checkout: `.../specfact-cli` (tracks `dev`) -- Worktrees root: `.../specfact-cli-worktrees//` -- Worktree folder name MUST reflect the branch slug - -Create a new worktree from `origin/dev`: - -```bash -git fetch origin -git worktree add ../specfact-cli-worktrees/feature/ -b feature/ origin/dev -``` - -Attach an existing local branch to a worktree: - -```bash -git fetch origin -git worktree add ../specfact-cli-worktrees/feature/ feature/ -``` - -Operational rules: - -- Never create a worktree for `dev` or `main` -- One branch maps to exactly one worktree path at a time -- Keep branch naming consistent: `/-` -- Keep one active OpenSpec change scope per branch where possible -- Create a separate virtual environment inside each worktree (for example, `.venv/`) -- Bootstrap Hatch once per new worktree before running quality gates: `hatch env create` -- Run quick pre-flight checks from the worktree root: `hatch run smart-test-status` and `hatch run contract-test-status` -- If Hatch cannot write to default home/cache paths, set writable overrides (for example `HATCH_DATA_DIR=/tmp/hatch-data` and `HATCH_CACHE_DIR=/tmp/hatch-cache`) -- Run all quality gates from inside the active worktree before commit/PR - -Conflict avoidance: - -- Check `openspec/CHANGE_ORDER.md` before creating new parallel branches -- Avoid concurrent branches editing the same `openspec/changes//` directory -- Rebase frequently on `origin/dev` in each worktree -- Use `git worktree list` daily to detect stale or incorrect branch/path attachments - -Local cleanup after merge to `dev`: - -```bash -git fetch origin -git worktree remove ../specfact-cli-worktrees/feature/ -git branch -d feature/ -git worktree prune -``` - -If remote cleanup is needed: - -```bash -git push origin --delete feature/ -``` - -### Pre-Commit Checklist - -Run all steps in order before committing. Every step must pass with no errors. - -1. `hatch run format` # ruff format + autofix -2. `hatch run type-check` # basedpyright strict -3. `hatch run lint` # full lint suite -4. `hatch run yaml-lint` # YAML + markdown validation -5. `hatch run contract-test` # contract-first validation -6. `hatch run smart-test` # targeted test run (use `smart-test-full` for larger modifications) - -### Clean-Code Review Gate - -specfact-cli enforces the 7-principle clean-code charter through the `specfact code review run` gate. The canonical charter lives in `skills/specfact-code-review/SKILL.md` (in `nold-ai/specfact-cli-modules`). This repo consumes the expanded clean-code categories from that review module: - -| Category | Principle covered | -|----------|-------------------| -| `naming` | Meaningful naming, exception-pattern rules | -| `kiss` | Keep It Simple: LOC, nesting-depth, parameter-count (Phase A: >80 warning / >120 error) | -| `yagni` | You Aren't Gonna Need It: unused-abstraction detection | -| `dry` | Don't Repeat Yourself: clone-detection and duplication checks | -| `solid` | SOLID principles: dependency-role and single-responsibility checks | - -Zero regressions in any of these categories are required before merge. Run the review gate with: - -```bash -hatch run specfact code review run --json --out .specfact/code-review.json -``` - -**Phase A thresholds are active.** Phase B thresholds (>40 / >80 LOC) are deferred to a later cleanup change and are not yet enforced. - -### OpenSpec Workflow - -Before modifying application code, **always** verify that an active OpenSpec change in `openspec/changes/` **explicitly covers the requested modification**. This is the spec-driven workflow defined in `openspec/config.yaml`. Skip only when the user explicitly says `"skip openspec"` or `"implement without openspec change"`. - -**Claude MUST NOT apply any code edits** when a fix, change, modification, or edit to any codebase file is requested unless an active OpenSpec change exists that explicitly covers the requested scope. If no such change exists, ask for clarification: - -- **a) New change** β€” create a new OpenSpec change proposal (`/opsx:new`) -- **b) Modify existing** β€” select and continue an existing change in `openspec/changes/` -- **c) Delta** β€” add a targeted delta to an existing change's specs - -The existence of *any* open change is not sufficient β€” the change must specifically address the requested modification. Do not proceed until one of the above is resolved. - -Use the `specfact-openspec-workflows` skill as the default execution path for OpenSpec lifecycle work. - -- When a Markdown plan exists and the intent is to create a change from that plan, use `.cursor/commands/wf-create-change-from-plan.md` (`/wf-change-from-plan`) to generate the proposal/tasks/spec deltas. -- For plans targeting an internal repository, still run the same workflow but follow its repo rules (for example, skip public GitHub issue creation where required). -- After any change is created or modified, run `.cursor/commands/wf-validate-change.md` (`/wf-validate-change`) and capture its output in `openspec/changes//CHANGE_VALIDATION.md`. -- Treat validation output as required context for dependency and interface impact, including any workflow-provided GitHub issue sync context. - -### Hard Gate: Strict TDD Order (Non-Negotiable) - -For any behavior change, the implementation order is mandatory and must be auditable: - -1. Update or add spec deltas first. -2. Add/modify tests next, mapped to spec scenarios. -3. Run tests and capture a **failing** result before implementation. -4. Only then modify production code. -5. Re-run tests and quality gates until passing. - -Required evidence: - -- Create/update `openspec/changes//TDD_EVIDENCE.md` with: - - test command(s) and timestamp for the pre-implementation failing run - - short failure summary - - test command(s) and timestamp for the post-implementation passing run - -Claude enforcement: - -- Claude MUST NOT edit production code for new/changed behavior until failing-test evidence is recorded. -- If this order cannot be followed, stop and ask the user for explicit override before proceeding. - -#### Change Order (`openspec/CHANGE_ORDER.md`) - -`openspec/CHANGE_ORDER.md` is the **single source of truth** for change sequencing, module grouping, and inter-change dependencies. Always use it to avoid redundant analysis of `openspec/changes/` folders. - -**Read it first** β€” before creating, implementing, or archiving any change, consult `CHANGE_ORDER.md` to: -- Check which changes are already archived (implemented) and their dates -- Verify hard blockers are resolved before starting implementation -- Understand where a new change fits in module order and wave sequencing - -**Keep it updated** β€” whenever a change lifecycle event occurs, update `CHANGE_ORDER.md` in the same commit: -- **New change created**: add a row to the correct module group table with folder name, GitHub issue link, and blocked-by dependencies -- **Change archived**: move the entry from "Pending" to "Implemented (archived)" with the archive date; update wave status if a wave is now complete -- **Change modified/renamed**: update the folder name and any affected dependency references -- **Blocker resolved**: update the "Blocked by" column (append βœ… to resolved blockers) - -### Version Updates - -When bumping version, sync across: `pyproject.toml`, `setup.py`, `src/specfact_cli/__init__.py`. CI/CD auto-publishes to PyPI on merge to `main` only if version exceeds the published one. - -**Version semantics (SemVer):** -- `feature/*` branches β†’ **minor** increment (e.g. `0.5.0 β†’ 0.6.0`) -- `bugfix/*` / `hotfix/*` branches β†’ **patch** increment (e.g. `0.5.0 β†’ 0.5.1`) -- Breaking changes or major milestones β†’ **major** increment (requires explicit confirmation) - -Always propose the increment type based on the branch name and ask for confirmation before applying the bump. - -### Changelog - -Keep `CHANGELOG.md` updated with every meaningful change. Update it in the same commit that bumps the version β€” never let them diverge. - -- Follow [Keep a Changelog](https://keepachangelog.com/) format: `Added`, `Changed`, `Fixed`, `Removed`, `Security` -- Each version entry must match the version in `pyproject.toml` -- Unreleased changes accumulate under `## [Unreleased]` until a version bump - -### Commits - -Follow Conventional Commits: `feat:`, `fix:`, `docs:`, `test:`, `refactor:`. - -#### Commit Signing (GPG) - -- This repository may enforce signed commits (`commit.gpgsign=true`). -- If an agent-run commit fails with `gpg failed to sign the data` in a non-interactive shell, Claude MUST: - 1. Stage all intended files. - 2. Provide the exact `git commit -S -m ""` command for the user to run locally. - 3. Continue with push/PR steps after the user confirms the signed commit exists. -- Claude MUST NOT bypass signing with `--no-gpg-sign` unless the user explicitly requests that override. - -### Documentation - -Keep docs current with every code change that affects user-facing behaviour. - -- Docs source lives in `docs/` and is published to [docs.specfact.io](https://docs.specfact.io) via GitHub Pages (Jekyll) -- **Preserve all front-matter** on every edit (`title`, `layout`, `nav_order`, `permalink`, etc.) β€” check `docs/_layouts/default.html` and `docs/index.md` before adding or removing front-matter keys -- When a command, option, or behaviour changes, update the corresponding doc page in the same PR -- Broken or outdated docs for users are P0 β€” prefer a small doc fix over shipping undocumented changes - -### README Maintenance - -`README.md` (repo root) and the docs landing page (`docs/index.md` or `docs/README.md`) must stay in sync with what SpecFact actually does. - -- On larger refactorings or feature additions, reconsider the README from an **external / new-user perspective**: lead with value and USP, not internal architecture -- A first-time reader should understand what SpecFact does, why they'd use it, and how to get started within the first screen -- Do not let the README drift from the actual CLI interface or command list - -## Backlog Command Topology - -Keep backlog functionality grouped under the common top-level `backlog` command: - -- `specfact backlog ceremony standup` -- `specfact backlog ceremony refinement` -- `specfact backlog analyze-deps` -- `specfact backlog delta status|impact|cost-estimate|rollback-analysis` -- `specfact backlog verify-readiness` - -Project-scoped orchestration belongs under `project`: - -- `specfact project link-backlog` -- `specfact project health-check` -- `specfact project devops-flow --stage --action <...>` -- `specfact project snapshot|regenerate|export-roadmap` - -## Code Conventions - -- Python 3.11+, line length 120, Google-style docstrings -- `snake_case` for files/modules, `PascalCase` for classes, `UPPER_SNAKE_CASE` for constants -- All data structures use Pydantic `BaseModel` with `Field(...)` and descriptions -- CLI commands use `typer.Typer()` + `rich.console.Console()` -- Only write high-value comments; avoid verbose or redundant commentary -- `rich~=13.5.2` is pinned for semgrep compatibility β€” do not upgrade without checking - -## CLI Command Pattern - -```python -import typer -from beartype import beartype -from icontract import require, ensure -from rich.console import Console - -app = typer.Typer() -console = Console() - -@app.command() -@require(lambda repo_path: repo_path.exists(), "Repository path must exist") -@beartype -def my_command( - repo_path: Path = typer.Argument(..., help="Path to repository"), -) -> None: - """Command docstring.""" - console.print("[bold]Processing...[/bold]") -``` - -## Testing - -**Contract-first approach**: `@icontract` contracts on public APIs are the primary coverage mechanism (target 80%+ API coverage). Redundant unit tests that merely assert input validation or type checks should be removed β€” contracts and beartype handle that. - -Test structure mirrors source: `tests/unit/`, `tests/integration/`, `tests/e2e/`. Use `@pytest.mark.asyncio` for async tests. Guard environment-sensitive logic with `os.environ.get("TEST_MODE") == "true"`. - -## CI/CD - -Key workflows in `.github/workflows/`: -- `tests.yml` β€” contract-first test execution -- `specfact.yml` β€” contract validation on PR/push (`hatch run specfact repro --verbose`) -- `pr-orchestrator.yml` β€” coordinates PR workflows -- `build-and-push.yml` β€” Docker image building (depends on all above passing) +Claude must preserve the clean-code compliance gate and its category references. The canonical review surface enforces `naming`, `kiss`, `yagni`, `dry`, and `solid` and treats clean-code regressions as blocking until they are fixed or explicitly justified. diff --git a/MEMORY.md b/MEMORY.md index d0621eba..83603631 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -5,6 +5,7 @@ This file survives context compression. Re-read on every interaction. ## 1. OpenSpec Gate (HARD BLOCK) **Do NOT edit any codebase file** unless an active OpenSpec change in `openspec/changes/` explicitly covers the requested scope. If none exists, stop and ask: + - a) Create new change (`/opsx:new`) - b) Continue an existing change - c) Add a delta to an existing change @@ -16,6 +17,7 @@ Read `openspec/CHANGE_ORDER.md` first β€” it is the single source of truth for s ## 2. Branch Protection `dev` and `main` are protected. Never commit directly. Always use: + - `feature/` β†’ minor version bump - `bugfix/` β†’ patch version bump - `hotfix/` β†’ patch version bump diff --git a/docs/.doc-frontmatter-enforced b/docs/.doc-frontmatter-enforced index bfca20a4..956e6474 100644 --- a/docs/.doc-frontmatter-enforced +++ b/docs/.doc-frontmatter-enforced @@ -6,3 +6,4 @@ docs/core-cli/init.md docs/reference/documentation-url-contract.md docs/contributing/docs-sync.md docs/contributing/frontmatter-schema.md +docs/agent-rules/*.md diff --git a/docs/README.md b/docs/README.md index f0bd4b8c..b5f14266 100644 --- a/docs/README.md +++ b/docs/README.md @@ -80,11 +80,13 @@ Preferred backlog workflow entrypoints: Compatibility note: `specfact backlog daily ...` and `specfact backlog refine ...` remain available, but the ceremony forms are the preferred command path. What the backlog ceremony and readiness commands do in practice: + - Converts team working agreements (DoR, DoD, flow/PI readiness) into deterministic checks. - Flags exact readiness gaps per backlog item with actionable evidence pointers. - Runs structured ceremony workflows against live backlog data. Start with: + - `specfact backlog ceremony standup --help` - `specfact backlog verify-readiness --bundle ` - `specfact backlog refine --help` diff --git a/docs/_data/nav.yml b/docs/_data/nav.yml index 0f944347..0eb2f4b6 100644 --- a/docs/_data/nav.yml +++ b/docs/_data/nav.yml @@ -91,6 +91,27 @@ url: /guides/openspec-journey/ expertise: [intermediate] +- section: Agent Governance + items: + - title: Rule Index + url: /contributing/agent-rules/ + expertise: [advanced] + - title: Non-Negotiable Checklist + url: /contributing/agent-rules/non-negotiable-checklist/ + expertise: [advanced] + - title: Session Bootstrap + url: /contributing/agent-rules/session-bootstrap/ + expertise: [advanced] + - title: OpenSpec and TDD + url: /contributing/agent-rules/openspec-and-tdd/ + expertise: [advanced] + - title: GitHub Change Governance + url: /contributing/agent-rules/github-change-governance/ + expertise: [advanced] + - title: Quality Gates and Review + url: /contributing/agent-rules/quality-gates-and-review/ + expertise: [advanced] + - section: Integrations items: - title: Integrations Overview diff --git a/docs/adapters/azuredevops.md b/docs/adapters/azuredevops.md index 7191e92d..6aab948a 100644 --- a/docs/adapters/azuredevops.md +++ b/docs/adapters/azuredevops.md @@ -6,7 +6,6 @@ permalink: /adapters/azuredevops/ # Azure DevOps Adapter - > Modules docs handoff: this page remains in the core docs set as release-line overview content. > Canonical bundle-specific deep guidance now lives in the canonical modules docs site, currently > published at `https://modules.specfact.io/`. diff --git a/docs/adapters/github.md b/docs/adapters/github.md index 6f3059e5..2e30a674 100644 --- a/docs/adapters/github.md +++ b/docs/adapters/github.md @@ -6,7 +6,6 @@ permalink: /adapters/github/ # GitHub Adapter - > Modules docs handoff: this page remains in the core docs set as release-line overview content. > Canonical bundle-specific deep guidance now lives in the canonical modules docs site, currently > published at `https://modules.specfact.io/`. diff --git a/docs/agent-rules/05-non-negotiable-checklist.md b/docs/agent-rules/05-non-negotiable-checklist.md new file mode 100644 index 00000000..7fcba18c --- /dev/null +++ b/docs/agent-rules/05-non-negotiable-checklist.md @@ -0,0 +1,52 @@ +--- +layout: default +title: Agent non-negotiable checklist +permalink: /contributing/agent-rules/non-negotiable-checklist/ +description: Always-load SHALL gates that apply to every implementation session in the repository. +keywords: [agents, governance, checklist, tdd, worktree] +audience: [team, enterprise] +expertise_level: [advanced] +doc_owner: specfact-cli +tracks: + - AGENTS.md + - docs/agent-rules/** + - openspec/CHANGE_ORDER.md + - openspec/config.yaml +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +id: agent-rules-non-negotiable-checklist +always_load: true +applies_when: + - session-bootstrap + - implementation +priority: 5 +blocking: true +user_interaction_required: true +stop_conditions: + - main checkout implementation attempted without override + - no valid OpenSpec change covers requested modification + - stale or ambiguous change requires refinement + - failing-before evidence missing for behavior change +depends_on: + - agent-rules-index +--- + +# Agent non-negotiable checklist + +- SHALL work in a git worktree unless the user explicitly overrides that rule. +- SHALL not implement from the `dev` or `main` checkout by default. +- SHALL treat a provided OpenSpec change id as candidate scope, not automatic permission to proceed. +- SHALL verify selected change validity against current repository reality and dependency state before implementation. +- SHALL not auto-refine stale, superseded, or ambiguous changes without the user. +- SHALL consult `openspec/CHANGE_ORDER.md` before creating, implementing, or archiving a change. +- SHALL finalize completed OpenSpec changes with `openspec archive ` (OpenSpec CLI) and SHALL NOT relocate `openspec/changes//` by hand. +- SHALL keep the internal wiki mirror in `specfact-cli-internal/wiki/sources/.md` aligned when an active change’s story, scope, or dependencies change (see `docs/agent-rules/40-openspec-and-tdd.md`), or SHALL record an explicit follow-up when the sibling internal checkout is unavailable. +- SHALL consult `.specfact/backlog/github_hierarchy_cache.md` before manual GitHub hierarchy lookup and SHALL refresh it when missing or stale. +- SHALL require public GitHub metadata completeness before implementation when linked issue workflow applies: parent, labels, project assignment, blockers, and blocked-by relationships. +- SHALL check whether a linked GitHub issue is already `in progress` and SHALL pause for clarification if concurrent work is possible. +- SHALL perform `spec -> tests -> failing evidence -> code -> passing evidence` in that order for behavior changes. +- SHALL run required verification and quality gates for the touched scope before finalization. +- SHALL fix SpecFact code review findings, including warnings, unless a rare and explicit exception is documented. +- SHALL enforce module signatures and version bumps when signed module assets or manifests are affected. +- SHALL preserve existing instructions by moving them to canonical rule files before shortening the bootstrap surfaces. diff --git a/docs/agent-rules/10-session-bootstrap.md b/docs/agent-rules/10-session-bootstrap.md new file mode 100644 index 00000000..19b0bc5d --- /dev/null +++ b/docs/agent-rules/10-session-bootstrap.md @@ -0,0 +1,54 @@ +--- +layout: default +title: Agent session bootstrap +permalink: /contributing/agent-rules/session-bootstrap/ +description: Deterministic startup sequence for repository sessions after AGENTS.md is loaded. +keywords: [agents, bootstrap, worktree, cache, instructions] +audience: [team, enterprise] +expertise_level: [advanced] +doc_owner: specfact-cli +tracks: + - AGENTS.md + - docs/agent-rules/** + - .specfact/backlog/github_hierarchy_cache.md + - scripts/sync_github_hierarchy_cache.py +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +id: agent-rules-session-bootstrap +always_load: true +applies_when: + - session-bootstrap +priority: 10 +blocking: true +user_interaction_required: true +stop_conditions: + - unsupported branch or worktree context + - cache-dependent GitHub work without refreshed hierarchy cache +depends_on: + - agent-rules-index + - agent-rules-non-negotiable-checklist +--- + +# Agent session bootstrap + +## Required startup checks + +1. Detect repository root, active branch, and whether the session is running in a worktree. +2. If the session is on `dev` or `main`, do not implement until the user explicitly allows it or a worktree is created. +3. Confirm `AGENTS.md` is already loaded, then load the rule index and non-negotiable checklist. +4. Determine whether the task is read-only, artifact-only, or implementation work. +5. If GitHub hierarchy data is required, confirm `.specfact/backlog/github_hierarchy_cache.md` is present and fresh enough for the task. +6. If the cache is missing or stale, refresh it with `python scripts/sync_github_hierarchy_cache.py`. +7. Load the additional rule files required by the task signal from the index. + +## Stop and continue behavior + +- If the session is on the main checkout and the user did not override, stop implementation and create or switch to a worktree. +- If the requested work is tied to stale or ambiguous change metadata, continue only in read-only investigation mode until the user clarifies. +- If GitHub hierarchy metadata is needed and the cache cannot answer after refresh, manual GitHub lookup is allowed. +- If the task is purely explanatory or read-only, full implementation gates do not need to run. + +## Why this file exists + +This file keeps session bootstrap deterministic after `AGENTS.md` becomes compact. It is small enough to load every time, but specific enough to prevent drift across models and sessions. diff --git a/docs/agent-rules/20-repository-context.md b/docs/agent-rules/20-repository-context.md new file mode 100644 index 00000000..ee8f5a7d --- /dev/null +++ b/docs/agent-rules/20-repository-context.md @@ -0,0 +1,67 @@ +--- +layout: default +title: Agent repository context +permalink: /contributing/agent-rules/repository-context/ +description: Project overview, key commands, architecture, and logging guidance preserved from the previous AGENTS.md. +keywords: [agents, commands, architecture, logging, project-overview] +audience: [team, enterprise] +expertise_level: [intermediate, advanced] +doc_owner: specfact-cli +tracks: + - AGENTS.md + - docs/agent-rules/** + - src/specfact_cli/** + - pyproject.toml +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +id: agent-rules-repository-context +always_load: false +applies_when: + - repository-orientation + - command-lookup +priority: 20 +blocking: false +user_interaction_required: false +stop_conditions: + - none +depends_on: + - agent-rules-index +--- + +# Agent repository context + +## Project overview + +SpecFact CLI is a Python CLI tool for agile DevOps teams. It keeps backlogs, specs, tests, and code in sync with contract-driven development, validation, and enforcement. It is built with Typer and Rich, uses Hatch as the build system, and targets Python 3.12 (supports Python 3.11+ at runtime). + +## Essential commands + +```bash +pip install -e ".[dev]" +hatch shell +hatch run format +hatch run type-check +hatch run contract-test +hatch test --cover -v +hatch run lint +hatch run yaml-lint +hatch run lint-workflows +hatch run scan-all +hatch run specfact code review run --json --out .specfact/code-review.json +``` + +## Architecture + +- Modular command registry with lazy loading under `src/specfact_cli/modules/` +- Pydantic models in `models/` +- Parsers and analyzers in `parsers/` and `analyzers/` +- Jinja2-backed generation under `generators/` +- Validation logic under `validators/` +- Tool integrations through bridge adapters in `adapters/` +- Operational modes under `modes/` +- Templates, prompts, schemas, and mappings under `resources/` + +## Logging + +Use `from specfact_cli.common import get_bridge_logger` in production paths and avoid `print()` in `src/`. Debug logs go to `~/.specfact/logs/specfact-debug.log` when `--debug` is passed. diff --git a/docs/agent-rules/30-worktrees-and-branching.md b/docs/agent-rules/30-worktrees-and-branching.md new file mode 100644 index 00000000..580bf520 --- /dev/null +++ b/docs/agent-rules/30-worktrees-and-branching.md @@ -0,0 +1,56 @@ +--- +layout: default +title: Agent worktrees and branching +permalink: /contributing/agent-rules/worktrees-and-branching/ +description: Branch protection, worktree policy, and conflict-avoidance rules for implementation work. +keywords: [agents, worktrees, git, branching, conflicts] +audience: [team, enterprise] +expertise_level: [advanced] +doc_owner: specfact-cli +tracks: + - AGENTS.md + - docs/agent-rules/** + - scripts/worktree.sh + - openspec/CHANGE_ORDER.md +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +id: agent-rules-worktrees-and-branching +always_load: false +applies_when: + - implementation + - branch-management +priority: 30 +blocking: true +user_interaction_required: true +stop_conditions: + - implementation requested from dev or main without override + - conflicting worktree ownership detected +depends_on: + - agent-rules-index + - agent-rules-non-negotiable-checklist +--- + +# Agent worktrees and branching + +## Branch protection + +`dev` and `main` are protected. Work on `feature/*`, `bugfix/*`, `hotfix/*`, or `chore/*` branches and submit PRs to `dev`. + +## Worktree policy + +- The primary checkout remains the canonical `dev` workspace. +- Use `scripts/worktree.sh create /` to create implementation worktrees (`feature`, `bugfix`, `hotfix`, or `chore` for ``). +- Never create a worktree for `dev` or `main`. +- One branch maps to one worktree path at a time. +- Keep one active OpenSpec change scope per branch where possible. +- Create a dedicated virtual environment inside each worktree. +- Bootstrap Hatch once per new worktree with `hatch env create`. +- Run quick pre-flight checks from the worktree root with `hatch run smart-test-status` and `hatch run contract-test-status`. + +## Conflict avoidance + +- Check `openspec/CHANGE_ORDER.md` before creating a new worktree. +- Avoid concurrent branches editing the same `openspec/changes//` directory. +- Rebase frequently on `origin/dev`. +- Use `git worktree list` to detect stale or incorrect attachments. diff --git a/docs/agent-rules/40-openspec-and-tdd.md b/docs/agent-rules/40-openspec-and-tdd.md new file mode 100644 index 00000000..5103a4c2 --- /dev/null +++ b/docs/agent-rules/40-openspec-and-tdd.md @@ -0,0 +1,120 @@ +--- +layout: default +title: Agent OpenSpec and TDD +permalink: /contributing/agent-rules/openspec-and-tdd/ +description: OpenSpec selection, change validation, and strict TDD order for behavior changes. +keywords: [agents, openspec, tdd, change-validation, evidence] +audience: [team, enterprise] +expertise_level: [advanced] +doc_owner: specfact-cli +tracks: + - AGENTS.md + - openspec/config.yaml + - openspec/CHANGE_ORDER.md + - docs/agent-rules/** +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +id: agent-rules-openspec-and-tdd +always_load: false +applies_when: + - implementation + - openspec-change-selection +priority: 40 +blocking: true +user_interaction_required: true +stop_conditions: + - no valid OpenSpec change + - change stale or superseded + - failing-before evidence missing +depends_on: + - agent-rules-index + - agent-rules-non-negotiable-checklist +--- + +# Agent OpenSpec and TDD + +## OpenSpec workflow + +- Before modifying application code, verify that an active OpenSpec change explicitly covers the requested modification. +- Skip only when the user explicitly says `skip openspec` or `implement without openspec change`. +- The existence of any open change is not sufficient; the change must cover the requested scope. +- If no change exists, clarify whether the work needs a new change, a modified existing change, or a delta. + +## Internal wiki and strategic context + +When a **sibling internal repository** is available on disk (typical layout: this repository’s parent directory also contains `specfact-cli-internal`, so the internal wiki resolves under `../specfact-cli-internal/wiki/` relative to this repo root), use that wiki as **read-only** input for design decisions. Do not copy wiki content into this public tree. + +Before **designing or materially scoping** a new OpenSpec change, read these wiki files when they exist: resolve each wiki-relative path against the sibling checkout so you open an **absolute path** on the host (for example join this repository root with `../specfact-cli-internal/wiki/hot.md` and canonicalize) and read the file directly: + +- `wiki/hot.md` for current blocker state +- `wiki/graph.md` to see what the change unblocks or depends on +- The relevant concept page under `wiki/concepts/` (for example `wiki/concepts/clean-code-principles.md`) + +If the sibling checkout or a given wiki file is missing, continue without it for **reading** design context; see **When the internal wiki checkout is missing** below for **writing** the wiki mirror. + +### Wiki mirror for active OpenSpec changes (`wiki/sources/.md`) + +Whenever you change an **active** `openspec/changes//` change in a way that would mislead someone who only reads the internal wikiβ€”for example you edit `proposal.md` (scope, **Why**, **What changes**), `design.md`, `tasks.md` story or ordering, or dependency / prerequisite textβ€”you must also update the matching page in the internal wiki: + +- **Path (sibling checkout):** `../specfact-cli-internal/wiki/sources/.md` (resolve to an absolute path when editing). +- **Update at least:** `depends-on`, `blocks`, `external-deps`, and `status` so they match the change; adjust the short summary if the intent shifted. Align any other mirrored fields that page uses. +- **Then**, with the internal repo as cwd: + +```bash +cd ../specfact-cli-internal && python3 scripts/wiki_rebuild_graph.py +``` + +**Carve-out:** You do **not** need to edit the wiki for every checkbox tick or typo-only editsβ€”only when the **story**, **scope**, or **dependency picture** changes. + +### When the internal wiki checkout is missing + +If `specfact-cli-internal` is **not** available in the expected sibling layout, **do not** imply the wiki was updated. Instead: + +- Add an explicit **merge checklist item** or **follow-up** such as: update `wiki/sources/.md` (depends-on / blocks / external-deps / status / summary) and run `python3 scripts/wiki_rebuild_graph.py` from the `specfact-cli-internal` repository root. + +### Internal wiki maintenance (sibling `specfact-cli-internal`) + +Automation for the internal wiki lives in the **sibling checkout** (`../specfact-cli-internal/` relative to this repo root), not in this repository. The scripts assume the **process current working directory is the internal repository root**; running them from `specfact-cli` or any other directory breaks path resolution and script logic. + +When that checkout exists and you **ship work** (for example after merges to `dev` or `main` that change OpenSpec or GitHub tracking), **change directory into the internal checkout first**, then invoke Python (from this repo’s root, a typical pattern is): + +```bash +cd ../specfact-cli-internal && python3 scripts/wiki_openspec_gh_status.py +``` + +If you changed **many** `docs/agent-rules/` frontmatter blocks or other wiki-linked metadata, also run: + +```bash +cd ../specfact-cli-internal && python3 scripts/wiki_rebuild_graph.py +``` + +If your shell’s cwd is not this public repo root, use the correct relative or absolute path to the internal checkout instead of `../specfact-cli-internal`. + +Do not copy script output or wiki bodies into this public tree; commit wiki updates only in the internal repository. + +## Change validity + +- Never implement from a change id alone. +- Revalidate the selected change against current repository reality, dependency state, and possible superseding work. +- Use `openspec validate --strict` and, where appropriate, the workflow validator to capture dependency and interface impact. + +## Strict TDD order + +1. Update or add spec deltas first. +2. Add or modify tests mapped to spec scenarios. +3. Run tests and capture a failing result before production edits. +4. Only then modify production code. +5. Re-run tests and quality gates until passing. + +## Evidence + +Record the failing-before and passing-after runs in `openspec/changes//TDD_EVIDENCE.md`. Behavior work is blocked until failing-first evidence exists. + +## Archive after merge (mandatory) + +- When a change is implemented and merged (or otherwise complete), **finalize it only with the OpenSpec CLI** from the repository root: `openspec archive `. +- The CLI merges delta specs into `openspec/specs/` and moves the change to `openspec/changes/archive/-/`. **Do not** manually `mv` or rename `openspec/changes//` into `archive/`. +- Use `-y` / `--yes` only when automation must skip confirmation prompts; default is interactive confirmation. +- Reserve `openspec archive --skip-specs` for rare cases with no spec deltas (for example pure infrastructure or confirmed doc-only closes) with explicit user agreement. +- Avoid `--no-validate` unless the user explicitly accepts that trade-off; default is validated archive. diff --git a/docs/agent-rules/50-quality-gates-and-review.md b/docs/agent-rules/50-quality-gates-and-review.md new file mode 100644 index 00000000..373e94cb --- /dev/null +++ b/docs/agent-rules/50-quality-gates-and-review.md @@ -0,0 +1,68 @@ +--- +layout: default +title: Agent quality gates and review +permalink: /contributing/agent-rules/quality-gates-and-review/ +description: Required formatting, typing, contract, review, and signature gates for touched scope. +keywords: [agents, quality, review, contracts, signatures] +audience: [team, enterprise] +expertise_level: [advanced] +doc_owner: specfact-cli +tracks: + - AGENTS.md + - pyproject.toml + - scripts/check_doc_frontmatter.py + - scripts/pre_commit_code_review.py + - scripts/verify-modules-signature.py + - docs/agent-rules/** +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +id: agent-rules-quality-gates-and-review +always_load: false +applies_when: + - implementation + - verification + - finalization +priority: 50 +blocking: true +user_interaction_required: false +stop_conditions: + - required quality gate failed + - specfact code review findings unresolved + - module signature verification failed +depends_on: + - agent-rules-index + - agent-rules-openspec-and-tdd +--- + +# Agent quality gates and review + +## Pre-commit order + +1. `hatch run format` +2. `hatch run type-check` +3. `hatch run lint` +4. `hatch run yaml-lint` +5. `hatch run contract-test` +6. `hatch run smart-test` + +## SpecFact code review JSON + +- Treat `.specfact/code-review.json` as mandatory evidence before an OpenSpec change is complete. +- Re-run the review when the report is missing or stale. +- Resolve every finding at any severity unless a rare, explicit exception is documented. +- Record the review command and timestamps in `TDD_EVIDENCE.md` or the PR description when quality gates are part of the change. + +## Clean-code review gate + +The repository enforces the clean-code charter through `specfact code review run`. Zero regressions in `naming`, `kiss`, `yagni`, `dry`, and `solid` are required before merge. + +## Module signature gate + +Before PR creation, every change that affects signed module assets or manifests must pass: + +```bash +hatch run ./scripts/verify-modules-signature.py --require-signature +``` + +If verification fails because module contents changed, re-sign the affected manifests and bump the module version before re-running verification. diff --git a/docs/agent-rules/60-github-change-governance.md b/docs/agent-rules/60-github-change-governance.md new file mode 100644 index 00000000..f80903dc --- /dev/null +++ b/docs/agent-rules/60-github-change-governance.md @@ -0,0 +1,70 @@ +--- +layout: default +title: Agent GitHub change governance +permalink: /contributing/agent-rules/github-change-governance/ +description: Cache-first GitHub issue governance for parent lookup, metadata completeness, and concurrency ambiguity checks. +keywords: [agents, github, hierarchy-cache, blockers, labels, project] +audience: [team, enterprise] +expertise_level: [advanced] +doc_owner: specfact-cli +tracks: + - AGENTS.md + - openspec/CHANGE_ORDER.md + - scripts/sync_github_hierarchy_cache.py + - .specfact/backlog/github_hierarchy_cache.md + - docs/agent-rules/** +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +id: agent-rules-github-change-governance +always_load: false +applies_when: + - github-public-work + - change-readiness +priority: 60 +blocking: true +user_interaction_required: true +stop_conditions: + - parent or blocker metadata missing + - labels or project assignment missing + - linked issue already in progress +depends_on: + - agent-rules-index + - agent-rules-session-bootstrap + - agent-rules-openspec-and-tdd +--- + +# Agent GitHub change governance + +## Hierarchy cache + +`.specfact/backlog/github_hierarchy_cache.md` is the local lookup source for current Epic and Feature hierarchy metadata in this repository. It is ephemeral local state and must not be committed. + +- Consult the cache first before creating a new change issue, syncing an existing change, or resolving parent or blocker metadata. +- If the cache is missing or stale, rerun `python scripts/sync_github_hierarchy_cache.py`. +- Use manual GitHub lookup only when the cache cannot answer the question after refresh. + +### `specfact-cli-modules` (not yet mirrored) + +The **`specfact-cli-modules`** repository does **not** yet ship `scripts/sync_github_hierarchy_cache.py` or the same `.specfact/backlog/*.md` / `.specfact/backlog/github_hierarchy_cache_state.json` layout as this repo. + +**TODO (modules repo):** Add a compatible `sync_github_hierarchy_cache.py` that produces the **same markdown structure** and **fingerprint/state JSON** (including `github_hierarchy_cache_state.json`) and follows the same **cache-first** rules described here and in `openspec/specs/github-hierarchy-cache/spec.md`, so shared tooling can rely on **identical ordering** and **deterministic rendering**. + +## Public-work readiness checks + +Before implementation on a publicly tracked change issue: + +- When the hierarchy cache file exists, ensure it is **fresh** for live issue-state checks: if `github_hierarchy_cache.md` (or its companion state) is **older than about five minutes** or you cannot tell when it was generated, run `python scripts/sync_github_hierarchy_cache.py` before trusting cached labels or hierarchy for blocking decisions. +- Verify the linked issue exists. +- Verify its parent relationship is correct against current cache-backed GitHub reality. +- Verify required labels are present. +- Verify project assignment is present. +- Verify blockers and blocked-by relationships are complete. + +## Concurrency ambiguity + +If the linked GitHub issue appears to be `in progress`, **do not** treat that as blocking until you have a **current** view of GitHub state: + +1. If `.specfact/backlog/github_hierarchy_cache.md` is missing, or was last updated **more than about five minutes ago** (or freshness is unknown), run `python scripts/sync_github_hierarchy_cache.py` so local metadata is up to date for this repository. +2. Re-read the issue state from GitHub (for example via `gh issue view` or the refreshed cache-backed workflow your session uses) and confirm the issue is **still** `in progress`. +3. Only **after** that verification, if it remains `in progress`, treat it as a blocking ambiguity: pause implementation and ask the user to clarify whether the change is already being worked in another session. Read-only investigation may continue while implementation remains blocked. diff --git a/docs/agent-rules/70-release-commit-and-docs.md b/docs/agent-rules/70-release-commit-and-docs.md new file mode 100644 index 00000000..89536027 --- /dev/null +++ b/docs/agent-rules/70-release-commit-and-docs.md @@ -0,0 +1,80 @@ +--- +layout: default +title: Agent release, commit, and docs rules +permalink: /contributing/agent-rules/release-commit-and-docs/ +description: Versioning, changelog, documentation, README, and commit-signing rules preserved from the previous AGENTS.md. +keywords: [agents, versioning, changelog, docs, commits] +audience: [team, enterprise] +expertise_level: [advanced] +doc_owner: specfact-cli +tracks: + - AGENTS.md + - CHANGELOG.md + - README.md + - docs/** + - pyproject.toml + - setup.py + - src/specfact_cli/__init__.py + - sibling specfact-cli-internal wiki scripts (see below) +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +id: agent-rules-release-commit-and-docs +always_load: false +applies_when: + - finalization + - release + - documentation-update +priority: 70 +blocking: false +user_interaction_required: true +stop_conditions: + - version bump requested without confirmation +depends_on: + - agent-rules-index + - agent-rules-quality-gates-and-review +--- + +# Agent release, commit, and docs rules + +## Versioning + +- Keep version updates in sync across `pyproject.toml`, `setup.py`, and `src/specfact_cli/__init__.py`. +- **Automated check:** Before tagging or publishing, run `hatch run check-version-sources` (or `python scripts/check_version_sources.py`). It exits non-zero with a clear diff if `pyproject.toml`, `setup.py`, `src/__init__.py`, and `src/specfact_cli/__init__.py` disagree. The **Tests** job in `.github/workflows/pr-orchestrator.yml` runs the same script so mismatches fail CI. Pre-commit runs it whenever a version file is staged (see `scripts/pre-commit-smart-checks.sh`) instead of treating version-only commits as β€œsafe” without verification. +- `hatch run release` is reserved for maintainers to chain `check-version-sources` before manual release steps; extend that script if you add more release automation. +- `feature/*` branches imply a minor bump, `bugfix/*` and `hotfix/*` imply a patch bump, and major bumps require explicit confirmation. + +## Changelog + +- Update `CHANGELOG.md` in the same commit as the version bump. +- Follow Keep a Changelog sections: `Added`, `Changed`, `Fixed`, `Removed`, `Security`. + +## Commits + +- Use Conventional Commits. +- If signed commits fail in a non-interactive shell, stage files and hand the exact `git commit -S -m ""` command to the user instead of bypassing signing. + +## Documentation and README + +- Keep docs current with every user-facing behavior change. +- Preserve all Jekyll frontmatter on docs edits. +- Update navigation when adding or moving pages. +- Keep `README.md` and the docs landing page aligned with what SpecFact actually does. + +## Internal wiki (sibling `specfact-cli-internal`) + +After **merging** changes that affect OpenSpec or GitHub-linked planning, and when a sibling `specfact-cli-internal` checkout is available, run the wiki scripts only after **`cd` into that internal repo** so the working directory matches what the scripts expect (running from `specfact-cli` or elsewhere will break them). From this repo’s root, for example: + +```bash +cd ../specfact-cli-internal && python3 scripts/wiki_openspec_gh_status.py +``` + +If the change touched **lots of** docs frontmatter (especially under `docs/agent-rules/`), also run: + +```bash +cd ../specfact-cli-internal && python3 scripts/wiki_rebuild_graph.py +``` + +When you materially edit an **active** OpenSpec change (scope, design, tasks story or dependencies), also update the mirrored `wiki/sources/.md` in the sibling internal repo when it is available, then run `wiki_rebuild_graph.py` as in [40-openspec-and-tdd.md](./40-openspec-and-tdd.md#internal-wiki-and-strategic-context). If the internal checkout is missing, record a merge checklist or follow-up instead of assuming the wiki is current. + +See **Internal wiki maintenance** under [40-openspec-and-tdd.md](./40-openspec-and-tdd.md#internal-wiki-and-strategic-context). diff --git a/docs/agent-rules/80-current-guidance-catalog.md b/docs/agent-rules/80-current-guidance-catalog.md new file mode 100644 index 00000000..f1a5c5d5 --- /dev/null +++ b/docs/agent-rules/80-current-guidance-catalog.md @@ -0,0 +1,71 @@ +--- +layout: default +title: Agent migrated guidance catalog +permalink: /contributing/agent-rules/migrated-guidance-catalog/ +description: Preserved guidance moved out of the previous long AGENTS.md before further tailoring and decomposition. +keywords: [agents, migrated-guidance, code-conventions, ci, testing] +audience: [team, enterprise] +expertise_level: [advanced] +doc_owner: specfact-cli +tracks: + - AGENTS.md + - docs/agent-rules/** + - src/specfact_cli/** + - tests/** + - .github/workflows/** +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +id: agent-rules-migrated-guidance-catalog +always_load: false +applies_when: + - detailed-reference +priority: 80 +blocking: false +user_interaction_required: false +stop_conditions: + - none +depends_on: + - agent-rules-index +--- + +# Agent migrated guidance catalog + +This file preserves current instructions that were previously inline in the long `AGENTS.md` but are not yet fully split into narrower docs. Nothing here was intentionally dropped during the compact-governance migration. + +## Developing specfact-cli-modules + +Bundle code in **specfact-cli-modules** imports from `specfact_cli`. For local IDE work in the modules repo, run `hatch env create` there, keep `specfact-cli` available at `../specfact-cli`, and select the modules repo `.venv` in the IDE. + +## Code conventions + +- Python 3.12 target (3.11+ runtime), line length 120, Google-style docstrings +- `snake_case` for files, modules, and functions +- `PascalCase` for classes +- `UPPER_SNAKE_CASE` for constants +- All data structures use Pydantic `BaseModel` with `Field(...)` and descriptions +- CLI commands use `typer.Typer()` and `rich.console.Console()` + +## CLI command pattern + +Use `@beartype`, `@require`, and `@ensure` on public command functions and keep command implementations typed and contract-bound. + +## Backlog command topology + +- `specfact backlog ceremony standup` +- `specfact backlog ceremony refinement` +- `specfact backlog analyze-deps` +- `specfact backlog delta status|impact|cost-estimate|rollback-analysis` +- `specfact backlog verify-readiness` +- `specfact project link-backlog` +- `specfact project health-check` +- `specfact project devops-flow --stage --action <...>` +- `specfact project snapshot|regenerate|export-roadmap` + +## Testing + +Contract-first coverage remains the primary testing philosophy. Test structure mirrors source under `tests/unit/`, `tests/integration/`, and `tests/e2e/`. Guard environment-sensitive logic with `os.environ.get("TEST_MODE") == "true"`. + +## CI/CD + +Key workflows in `.github/workflows/` include `tests.yml`, `specfact.yml`, `pr-orchestrator.yml`, and `build-and-push.yml`. diff --git a/docs/agent-rules/INDEX.md b/docs/agent-rules/INDEX.md new file mode 100644 index 00000000..8967fd72 --- /dev/null +++ b/docs/agent-rules/INDEX.md @@ -0,0 +1,111 @@ +--- +layout: default +title: Agent rules index +permalink: /contributing/agent-rules/ +description: Canonical deterministic loader for repository governance instructions used by AGENTS.md and other AI instruction surfaces. +keywords: [agents, governance, instructions, openspec, worktree] +audience: [team, enterprise] +expertise_level: [advanced] +doc_owner: specfact-cli +tracks: + - AGENTS.md + - CLAUDE.md + - .github/copilot-instructions.md + - .cursor/rules/session_startup_instructions.mdc + - docs/agent-rules/** + - scripts/check_doc_frontmatter.py + - scripts/validate_agent_rule_applies_when.py +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +id: agent-rules-index +always_load: true +applies_when: + - session-bootstrap +priority: 0 +blocking: true +user_interaction_required: false +stop_conditions: + - canonical rule index missing +depends_on: [] +--- + +# Agent rules index + +This page is the canonical loader for repository governance instructions. `AGENTS.md` stays small and mandatory, but the detailed rules live here and in the linked rule files so new sessions do not have to absorb the full policy corpus up front. + +## Bootstrap sequence + +1. Read `AGENTS.md`. +2. Load this index. +3. Load [`05-non-negotiable-checklist.md`](./05-non-negotiable-checklist.md). +4. Load [`10-session-bootstrap.md`](./10-session-bootstrap.md) (always-load; deterministic startup orchestration before enforcement). +5. Detect repository, branch, and worktree state. +6. Reject implementation from the `dev` or `main` checkout unless the user explicitly overrides that rule. +7. If GitHub hierarchy metadata is needed and `.specfact/backlog/github_hierarchy_cache.md` is missing or stale, refresh it with `python scripts/sync_github_hierarchy_cache.py`. +8. Load additional rule files from the applicability matrix below before implementation (beyond the always-load set, which already includes this index, the checklist, and session bootstrap). + +## Precedence + +1. Direct system and developer instructions +2. Explicit user override where repository governance allows it +3. `AGENTS.md` +4. `docs/agent-rules/05-non-negotiable-checklist.md` +5. Other `docs/agent-rules/*.md` files selected through this index +6. Change-local OpenSpec artifacts and workflow notes + +## Always-load rules + +| Order | File | Purpose | +| --- | --- | --- | +| 0 | `INDEX.md` | Deterministic rule dispatch and precedence | +| 5 | `05-non-negotiable-checklist.md` | Invariant SHALL gates | +| 10 | `10-session-bootstrap.md` | Startup checks and stop conditions | + +## Applicability matrix + +### Task signal definitions + +Use these **canonical `applies_when` tokens** in rule file frontmatter (under `docs/agent-rules/*.md`). They are the vocabulary `AGENTS.md` bootstrap and automation consume; do not invent ad-hoc strings. + +| Canonical signal | Typical user intent | +| --- | --- | +| `session-bootstrap` | First-load / startup sequencing (`INDEX.md`, checklist, session bootstrap). | +| `implementation` | Code or behavior change in a worktree. | +| `openspec-change-selection` | Choosing, validating, or editing an OpenSpec change. | +| `branch-management` | Branch / worktree operations and conflicts. | +| `github-public-work` | Public-repo GitHub issue linkage and hierarchy. | +| `change-readiness` | Pre-flight metadata completeness before implementation. | +| `finalization` | Closing out a change, evidence, or PR. | +| `release` | Versioning, tagging, publish prep. | +| `documentation-update` | User-facing docs and README edits. | +| `repository-orientation` | Onboarding / where things live in the repo. | +| `command-lookup` | CLI usage and command discovery. | +| `detailed-reference` | Long-form catalogs and non-blocking reference. | +| `verification` | Quality gates, tests, review artifacts. | + +**Validation:** `hatch run validate-agent-rule-signals` (runs `scripts/validate_agent_rule_applies_when.py`) checks every rule file’s `applies_when` list against this set. CI runs it from the **Docs Review** workflow when governance docs change. + +| Matrix row (human summary) | Canonical signals (`applies_when`) | Required rule files | Optional rule files | +| --- | --- | --- | --- | +| Any implementation request | `implementation`, `openspec-change-selection`, `verification` | `10-session-bootstrap.md`, `40-openspec-and-tdd.md`, `50-quality-gates-and-review.md` | `20-repository-context.md`; sibling internal `wiki/` (see **Internal wiki and strategic context** in `40-openspec-and-tdd.md`) when present | +| Code or docs changes on a branch | `branch-management`, `implementation` | `30-worktrees-and-branching.md` | `80-current-guidance-catalog.md` | +| Public GitHub issue work | `github-public-work`, `change-readiness` | `60-github-change-governance.md` | `30-worktrees-and-branching.md` | +| Release or finalization work | `finalization`, `release`, `documentation-update`, `verification` | `70-release-commit-and-docs.md`, `50-quality-gates-and-review.md` | `80-current-guidance-catalog.md` | +| Repo orientation or command lookup | `repository-orientation`, `command-lookup` | `20-repository-context.md` | `80-current-guidance-catalog.md` | + +## Canonical rule files + +- [`05-non-negotiable-checklist.md`](./05-non-negotiable-checklist.md): always-load SHALL gates +- [`10-session-bootstrap.md`](./10-session-bootstrap.md): startup checks, compact context loading, and stop/continue behavior +- [`20-repository-context.md`](./20-repository-context.md): project overview, commands, architecture, and logging +- [`30-worktrees-and-branching.md`](./30-worktrees-and-branching.md): branch protection, worktree policy, and conflict avoidance +- [`40-openspec-and-tdd.md`](./40-openspec-and-tdd.md): OpenSpec selection, change validity, strict TDD order, internal wiki mirror (`wiki/sources/.md`) when scope or dependencies shift, and optional sibling internal wiki context for change design +- [`50-quality-gates-and-review.md`](./50-quality-gates-and-review.md): required gates, code review JSON, clean-code enforcement, module signatures +- [`60-github-change-governance.md`](./60-github-change-governance.md): cache-first GitHub metadata, dependency completeness, and `in progress` ambiguity handling +- [`70-release-commit-and-docs.md`](./70-release-commit-and-docs.md): versioning, changelog, docs, README, and commit signing +- [`80-current-guidance-catalog.md`](./80-current-guidance-catalog.md): preserved migrated guidance not yet split into narrower documents + +## Preservation note + +The prior long `AGENTS.md` content has been preserved by reference in these rule files. The goal of this migration is to reduce startup token cost without silently dropping repository instructions. diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index 36bdaa26..fd5a718c 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -66,6 +66,7 @@ Common manifest fields: - Extension/security optional: `schema_extensions`, `service_bridges`, `publisher`, `integrity` See also: + - [Module Development Guide](../guides/module-development.md) - [Module Contracts](../reference/module-contracts.md) - [Module Security](../reference/module-security.md) @@ -124,6 +125,7 @@ All adapters implement: `BridgeProbe`/sync flows use detection and capabilities to select adapters and choose sync behavior safely. See also: + - [Adapter Development Guide](../guides/adapter-development.md) - [Bridge Registry](../reference/bridge-registry.md) diff --git a/docs/contributing/frontmatter-schema.md b/docs/contributing/frontmatter-schema.md index a886161e..48b37db0 100644 --- a/docs/contributing/frontmatter-schema.md +++ b/docs/contributing/frontmatter-schema.md @@ -31,6 +31,21 @@ This page is the **authoritative schema** for the ownership and tracking fields Runtime validation uses a Pydantic model (`DocFrontmatter` in `scripts/check_doc_frontmatter.py`): missing keys, wrong types, bad globs, unresolved `doc_owner`, and invalid exempt/reason pairs fail the check. +## Additional fields for `docs/agent-rules/` + +Files under `docs/agent-rules/` carry extra governance metadata on top of the standard doc-sync fields so bootstrap loaders can make deterministic decisions without reading every rule file in full. + +| Field | Type | Rules | +| --- | --- | --- | +| `id` | string | Stable rule identifier used by references and dependencies; must be **kebab-case** (lowercase words separated by hyphens; see `RULE_ID_RE` in `scripts/check_doc_frontmatter.py`). | +| `always_load` | boolean | `true` when the rule must be loaded during every applicable bootstrap. When `true`, runtime validation requires that `applies_when` intersects `session-bootstrap`, `implementation`, or `all` (see `_always_load_requires_bootstrap_signal` in the script). | +| `applies_when` | list of strings | Non-empty task signals such as `session-bootstrap`, `implementation`, `all`, or `github-public-work`. If `always_load` is `true`, at least one entry must be `session-bootstrap`, `implementation`, or `all`. | +| `priority` | integer | Non-negative ordering value used for deterministic rule loading. | +| `blocking` | boolean | Whether the rule can stop progress when its conditions are unmet. | +| `user_interaction_required` | boolean | Whether unmet conditions require user clarification before implementation continues. | +| `stop_conditions` | list of strings | Non-empty human-readable blocking conditions enforced by the rule. | +| `depends_on` | list of strings | Other rule ids that must load first. Empty list is allowed. | + ## Cross-repo notes - **Core vs modules:** Canonical user-facing docs for bundles and marketplace content live on [modules.specfact.io](https://modules.specfact.io/). Linking and URL rules are described in [Documentation URL contract](../reference/documentation-url-contract.md). diff --git a/docs/examples/integration-showcases/integration-showcases-testing-guide.md b/docs/examples/integration-showcases/integration-showcases-testing-guide.md index 2f5cef77..6c1a3deb 100644 --- a/docs/examples/integration-showcases/integration-showcases-testing-guide.md +++ b/docs/examples/integration-showcases/integration-showcases-testing-guide.md @@ -1056,6 +1056,7 @@ Report written to: .specfact/projects//reports/enforcement/report-< ```bash specfact code repro setup ``` + This automatically generates `[tool.crosshair]` configuration in `pyproject.toml` to enable contract exploration. **Important**: `repro` does **not** perform runtime contract validation (checking `@icontract` decorators at runtime). It runs static analysis (linting, type checking) and symbolic execution (CrossHair) for contract exploration. Type mismatches will be detected by the type checking tool (basedpyright) if available. The enforcement configuration determines whether failures block the workflow. diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index ada8d40a..5e3b017f 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -78,6 +78,7 @@ specfact init ide --ide cursor --install-deps [More options ↓](#more-options) ## More options + {: #more-options} ### Container diff --git a/docs/getting-started/tutorial-openspec-speckit.md b/docs/getting-started/tutorial-openspec-speckit.md index de2ee1bf..a8713bb5 100644 --- a/docs/getting-started/tutorial-openspec-speckit.md +++ b/docs/getting-started/tutorial-openspec-speckit.md @@ -147,6 +147,7 @@ specfact code import legacy-api --repo . - Creates a SpecFact project bundle (`.specfact/projects/legacy-api/`) **Note**: If using `hatch run specfact`, run from the specfact-cli directory: + ```bash cd /path/to/specfact-cli hatch run specfact code import legacy-api --repo /path/to/your-openspec-project @@ -422,6 +423,7 @@ specfact project health-check ``` **Note**: + - `project health-check` checks the project state and SDD compliance - It uses the current directory to find `.specfact/projects/` (no `--repo` option) - You must be in the project directory where the bundle was created @@ -521,6 +523,7 @@ specfact project regenerate - Helps catch drift between design and code **Note**: + - `project regenerate` uses the current directory to find bundles - You must be in the project directory where the bundle was created diff --git a/docs/guides/command-chains.md b/docs/guides/command-chains.md index 36f823db..e3c80cd4 100644 --- a/docs/guides/command-chains.md +++ b/docs/guides/command-chains.md @@ -246,7 +246,7 @@ graph LR - **After `import from-bridge`**: Review the imported plan. If it needs refinement, use `project devops-flow --stage plan --action update-feature`. - **Bidirectional sync**: Use `--watch` mode for continuous synchronization, or run sync manually as needed. -- **Adapter selection**: +- **Adapter selection**: - **Code/Spec adapters** (use `import from-bridge`): `speckit`, `openspec`, `generic-markdown` - **Backlog adapters** (use `sync bridge`): `github`, `ado`, `linear`, `jira` - **Note**: Backlog adapters (GitHub Issues, ADO, Linear, Jira) use `sync bridge` for bidirectional synchronization, not `import from-bridge`. The `import from-bridge` command is specifically for importing entire code/spec projects. diff --git a/docs/guides/dual-stack-enrichment.md b/docs/guides/dual-stack-enrichment.md index 627e3e95..1d9195e6 100644 --- a/docs/guides/dual-stack-enrichment.md +++ b/docs/guides/dual-stack-enrichment.md @@ -7,7 +7,6 @@ description: Guidance for combining SpecFact CLI automation with AI IDE enrichme # Dual-Stack Enrichment Pattern - > Modules docs handoff: this page remains in the core docs set as release-line overview content. > Canonical bundle-specific deep guidance now lives in the canonical modules docs site, currently > published at `https://modules.specfact.io/`. diff --git a/docs/guides/using-module-security-and-extensions.md b/docs/guides/using-module-security-and-extensions.md index ae7b161f..4110808b 100644 --- a/docs/guides/using-module-security-and-extensions.md +++ b/docs/guides/using-module-security-and-extensions.md @@ -25,9 +25,11 @@ With **arch-06** (manifest security) and **arch-07** (schema extension system) i - **Verified modules**: When you run any command that loads modules (e.g. `specfact backlog ...`, `specfact project ...`), the registry discovers modules and, when a module has `integrity.checksum` in its `module-package.yaml`, verifies the manifest checksum before registering. If verification fails, that module is skipped and a warning is logged; other modules still load. - **Unsigned modules**: Modules without `integrity` metadata are allowed by default (backward compatible). To document explicit opt-in in strict environments, set: + ```bash export SPECFACT_ALLOW_UNSIGNED=1 ``` + - **Versioned dependencies**: Manifests can declare `module_dependencies_versioned` and `pip_dependencies_versioned` (each entry: `name`, `version_specifier`) for install-time resolution. You don’t need to do anything special; the installer uses these when present. You don’t run a separate β€œverify” command; verification happens automatically at module registration when the CLI starts. @@ -82,10 +84,13 @@ Details: [Module Security](/reference/module-security/). Several commands already read or write extension data on `ProjectBundle` (and its manifest). You use them as usual; extensions are persisted with the bundle. - **Link a backlog provider** (writes `backlog_core.backlog_config` on project metadata): + ```bash specfact project link-backlog --bundle my-bundle --adapter github --project-id my-org/my-repo ``` + - **Health check and other project commands** read that same extension to resolve adapter/project/template: + ```bash specfact project health-check --bundle my-bundle ``` diff --git a/docs/reference/authentication.md b/docs/reference/authentication.md index 421c6b3e..5958463e 100644 --- a/docs/reference/authentication.md +++ b/docs/reference/authentication.md @@ -9,7 +9,6 @@ expertise_level: [intermediate, advanced] # Authentication - > Modules docs handoff: this page remains in the core docs set as release-line overview content. > Canonical bundle-specific deep guidance now lives in the canonical modules docs site, currently > published at `https://modules.specfact.io/`. @@ -113,10 +112,12 @@ Adapters resolve tokens in this order: **Azure DevOps Specific:** For Azure DevOps commands, stored tokens are automatically used by: + - `specfact backlog refine ado` - Automatically uses stored token if available - `specfact backlog map-fields` - Automatically uses stored token if available If a stored token is expired, you'll see a warning with options to: + 1. Use a PAT token (recommended for longer expiration) 2. Re-authenticate via `specfact backlog auth azure-devops` 3. Use `--ado-token` option with a valid token @@ -130,22 +131,26 @@ If a stored token is expired, you'll see a warning with options to: **Solutions:** 1. **Check token expiration**: OAuth tokens expire after ~1 hour. Use a PAT token for longer expiration: + ```bash specfact backlog auth azure-devops --pat your_pat_token ``` 2. **Use explicit token**: Override with `--ado-token` flag: + ```bash specfact backlog refine ado --ado-org myorg --ado-project myproject --ado-token your_token ``` 3. **Set environment variable**: Use `AZURE_DEVOPS_TOKEN` environment variable: + ```bash export AZURE_DEVOPS_TOKEN=your_token specfact backlog refine ado --ado-org myorg --ado-project myproject ``` 4. **Re-authenticate**: Clear and re-authenticate: + ```bash specfact backlog auth clear --provider azure-devops specfact backlog auth azure-devops diff --git a/docs/reference/command-syntax-policy.md b/docs/reference/command-syntax-policy.md index a1e1dd19..a5781904 100644 --- a/docs/reference/command-syntax-policy.md +++ b/docs/reference/command-syntax-policy.md @@ -49,4 +49,3 @@ hatch run specfact project devops-flow --help hatch run specfact govern enforce --help hatch run specfact spec validate --help ``` - diff --git a/openspec/CHANGE_ORDER.md b/openspec/CHANGE_ORDER.md index c1ed8b7d..6d913aab 100644 --- a/openspec/CHANGE_ORDER.md +++ b/openspec/CHANGE_ORDER.md @@ -181,6 +181,8 @@ Cross-repo dependency: `docs-07-core-handoff-conversion` depends on `specfact-cl | Module | Order | Change folder | GitHub # | Blocked by | |--------|-------|---------------|----------|------------| | governance | 01 | cross-repo-issue-realignment | [#484](https://github.com/nold-ai/specfact-cli/issues/484) | agile-01 βœ…; module-migration-11 [#408](https://github.com/nold-ai/specfact-cli/issues/408); backlog-module-ownership-cleanup | +| governance | 02 | βœ… governance-02-github-hierarchy-cache (archived `openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache`) | [#491](https://github.com/nold-ai/specfact-cli/issues/491) | governance-01 [#484](https://github.com/nold-ai/specfact-cli/issues/484); Parent Feature: [#486](https://github.com/nold-ai/specfact-cli/issues/486) | +| governance | 03 | βœ… governance-03-deterministic-agent-governance-loading (archived `openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading`) | [#494](https://github.com/nold-ai/specfact-cli/issues/494) | governance-02 βœ…; Parent Feature: [#486](https://github.com/nold-ai/specfact-cli/issues/486) | ### Cross-cutting foundations (no hard dependencies β€” implement early) @@ -256,6 +258,7 @@ Cross-repo dependency: `docs-07-core-handoff-conversion` depends on `specfact-cl | profile | 01 | profile-01-config-layering | [#237](https://github.com/nold-ai/specfact-cli/issues/237) | #193 (existing init/module-state baseline) | | profile | 02 | profile-02-central-config-sources | [#249](https://github.com/nold-ai/specfact-cli/issues/249) | #237 (profile-01) | | profile | 03 | profile-03-domain-overlays | [#250](https://github.com/nold-ai/specfact-cli/issues/250) | #237 (profile-01), #249 (profile-02), #213 | +| profile | 04 | profile-04-safe-project-artifact-writes | [#490](https://github.com/nold-ai/specfact-cli/issues/490) | Parent Feature: [#365](https://github.com/nold-ai/specfact-cli/issues/365); related bug [#487](https://github.com/nold-ai/specfact-cli/issues/487) | ### Requirements layer (architecture integration plan, 2026-02-15) @@ -315,6 +318,7 @@ Spec-Kit has evolved to v0.4.3 with 46 extensions, pluggable presets, 7+ slash c **Cross-repo note**: speckit-03 lives in `nold-ai/specfact-cli-modules` but depends on speckit-02 in this repo (ToolCapabilities extension fields). **Updated proposals** (spec-kit interop sections added 2026-03-27): + - `sync-01-unified-kernel`: Added spec-kit extension interop β€” sync kernel detects external sync actors from spec-kit reconcile/sync/iterate extensions - `requirements-03-backlog-sync`: Added spec-kit backlog extension awareness β€” prevents duplicate issue creation when spec-kit Jira/ADO/Linear extensions have already created issues @@ -440,6 +444,7 @@ The following ownership boundaries are mandatory before implementation for overl | `ai_sync.review_runs` and `ai_sync.reward_ledger` Supabase tables | `code-review-06-reward-ledger` | code-review-07, code-review-09 | Pre-implementation rule: + - No dependent change may redefine an owned surface. Any required semantic change must be proposed as a delta to the authoritative change first. --- diff --git a/openspec/changes/ai-integration-01-agent-skill/design.md b/openspec/changes/ai-integration-01-agent-skill/design.md index a24a132e..4830ddec 100644 --- a/openspec/changes/ai-integration-01-agent-skill/design.md +++ b/openspec/changes/ai-integration-01-agent-skill/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `ai-integration-01-agent-skill` from t ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/ai-integration-01-agent-skill/proposal.md b/openspec/changes/ai-integration-01-agent-skill/proposal.md index d8928d0d..5fb6b1d2 100644 --- a/openspec/changes/ai-integration-01-agent-skill/proposal.md +++ b/openspec/changes/ai-integration-01-agent-skill/proposal.md @@ -2,16 +2,10 @@ ## Why - - - AI IDEs generate 41% of all code with zero specification validation. No spec-driven tool offers a Skills-first integration that keeps context usage near-zero until activated. Agent Skills β€” adopted by 26+ platforms (Claude Code, Copilot, Cursor, Windsurf, Gemini CLI, Codex) in under two months β€” provide the most context-efficient integration path: ~80 tokens at rest vs 13,647+ tokens for MCP servers. A SpecFact Agent Skill teaches AI agents when and how to invoke SpecFact validation, making specification intelligence available across the entire AI IDE ecosystem with a single implementation. ## What Changes - - - - **NEW**: Agent Skill at `skills/specfact/SKILL.md` following the open standard: - YAML frontmatter: name, description, allowed-tools (bash/terminal) - Markdown instructions: when to invoke SpecFact, what validation modes exist, how to interpret results @@ -28,6 +22,7 @@ AI IDEs generate 41% of all code with zero specification validation. No spec-dri - **NEW**: Skill bundled resources: common spec patterns, validation result interpretation guide, example workflows ## Capabilities + ### New Capabilities - `agent-skill-spec-intelligence`: Agent Skill (open standard, 26+ platform support) for specification validation, traceability queries, and requirements coverage β€” ~80 tokens at rest, ~2,000-3,000 tokens on activation. Includes composable sub-skills for PR assessment, architecture checking, and coverage reporting. @@ -36,7 +31,6 @@ AI IDEs generate 41% of all code with zero specification validation. No spec-dri (none) - --- ## Source Tracking diff --git a/openspec/changes/ai-integration-01-agent-skill/specs/agent-skill-spec-intelligence/spec.md b/openspec/changes/ai-integration-01-agent-skill/specs/agent-skill-spec-intelligence/spec.md index e6982830..32fdad0e 100644 --- a/openspec/changes/ai-integration-01-agent-skill/specs/agent-skill-spec-intelligence/spec.md +++ b/openspec/changes/ai-integration-01-agent-skill/specs/agent-skill-spec-intelligence/spec.md @@ -1,15 +1,18 @@ ## ADDED Requirements ### Requirement: Agent Skill Spec Intelligence + The system SHALL provide a skill-first integration that directs AI agents to SpecFact workflows with low context overhead. #### Scenario: Skill instructs agent to run deterministic CLI checks + - **GIVEN** skill is activated for PR or change analysis - **WHEN** agent follows skill guidance - **THEN** agent invokes commands such as `specfact validate`, `specfact trace show`, and `specfact requirements list` - **AND** output references evidence paths instead of embedding large raw artifacts. #### Scenario: Skill content remains lightweight at rest + - **GIVEN** skill metadata is loaded by an IDE agent - **WHEN** idle context is measured - **THEN** base skill content remains compact diff --git a/openspec/changes/ai-integration-02-mcp-server/design.md b/openspec/changes/ai-integration-02-mcp-server/design.md index bbc17068..02b58c07 100644 --- a/openspec/changes/ai-integration-02-mcp-server/design.md +++ b/openspec/changes/ai-integration-02-mcp-server/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `ai-integration-02-mcp-server` from th ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/ai-integration-02-mcp-server/proposal.md b/openspec/changes/ai-integration-02-mcp-server/proposal.md index 4ec70f7a..33b17274 100644 --- a/openspec/changes/ai-integration-02-mcp-server/proposal.md +++ b/openspec/changes/ai-integration-02-mcp-server/proposal.md @@ -2,16 +2,10 @@ ## Why - - - While the Agent Skill handles 90% of developer interactions via CLI invocation, agentic workflows (CI/CD pipelines, autonomous coding agents, multi-step validation loops) need programmatic tool access with structured inputs and outputs. MCP (Model Context Protocol) β€” adopted by every major AI platform with 97M+ monthly SDK downloads β€” provides the standard interface. An MCP server exposing 3-5 high-level tools with compressed, summarized results gives SpecFact programmatic accessibility without the context bloat of naive MCP implementations. ## What Changes - - - - **NEW**: MCP server in `src/specfact_cli/mcp/` exposing high-level tools: - `validate_spec(spec_path, options)` β€” run validation (spec-only or full-chain), return compressed summary - `check_compatibility(base_spec, revision_spec)` β€” breaking change detection between spec versions @@ -25,6 +19,7 @@ While the Agent Skill handles 90% of developer interactions via CLI invocation, - **DESIGN DECISION**: MCP server wraps CLI commands internally β€” the CLI remains the source of truth, MCP is a thin adapter layer ## Capabilities + ### New Capabilities - `mcp-server`: MCP server exposing 3-5 high-level specification intelligence tools with server-side summarization, dual-response pattern, stdio and HTTP/SSE transports. Wraps CLI commands as thin adapter layer. @@ -33,7 +28,6 @@ While the Agent Skill handles 90% of developer interactions via CLI invocation, (none) - --- ## Source Tracking diff --git a/openspec/changes/ai-integration-02-mcp-server/specs/mcp-server/spec.md b/openspec/changes/ai-integration-02-mcp-server/specs/mcp-server/spec.md index 4161b6a2..9e0b96a7 100644 --- a/openspec/changes/ai-integration-02-mcp-server/specs/mcp-server/spec.md +++ b/openspec/changes/ai-integration-02-mcp-server/specs/mcp-server/spec.md @@ -1,15 +1,18 @@ ## ADDED Requirements ### Requirement: Mcp Server + The system SHALL expose a bounded set of high-value MCP tools for validation, compatibility, coverage, and fix guidance. #### Scenario: MCP tool returns summarized result with reference + - **GIVEN** a validation MCP tool invocation - **WHEN** execution completes - **THEN** response returns concise summary metrics - **AND** includes a link or path to the full report artifact. #### Scenario: Tool surface remains intentionally small + - **GIVEN** MCP server configuration - **WHEN** server starts - **THEN** only approved core tools are exposed diff --git a/openspec/changes/ai-integration-03-instruction-files/design.md b/openspec/changes/ai-integration-03-instruction-files/design.md index db5065f6..4fb5ceab 100644 --- a/openspec/changes/ai-integration-03-instruction-files/design.md +++ b/openspec/changes/ai-integration-03-instruction-files/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `ai-integration-03-instruction-files` ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/ai-integration-03-instruction-files/proposal.md b/openspec/changes/ai-integration-03-instruction-files/proposal.md index addf08ae..1e05fe88 100644 --- a/openspec/changes/ai-integration-03-instruction-files/proposal.md +++ b/openspec/changes/ai-integration-03-instruction-files/proposal.md @@ -2,16 +2,10 @@ ## Why - - - Each AI IDE has its own instruction file format (`.cursor/rules/*.mdc`, `.github/copilot-instructions.md`, `CLAUDE.md`, `.windsurf/rules/`). Teams using multiple IDEs need SpecFact guidance in each format. Auto-generated lightweight instruction files that point to the core Agent Skill β€” with glob-based auto-attachment on spec files β€” ensure consistent spec validation guidance across all IDEs without manual per-platform maintenance. ## What Changes - - - - **NEW**: `specfact ide setup --platforms cursor,copilot,claude,windsurf` β€” auto-generate instruction files for selected platforms: - `.cursor/rules/specfact.mdc` β€” Cursor rule with glob: `**/*.yaml, **/*.json, **/openapi*` - `.github/instructions/specfact.instructions.md` β€” Copilot instructions with `applyTo` glob @@ -27,6 +21,7 @@ Each AI IDE has its own instruction file format (`.cursor/rules/*.mdc`, `.github - **MODIFY**: Generated instruction files stay lightweight and MUST reference clean-code enforcement through a one-line alias to the canonical `specfact-code-review` skill instead of embedding the full 7-principle charter inline ## Capabilities + ### New Capabilities - `cross-platform-instructions`: Auto-generated AI IDE instruction files for Cursor, Copilot, Claude Code, and Windsurf. Lightweight aliases pointing to the core Agent Skill, with glob-based auto-attachment on spec files. @@ -35,7 +30,6 @@ Each AI IDE has its own instruction file format (`.cursor/rules/*.mdc`, `.github - `cross-platform-instructions`: Extended so generated aliases can reference clean-code enforcement without breaking the lightweight token budget for IDE instruction surfaces - --- ## Source Tracking diff --git a/openspec/changes/ai-integration-03-instruction-files/specs/cross-platform-instructions/spec.md b/openspec/changes/ai-integration-03-instruction-files/specs/cross-platform-instructions/spec.md index 8c0ad20a..883dc03a 100644 --- a/openspec/changes/ai-integration-03-instruction-files/specs/cross-platform-instructions/spec.md +++ b/openspec/changes/ai-integration-03-instruction-files/specs/cross-platform-instructions/spec.md @@ -1,21 +1,25 @@ ## ADDED Requirements ### Requirement: Cross Platform Instructions + The system SHALL generate platform-specific instruction files that reference the canonical SpecFact skill behavior. #### Scenario: Multi-platform setup command writes instruction files + - **GIVEN** `specfact ide setup --platforms cursor,copilot,claude,windsurf` - **WHEN** setup runs - **THEN** platform instruction files are generated in expected locations - **AND** each file references the same canonical SpecFact workflow entry points. #### Scenario: Regeneration is idempotent + - **GIVEN** instruction files already exist - **WHEN** setup is re-run without behavior changes - **THEN** files are updated deterministically - **AND** no duplicate instruction blocks are introduced. #### Scenario: Clean-code guidance stays an alias, not an inline charter + - **GIVEN** clean-code enforcement is active in the installed skills - **WHEN** platform instruction files are generated or refreshed - **THEN** each file includes a short reference to the canonical `specfact-code-review` skill diff --git a/openspec/changes/ai-integration-04-intent-skills/CHANGE_VALIDATION.md b/openspec/changes/ai-integration-04-intent-skills/CHANGE_VALIDATION.md index ff72ee1c..2506a6eb 100644 --- a/openspec/changes/ai-integration-04-intent-skills/CHANGE_VALIDATION.md +++ b/openspec/changes/ai-integration-04-intent-skills/CHANGE_VALIDATION.md @@ -91,6 +91,7 @@ None. ## Wave/Sequencing Confirmation Wave 8, blocked by: + - ai-integration-01 (#251) β€” skill install infrastructure - requirements-01 (#238) + requirements-02 (#239) β€” skills invoke `specfact requirements capture/validate/trace` diff --git a/openspec/changes/ai-integration-04-intent-skills/design.md b/openspec/changes/ai-integration-04-intent-skills/design.md index 3422510b..26f2a766 100644 --- a/openspec/changes/ai-integration-04-intent-skills/design.md +++ b/openspec/changes/ai-integration-04-intent-skills/design.md @@ -7,12 +7,14 @@ ## Goals / Non-Goals **Goals:** + - Ship 6 skill files covering the full intent engineering workflow (capture β†’ decompose β†’ architecture β†’ trace-validate β†’ evidence-check) - Extend `specfact ide skill install` with `--type intent` to install intent skills alongside or separately from spec skills - Keep each skill file self-contained and composable (an agent can invoke one or all) - Follow SQUER's 7-question interview exactly β€” this is the scholarly grounding for the intent capture pattern **Non-Goals:** + - Building a new CLI command group for intent β€” the skills invoke existing `specfact requirements`, `specfact architecture`, and `specfact validate` commands - IDE-specific integrations β€” the open skills standard handles 26+ platforms without per-IDE code - Replacing `ai-integration-01` β€” this change extends the skills surface, not replaces it diff --git a/openspec/changes/ai-integration-04-intent-skills/specs/agent-skill-intent-workflow/spec.md b/openspec/changes/ai-integration-04-intent-skills/specs/agent-skill-intent-workflow/spec.md index 6b95ab81..4f923e79 100644 --- a/openspec/changes/ai-integration-04-intent-skills/specs/agent-skill-intent-workflow/spec.md +++ b/openspec/changes/ai-integration-04-intent-skills/specs/agent-skill-intent-workflow/spec.md @@ -1,45 +1,54 @@ ## ADDED Requirements ### Requirement: Intent Capture Skill + The system SHALL provide a SQUER-based intent capture Agent Skill at `skills/specfact-intent-capture/SKILL.md` that guides AI agents through a 7-question business intent interview and persists the result to `.specfact/requirements/{id}.req.yaml`. #### Scenario: Intent capture skill performs SQUER 7-question interview + - **GIVEN** an AI IDE agent with the `specfact-intent-capture` skill installed - **WHEN** the agent activates the intent capture skill - **THEN** the skill prompts the user with the 7 SQUER questions in sequence: What problem? Who has it? What happens today? What should change? How will we know? What must not break? What's the priority? - **AND** answers are mapped to `BusinessOutcome` fields (description, persona, current_state, target_state, success_criteria, constraints, priority) #### Scenario: Intent capture produces valid requirement artifact + - **GIVEN** the SQUER interview is complete with all 7 answers - **WHEN** the skill invokes `specfact requirements capture` - **THEN** a `.specfact/requirements/{id}.req.yaml` file is created - **AND** the artifact validates against the `BusinessOutcome` schema without errors #### Scenario: Intent capture skill verifies CLI prerequisites + - **GIVEN** a fresh IDE session before the skill is activated - **WHEN** the skill checks prerequisites - **THEN** it runs `specfact requirements --help` to verify the requirements module is installed - **AND** if the module is missing it directs the agent to install it before proceeding ### Requirement: Requirements Decomposition Skill + The system SHALL provide a decomposition Agent Skill at `skills/specfact-intent-decompose/SKILL.md` that takes a captured `BusinessOutcome` and decomposes it into `BusinessRule` (Given/When/Then) and `ArchitecturalConstraint` artifacts via `specfact requirements validate`. #### Scenario: Decomposition skill generates G/W/T business rules + - **GIVEN** a `.specfact/requirements/{id}.req.yaml` file containing a `BusinessOutcome` - **WHEN** the agent activates the intent decompose skill - **THEN** the skill prompts the agent to derive at least one `BusinessRule` per success criterion - **AND** each rule is expressed in Given/When/Then format and assigned a stable rule ID (BR-NNN) #### Scenario: Decomposition skill identifies architectural constraints + - **GIVEN** a decomposition session with at least one business rule defined - **WHEN** the skill processes the rules - **THEN** it prompts the agent to identify at least one `ArchitecturalConstraint` derived from the constraints field of the `BusinessOutcome` - **AND** each constraint is assigned a stable ID (AC-NNN) and linked to the parent `BusinessOutcome` ### Requirement: Architecture Derivation Skill + The system SHALL provide an architecture derivation Agent Skill at `skills/specfact-intent-architecture/SKILL.md` that invokes `specfact architecture derive` to generate ADRs from captured requirements context. #### Scenario: Architecture skill generates ADR from requirements + - **GIVEN** at least one `BusinessRule` and one `ArchitecturalConstraint` in `.specfact/requirements/` - **WHEN** the agent activates the architecture derive skill - **THEN** the skill invokes `specfact architecture derive --requirement {id}` @@ -47,24 +56,29 @@ The system SHALL provide an architecture derivation Agent Skill at `skills/specf - **AND** the ADR includes an explicit link to the `BusinessOutcome` ID and at least one `ArchitecturalConstraint` ID ### Requirement: Trace Validation Skill + The system SHALL provide a trace validation Agent Skill at `skills/specfact-intent-trace-validate/SKILL.md` that validates the full traceability chain (outcome β†’ rule β†’ constraint β†’ spec β†’ code) and reports gaps with structured fix prompts. #### Scenario: Trace validation reports complete chain + - **GIVEN** a project with requirements, specs, and code present - **WHEN** the agent activates the trace-validate skill - **THEN** the skill invokes `specfact validate --full-chain` - **AND** a gap report is produced listing any orphaned artifacts (requirements with no spec link, specs with no test, etc.) #### Scenario: Trace validation generates fix prompts for gaps + - **GIVEN** the trace validation finds at least one gap - **WHEN** the skill processes the gap report - **THEN** it generates a structured fix prompt for each gap type (missing spec, missing test binding, missing requirement link) - **AND** the fix prompt references the specific artifact IDs involved ### Requirement: Evidence Check Skill + The system SHALL provide an evidence-check Agent Skill at `skills/specfact-intent-evidence-check/SKILL.md` that checks evidence completeness for all artifacts in the intent-to-code chain. #### Scenario: Evidence check reports missing evidence envelopes + - **GIVEN** a project where some artifacts lack evidence JSON files - **WHEN** the agent activates the evidence-check skill - **THEN** the skill invokes `specfact validate --full-chain --evidence-dir .specfact/evidence/` @@ -72,21 +86,25 @@ The system SHALL provide an evidence-check Agent Skill at `skills/specfact-inten - **AND** the exit code is non-zero if any required evidence is missing in strict mode ### Requirement: Intent Skills Installation + The system SHALL extend `specfact ide skill install` with a `--type` option so intent skills can be installed independently of spec-validation skills. #### Scenario: Intent skills installed via CLI + - **GIVEN** the `specfact ide skill install` command is available (from ai-integration-01) - **WHEN** the user runs `specfact ide skill install --type intent` - **THEN** all 6 intent skill files are copied to the IDE-appropriate location - **AND** the command confirms each skill file was installed successfully #### Scenario: Existing spec skill install is backwards-compatible + - **GIVEN** a user running `specfact ide skill install` without the `--type` flag - **WHEN** the command executes - **THEN** it behaves identically to the pre-change behavior (installs spec skills only) - **AND** no error or deprecation warning is emitted #### Scenario: All skill types installed with --type all + - **GIVEN** the `--type all` option is used - **WHEN** the command executes - **THEN** both spec skills (from ai-integration-01) and intent skills are installed diff --git a/openspec/changes/ai-integration-04-intent-skills/specs/agent-skill-spec-intelligence/spec.md b/openspec/changes/ai-integration-04-intent-skills/specs/agent-skill-spec-intelligence/spec.md index 5387b879..97236db6 100644 --- a/openspec/changes/ai-integration-04-intent-skills/specs/agent-skill-spec-intelligence/spec.md +++ b/openspec/changes/ai-integration-04-intent-skills/specs/agent-skill-spec-intelligence/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Agent Skill Installation + The system SHALL provide `specfact ide skill install` with a `--type {spec,intent,all}` option to install skill files to the IDE-appropriate location (modified to support skill type selection). #### Scenario: Spec skill install without --type flag (backwards compatible) + - **GIVEN** a project with an active AI IDE configuration - **WHEN** the user runs `specfact ide skill install` without a `--type` flag - **THEN** spec-validation skills (from ai-integration-01) are installed as before - **AND** no breaking change occurs to the existing install flow #### Scenario: Skill list enumerates all available skill types + - **GIVEN** both spec skills (ai-integration-01) and intent skills (ai-integration-04) are available - **WHEN** the user runs `specfact ide skill list` - **THEN** both `spec` and `intent` skill types are listed with their descriptions diff --git a/openspec/changes/ai-integration-04-intent-skills/tasks.md b/openspec/changes/ai-integration-04-intent-skills/tasks.md index 70e05c13..ec3ca72a 100644 --- a/openspec/changes/ai-integration-04-intent-skills/tasks.md +++ b/openspec/changes/ai-integration-04-intent-skills/tasks.md @@ -5,6 +5,7 @@ Per `openspec/config.yaml`, tests MUST precede production code for any behavior-changing task. Order: + 1. Spec deltas (already in `specs/`) 2. Tests derived from spec scenarios β€” run and expect failure 3. Production code β€” implement until tests pass @@ -97,6 +98,7 @@ Do not implement production code until tests exist and have been run (expecting ## 8. GitHub issue creation - [ ] 8.1 Create GitHub issue: + ```bash gh issue create \ --repo nold-ai/specfact-cli \ @@ -105,6 +107,7 @@ Do not implement production code until tests exist and have been run (expecting --label "enhancement" \ --label "change-proposal" ``` + - [ ] 8.2 Link issue to project: `gh project item-add 1 --owner nold-ai --url ` - [ ] 8.3 Update `proposal.md` Source Tracking section with issue number and URL - [ ] 8.4 Link branch to issue: `gh issue develop --repo nold-ai/specfact-cli --name feature/ai-integration-04-intent-skills` @@ -114,6 +117,7 @@ Do not implement production code until tests exist and have been run (expecting - [ ] 9.1 `git add` all changed files; commit with `feat: add SQUER intent engineering skills for AI IDEs` - [ ] 9.2 `git push -u origin feature/ai-integration-04-intent-skills` - [ ] 9.3 Create PR: + ```bash gh pr create \ --repo nold-ai/specfact-cli \ @@ -122,6 +126,7 @@ Do not implement production code until tests exist and have been run (expecting --title "feat: SQUER intent engineering skills for AI IDEs" \ --body-file /tmp/pr-body-ai-integration-04.md ``` + - [ ] 9.4 Link PR to project: `gh project item-add 1 --owner nold-ai --url ` - [ ] 9.5 Set project status to "In Progress" diff --git a/openspec/changes/architecture-01-solution-layer/design.md b/openspec/changes/architecture-01-solution-layer/design.md index 8ed9ce20..a3e91637 100644 --- a/openspec/changes/architecture-01-solution-layer/design.md +++ b/openspec/changes/architecture-01-solution-layer/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `architecture-01-solution-layer` from ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/architecture-01-solution-layer/proposal.md b/openspec/changes/architecture-01-solution-layer/proposal.md index 393a8510..c263a126 100644 --- a/openspec/changes/architecture-01-solution-layer/proposal.md +++ b/openspec/changes/architecture-01-solution-layer/proposal.md @@ -2,9 +2,6 @@ ## Why - - - Architectural decisions live in separate ADRs or Confluence pages with zero programmatic links to requirements or code. This is the layer where the costliest misalignments occur β€” a wrong architectural choice invalidates entire implementation efforts regardless of code quality. No tool today systematically connects business requirements β†’ architectural decisions β†’ implementation. A solution architecture module that derives, stores, and validates architecture with explicit traceability to requirements closes the biggest blind spot in the end-to-end chain. ## Ownership Alignment (2026-04-08) @@ -42,6 +39,7 @@ modules/architecture/ ``` **`module-package.yaml` declares:** + - `name: architecture` - `version: 0.1.0` - `commands: [architecture derive, architecture validate-coverage, architecture trace]` @@ -76,6 +74,7 @@ modules/architecture/ ``` **`module-package.yaml` declares:** + - `name: architecture` - `version: 0.1.0` - `commands: [architecture derive, architecture validate-coverage, architecture trace]` @@ -85,9 +84,6 @@ modules/architecture/ ## What Changes - - - - **NEW**: Pydantic domain models in `modules/architecture/src/architecture/models/`: - `SolutionArchitecture` β€” architecture ID, requirement IDs (traceability links), components, data flows, ADRs - `ComponentSpec` β€” name, responsibility, business rule IDs (from requirements), integrations @@ -101,6 +97,7 @@ modules/architecture/ - **EXTEND**: `ProjectBundle` extended with optional `architecture` field via arch-07 schema extensions (namespace: `architecture.solution_architecture`) ## Capabilities + ### New Capabilities - `solution-architecture`: Derive, store, and validate solution architecture with explicit traceability to business requirements. Includes component specs, data flows, ADRs, and coverage validation. @@ -109,7 +106,6 @@ modules/architecture/ - `data-models`: ProjectBundle extended with architecture field via arch-07 schema extensions - --- ## Source Tracking diff --git a/openspec/changes/architecture-01-solution-layer/specs/data-models/spec.md b/openspec/changes/architecture-01-solution-layer/specs/data-models/spec.md index e874f6ce..d56d39e0 100644 --- a/openspec/changes/architecture-01-solution-layer/specs/data-models/spec.md +++ b/openspec/changes/architecture-01-solution-layer/specs/data-models/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Data Models + The system SHALL extend project-level models with an architecture namespace linked to requirements. #### Scenario: Architecture model references requirement IDs + - **GIVEN** a solution architecture artifact - **WHEN** model validation runs - **THEN** each architecture document includes requirement identifiers - **AND** referenced IDs preserve stable cross-layer linkage. #### Scenario: Architecture namespace remains optional for backward compatibility + - **GIVEN** older bundles without architecture data - **WHEN** bundle parsing runs - **THEN** parsing succeeds diff --git a/openspec/changes/architecture-01-solution-layer/specs/solution-architecture/spec.md b/openspec/changes/architecture-01-solution-layer/specs/solution-architecture/spec.md index c8bd9252..d57657a1 100644 --- a/openspec/changes/architecture-01-solution-layer/specs/solution-architecture/spec.md +++ b/openspec/changes/architecture-01-solution-layer/specs/solution-architecture/spec.md @@ -1,21 +1,25 @@ ## ADDED Requirements ### Requirement: Solution Architecture + The system SHALL provide architecture derive, coverage validation, and trace outputs linked to requirements. #### Scenario: Derive architecture from requirements + - **GIVEN** `specfact architecture derive --requirements .specfact/requirements/ --interactive` - **WHEN** derive completes - **THEN** an `.arch.yaml` artifact is created - **AND** components and ADR entries reference requirement rules. #### Scenario: Validate architecture coverage + - **GIVEN** requirements and architecture artifacts exist - **WHEN** `specfact architecture validate-coverage` runs - **THEN** unmapped business rules are reported - **AND** missing ADRs for architectural constraints are reported. #### Scenario: Trace command exports architecture linkage + - **GIVEN** architecture and requirements data - **WHEN** `specfact architecture trace --format json` runs - **THEN** output includes requirement-to-component-to-ADR mappings diff --git a/openspec/changes/archive/2025-12-29-add-devops-backlog-tracking/proposal.md b/openspec/changes/archive/2025-12-29-add-devops-backlog-tracking/proposal.md index 4af72772..e5216ad1 100644 --- a/openspec/changes/archive/2025-12-29-add-devops-backlog-tracking/proposal.md +++ b/openspec/changes/archive/2025-12-29-add-devops-backlog-tracking/proposal.md @@ -147,13 +147,6 @@ All three capabilities can coexist in the same adapter using different sync mode - βœ… Integration tests pass - βœ… Test coverage β‰₯80% - - - - - - - --- ## Source Tracking diff --git a/openspec/changes/archive/2025-12-30-add-change-tracking-datamodel/design.md b/openspec/changes/archive/2025-12-30-add-change-tracking-datamodel/design.md index d7d68739..3f4841b2 100644 --- a/openspec/changes/archive/2025-12-30-add-change-tracking-datamodel/design.md +++ b/openspec/changes/archive/2025-12-30-add-change-tracking-datamodel/design.md @@ -155,6 +155,7 @@ def save_change_proposal(self, bundle_dir: Path, proposal: ChangeProposal, bridg **Cross-Repository Support**: All adapter methods must support cross-repository configurations: + - Check `bridge_config.external_base_path` before using `bundle_dir` - Resolve all paths relative to external base when provided - Support both same-repo (default) and cross-repo scenarios transparently diff --git a/openspec/changes/archive/2025-12-30-add-code-change-tracking/proposal.md b/openspec/changes/archive/2025-12-30-add-code-change-tracking/proposal.md index 8f6a6e2b..ccf5dd2b 100644 --- a/openspec/changes/archive/2025-12-30-add-code-change-tracking/proposal.md +++ b/openspec/changes/archive/2025-12-30-add-code-change-tracking/proposal.md @@ -5,6 +5,7 @@ The current DevOps sync implementation only updates issue bodies when proposal content changes and adds comments for status changes or significant content changes. However, teams need to track implementation progress based on actual code changes (git commits, file modifications) and add progress comments to existing issues without replacing the entire issue body. This enables: + - Track implementation milestones as code is written - Notify stakeholders when implementation work progresses - Provide incremental updates without replacing issue content @@ -66,7 +67,6 @@ This change extends the existing DevOps sync capability to detect code changes r - βœ… Integration tests pass - βœ… Test coverage β‰₯80% - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-01-01-add-change-tracking-datamodel/design.md b/openspec/changes/archive/2026-01-01-add-change-tracking-datamodel/design.md index d7d68739..3f4841b2 100644 --- a/openspec/changes/archive/2026-01-01-add-change-tracking-datamodel/design.md +++ b/openspec/changes/archive/2026-01-01-add-change-tracking-datamodel/design.md @@ -155,6 +155,7 @@ def save_change_proposal(self, bundle_dir: Path, proposal: ChangeProposal, bridg **Cross-Repository Support**: All adapter methods must support cross-repository configurations: + - Check `bridge_config.external_base_path` before using `bundle_dir` - Resolve all paths relative to external base when provided - Support both same-repo (default) and cross-repo scenarios transparently diff --git a/openspec/changes/archive/2026-01-01-implement-openspec-bridge-adapter/INTEGRATION_REVIEW.md b/openspec/changes/archive/2026-01-01-implement-openspec-bridge-adapter/INTEGRATION_REVIEW.md index 10604d60..dfb7f32e 100644 --- a/openspec/changes/archive/2026-01-01-implement-openspec-bridge-adapter/INTEGRATION_REVIEW.md +++ b/openspec/changes/archive/2026-01-01-implement-openspec-bridge-adapter/INTEGRATION_REVIEW.md @@ -28,6 +28,7 @@ This document reviews the OpenSpec bridge adapter proposal against all relevant ### 1. Bridge Adapter Data Model Plan **Plan Requirements**: + - βœ… Plugin-based adapter architecture (AdapterRegistry, BridgeAdapter interface) - βœ… No hard-coded adapter checks in core - βœ… No hard-coded detection logic @@ -35,6 +36,7 @@ This document reviews the OpenSpec bridge adapter proposal against all relevant - βœ… Cross-repository support via `external_base_path` **Proposal Status**: + - βœ… Creates `OpenSpecAdapter` implementing `BridgeAdapter` interface - βœ… Registers adapter in `AdapterRegistry` - ⚠️ **MISMATCH**: Adds hard-coded `_is_openspec_repo()` and `_detect_openspec()` in BridgeProbe @@ -42,6 +44,7 @@ This document reviews the OpenSpec bridge adapter proposal against all relevant - βœ… Uses adapter registry in BridgeSync (mentioned but needs explicit refactoring task) **Required Fixes**: + 1. **Refactor BridgeProbe** to use adapter registry (section 1.6 requirement) 2. **Refactor BridgeSync** to remove all hard-coded adapter checks 3. **Document parser location** decision (adapter module vs shared utility) @@ -51,12 +54,14 @@ This document reviews the OpenSpec bridge adapter proposal against all relevant ### 2. OpenSpec Integration Plan **Plan Requirements**: + - βœ… Phase 1: Read-only sync (OpenSpec β†’ SpecFact) - βœ… Cross-repository support - βœ… Alignment report generation - βœ… Plugin-based adapter architecture **Proposal Status**: + - βœ… Implements Phase 1 (read-only sync) - βœ… Supports cross-repository via `external_base_path` - βœ… Generates alignment reports @@ -68,12 +73,14 @@ This document reviews the OpenSpec bridge adapter proposal against all relevant ### 3. OpenSpec Data Model Plan **Plan Requirements**: + - βœ… Change tracking accessed via adapter interface (not hard-coded paths) - βœ… Tool-specific metadata in `source_tracking.source_metadata` - βœ… Adapter decides storage location - βœ… No hard-coded paths in core **Proposal Status**: + - βœ… Uses adapter interface for change tracking - βœ… Stores OpenSpec paths in `source_tracking.source_metadata` - βœ… Adapter handles storage location @@ -85,18 +92,21 @@ This document reviews the OpenSpec bridge adapter proposal against all relevant ### 4. OpenSpec Implementation Requirements **Plan Requirements**: + - βœ… Section 1.5: Create OpenSpec Adapter (plugin-based) - **REQUIRED** - βœ… Section 1.6: Refactor BridgeProbe to use adapter registry - **REQUIRED** - βœ… Section 1.5: Update BridgeSync to use adapter registry - **REQUIRED** - βœ… No hard-coded adapter checks **Proposal Status**: + - βœ… Section 1.5: Creates OpenSpecAdapter - **COMPLETE** - βœ… **FIXED**: Section 3 explicitly requires BridgeProbe refactoring to use adapter registry - βœ… **FIXED**: Section 6 explicitly requires BridgeSync refactoring to remove hard-coded checks - βœ… **FIXED**: Proposal explicitly forbids adding hard-coded detection methods **Fixes Applied**: + 1. βœ… **Section 3** - Added explicit tasks to refactor BridgeProbe.detect() to use adapter registry 2. βœ… **Section 6** - Added explicit tasks to refactor BridgeSync.import_artifact() to remove hard-coded checks 3. βœ… **Section 2** - Added requirement to add `get_capabilities()` to BridgeAdapter interface @@ -109,6 +119,7 @@ This document reviews the OpenSpec bridge adapter proposal against all relevant ### Mismatch 1: BridgeProbe Hard-Coded Detection βœ… FIXED **Original Issue**: + ```python # ❌ BAD: Would have added hard-coded methods class BridgeProbe: @@ -122,6 +133,7 @@ class BridgeProbe: ``` **Required Pattern** (from plans): + ```python # βœ… GOOD: Uses adapter registry class BridgeProbe: @@ -138,6 +150,7 @@ class BridgeProbe: ``` **Fix Applied**: + - βœ… **Section 3** - Explicitly requires refactoring `detect()` to use adapter registry - βœ… **Section 3.1** - Explicitly forbids adding `_is_openspec_repo()` or `_detect_openspec()` methods - βœ… **Section 3.1** - Requires removing existing hard-coded Spec-Kit detection methods @@ -149,6 +162,7 @@ class BridgeProbe: ### Mismatch 2: BridgeSync Hard-Coded Adapter Checks βœ… FIXED **Original Issue** (line 180): + ```python # ❌ BAD: Hard-coded adapter check if self.bridge_config.adapter == AdapterType.SPECKIT: @@ -158,6 +172,7 @@ else: ``` **Required Pattern** (from plans): + ```python # βœ… GOOD: Uses adapter registry from specfact_cli.adapters.registry import AdapterRegistry @@ -167,6 +182,7 @@ adapter.import_artifact(artifact_key, artifact_path, project_bundle, bridge_conf ``` **Fix Applied**: + - βœ… **Section 6.1** - Explicitly requires removing `_import_speckit_artifact()` and `_import_generic_markdown()` methods - βœ… **Section 6.1.1** - Explicitly forbids adding `_import_openspec_artifact()` method - βœ… **Section 6.2** - Detailed tasks for refactoring `import_artifact()` to use adapter registry @@ -178,9 +194,11 @@ adapter.import_artifact(artifact_key, artifact_path, project_bundle, bridge_conf ### Mismatch 3: Parser Location Decision βœ… FIXED **Original Proposal**: + - Parser in `src/specfact_cli/sync/openspec_parser.py` **Decision Applied**: + - βœ… **Section 4** - Parser location changed to `src/specfact_cli/adapters/openspec_parser.py` (adapter-specific) - βœ… **Proposal** - Updated to reflect parser as adapter-specific implementation detail - βœ… **Rationale**: Parser is adapter-specific, not shared utility, so it belongs in adapter module @@ -192,10 +210,12 @@ adapter.import_artifact(artifact_key, artifact_path, project_bundle, bridge_conf ### Mismatch 4: Missing BridgeAdapter.get_capabilities() Method βœ… FIXED **Original Issue**: + - BridgeAdapter interface had `detect()` method - Missing `get_capabilities()` method required by BridgeProbe **Required Addition**: + ```python class BridgeAdapter(ABC): @abstractmethod @@ -206,6 +226,7 @@ class BridgeAdapter(ABC): ``` **Fix Applied**: + - βœ… **Section 2** - Added requirement to add `get_capabilities()` to BridgeAdapter interface - βœ… **Section 2.1** - Detailed tasks for adding abstract method with contract decorators - βœ… **Section 5.3** - Requires implementing `get_capabilities()` in OpenSpecAdapter @@ -220,6 +241,7 @@ All required updates have been applied to both `proposal.md` and `tasks.md`. Sum ### Update 1: Refactor BridgeProbe βœ… APPLIED **Status**: βœ… **Section 3** in tasks.md + - βœ… Section 3.1 - Remove hard-coded detection methods (explicitly forbids OpenSpec methods) - βœ… Section 3.2 - Refactor `detect()` method to use adapter registry - βœ… Section 3.3 - Refactor `auto_generate_bridge()` to use adapter registry @@ -230,6 +252,7 @@ All required updates have been applied to both `proposal.md` and `tasks.md`. Sum ### Update 2: Refactor BridgeSync βœ… APPLIED **Status**: βœ… **Section 6** in tasks.md + - βœ… Section 6.1 - Remove hard-coded adapter checks (explicitly forbids OpenSpec method) - βœ… Section 6.2 - Refactor `import_artifact()` to use adapter registry - βœ… Section 6.3 - Refactor `export_artifact()` similarly @@ -242,6 +265,7 @@ All required updates have been applied to both `proposal.md` and `tasks.md`. Sum ### Update 3: Parser Location Decision βœ… APPLIED **Status**: βœ… **Section 4** in tasks.md and **proposal.md** + - βœ… Parser location: `src/specfact_cli/adapters/openspec_parser.py` (adapter-specific) - βœ… Decision documented: Parser is adapter-specific implementation detail - βœ… Proposal updated to reflect this decision @@ -251,6 +275,7 @@ All required updates have been applied to both `proposal.md` and `tasks.md`. Sum ### Update 4: Add get_capabilities() to BridgeAdapter Interface βœ… APPLIED **Status**: βœ… **Section 2** in tasks.md + - βœ… Section 2.1 - Add `get_capabilities()` method to BridgeAdapter base class - βœ… Section 2.2 - Implement in existing adapters (GitHubAdapter, OpenSpecAdapter) - βœ… Section 2.3 - Quality checks included diff --git a/openspec/changes/archive/2026-01-01-implement-openspec-bridge-adapter/proposal.md b/openspec/changes/archive/2026-01-01-implement-openspec-bridge-adapter/proposal.md index 70171d3f..cc69fbbc 100644 --- a/openspec/changes/archive/2026-01-01-implement-openspec-bridge-adapter/proposal.md +++ b/openspec/changes/archive/2026-01-01-implement-openspec-bridge-adapter/proposal.md @@ -90,20 +90,6 @@ This change implements Phase 1 (read-only sync) of the OpenSpec integration, ena - βœ… Integration tests pass - βœ… Test coverage β‰₯80% - - - - - - - - - - - - - - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-01-02-refactor-speckit-to-bridge-adapter/proposal.md b/openspec/changes/archive/2026-01-02-refactor-speckit-to-bridge-adapter/proposal.md index ede2bcd6..6bb0bb39 100644 --- a/openspec/changes/archive/2026-01-02-refactor-speckit-to-bridge-adapter/proposal.md +++ b/openspec/changes/archive/2026-01-02-refactor-speckit-to-bridge-adapter/proposal.md @@ -98,20 +98,18 @@ The OpenSpec adapter implementation demonstrated the correct pattern: all adapte - `src/specfact_cli/cli.py` (remove bridge and implement command registrations, update sdd command) - `src/specfact_cli/commands/__init__.py` (remove implement import) - `tests/` (update tests to use adapter registry, remove SpecKitSync tests, add bidirectional sync tests, update constitution command tests, update import command tests) -- **Breaking changes**: +- **Breaking changes**: - **Command change**: `specfact bridge constitution` β†’ `specfact sdd constitution` (breaking CLI change) - **Removed class**: `SpecKitSync` class removed (breaking API change for any code using it directly) - **Removed command**: `specfact bridge` command removed (breaking CLI change) - **Removed command**: `specfact implement` command removed (breaking CLI change - deprecated in v0.17.0) - **Removed command**: `specfact generate tasks` command removed (breaking CLI change - deprecated per SPECFACT_0x_TO_1x_BRIDGE_PLAN.md) -- **Migration**: +- **Migration**: - Update scripts/tooling using `specfact bridge constitution` to use `specfact sdd constitution` - Any code directly using `SpecKitSync` must migrate to `SpecKitAdapter` via adapter registry - **For task generation**: Use Spec-Kit, OpenSpec, or other SDD tools (SpecFact CLI complements these tools for enforcement, not task creation) - **For code implementation**: Use `specfact generate fix-prompt` and AI IDE tools (SpecFact CLI provides prompts, not code generation in 0.x) - - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-01-03-enhance-cli-terminal-output/design.md b/openspec/changes/archive/2026-01-03-enhance-cli-terminal-output/design.md index 289e3339..0eb87bb8 100644 --- a/openspec/changes/archive/2026-01-03-enhance-cli-terminal-output/design.md +++ b/openspec/changes/archive/2026-01-03-enhance-cli-terminal-output/design.md @@ -24,17 +24,20 @@ SpecFact CLI uses Rich Console and Progress bars for user feedback, but these do **What**: Detect terminal capabilities via environment variables and TTY checks -**Why**: +**Why**: + - Rich Console supports `force_terminal`, `no_color`, `is_terminal` parameters - Environment variables (NO_COLOR, FORCE_COLOR, CI) are standard indicators - TTY detection distinguishes interactive vs non-interactive terminals **Alternatives considered**: + - Manual `--no-color` flag: Adds complexity, users forget to use it - Always use plain text: Loses Rich features in full terminals - Terminal library: Adds dependency, Rich already has detection **Implementation**: + ```python def detect_terminal_capabilities() -> TerminalCapabilities: """Detect terminal capabilities from environment and TTY.""" @@ -60,16 +63,19 @@ def detect_terminal_capabilities() -> TerminalCapabilities: **What**: Use Rich Progress for full terminals, plain text for basic terminals **Why**: + - Rich Progress with animations doesn't work in embedded terminals - Plain text updates are visible in CI/CD logs - Same information content in both modes **Alternatives considered**: + - Always use Rich: Breaks in embedded terminals - Always use plain text: Loses Rich features unnecessarily - Custom progress library: Adds dependency, Rich is already used **Implementation**: + ```python def get_progress_config() -> dict: """Get Progress configuration based on terminal capabilities.""" @@ -103,16 +109,19 @@ def get_progress_config() -> dict: **What**: Cache Console instance per terminal mode to avoid repeated detection **Why**: + - Terminal capabilities don't change during command execution - Console creation is lightweight but detection logic should run once - Simplifies usage in command modules **Alternatives considered**: + - Create Console per command: Works but redundant detection - Global Console instance: Breaks when terminal mode changes (unlikely but possible) - No caching: Works but inefficient **Implementation**: + ```python _console_cache: dict[TerminalMode, Console] = {} @@ -130,16 +139,19 @@ def get_configured_console() -> Console: **What**: Emit periodic plain text status updates when animations disabled **Why**: + - Users need feedback that command is working - CI/CD logs need readable progress information - Plain text is universally supported **Alternatives considered**: + - No fallback: Users see nothing (current problem) - Always emit: Works but verbose in graphical terminals - Throttled updates: Best balance (chosen) **Implementation**: + ```python def print_progress(description: str, current: int, total: int) -> None: """Print plain text progress update.""" @@ -156,7 +168,8 @@ def print_progress(description: str, current: int, total: int) -> None: **Risk**: Auto-detection incorrectly identifies terminal capabilities -**Mitigation**: +**Mitigation**: + - Use standard environment variables (NO_COLOR, FORCE_COLOR) - Prefer explicit overrides (FORCE_COLOR=1) - Fall back to basic mode when uncertain @@ -166,6 +179,7 @@ def print_progress(description: str, current: int, total: int) -> None: **Risk**: Terminal detection adds overhead to command startup **Mitigation**: + - Cache detection results - Detection is fast (env var reads, TTY check) - One-time cost per command execution @@ -175,6 +189,7 @@ def print_progress(description: str, current: int, total: int) -> None: **Risk**: Changes break existing Rich features in full terminals **Mitigation**: + - Test in both graphical and basic modes - Use Rich's built-in capabilities (no custom rendering) - Maintain same Console/Progress API usage diff --git a/openspec/changes/archive/2026-01-03-enhance-cli-terminal-output/proposal.md b/openspec/changes/archive/2026-01-03-enhance-cli-terminal-output/proposal.md index fa315b9d..0d597d93 100644 --- a/openspec/changes/archive/2026-01-03-enhance-cli-terminal-output/proposal.md +++ b/openspec/changes/archive/2026-01-03-enhance-cli-terminal-output/proposal.md @@ -75,8 +75,6 @@ Rich Console supports terminal detection via `force_terminal`, `no_color`, and ` - **Not implementing**: Custom terminal rendering (using Rich's built-in capabilities) - **Not adding**: New CLI flags for terminal mode (auto-detection only) - - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-01-04-improve-documentation-structure/proposal.md b/openspec/changes/archive/2026-01-04-improve-documentation-structure/proposal.md index 273417d0..9de53996 100644 --- a/openspec/changes/archive/2026-01-04-improve-documentation-structure/proposal.md +++ b/openspec/changes/archive/2026-01-04-improve-documentation-structure/proposal.md @@ -73,7 +73,6 @@ This change transforms SpecFact CLI documentation from scattered silos to unifie - **Target Repository**: `nold-ai/specfact-cli` (public) - **Estimated Effort**: 30-40 hours over 2 weeks - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-01-04-improve-documentation-structure/tasks.md b/openspec/changes/archive/2026-01-04-improve-documentation-structure/tasks.md index 73403fc3..e8cb98ca 100644 --- a/openspec/changes/archive/2026-01-04-improve-documentation-structure/tasks.md +++ b/openspec/changes/archive/2026-01-04-improve-documentation-structure/tasks.md @@ -203,7 +203,7 @@ - [x] 9.3.5.1 Verify branch is linked: Branch was created using `gh issue develop 78` (Step 1.1.2), which automatically links the branch to issue #78 - [x] 9.3.5.2 Verify PR is linked: PR body contains `Fixes nold-ai/specfact-cli#78`, which should automatically link the PR to issue #78 - [x] 9.3.5.3 **If automatic linking didn't work**: Manually link from issue's Development section: - - Open issue page: https://github.com/nold-ai/specfact-cli/issues/78 + - Open issue page: - In the right sidebar, find the "Development" section - Click "Development" and search for PR #79 (or branch `feature/improve-documentation-structure` if PR doesn't exist yet) - Select the PR/branch to link it to the issue @@ -211,7 +211,7 @@ - [x] 9.3.6 Update project status for issue #78 to "In Progress": `gh project item-edit --id PVTI_lADODWwjB84BKws4zgjMYnU --field-id PVTSSF_lADODWwjB84BKws4zg6iOak --project-id PVT_kwDODWwjB84BKws4 --single-select-option-id 47fc9ee4` (Status: "In Progress") - [x] 9.3.7 Update project status for PR #79 to "In Progress": `gh project item-edit --id PVTI_lADODWwjB84BKws4zgjMaxw --field-id PVTSSF_lADODWwjB84BKws4zg6iOak --project-id PVT_kwDODWwjB84BKws4 --single-select-option-id 47fc9ee4` (Status: "In Progress") - [x] 9.3.8 Verify Development link: PR and branch automatically linked to issue #78 (check issue page "Development" section) - - [x] 9.3.9 Verify project link: PR appears in project board (https://github.com/orgs/nold-ai/projects/1) + - [x] 9.3.9 Verify project link: PR appears in project board () - [x] 9.3.10 Cleanup PR body file: `rm /tmp/pr-body-improve-documentation-structure.md` **Validation**: Verify PR was created, Development link present (if issue exists), PR body follows template structure. diff --git a/openspec/changes/archive/2026-01-09-integrate-sidecar-validation/VERIFICATION-RESULTS.md b/openspec/changes/archive/2026-01-09-integrate-sidecar-validation/VERIFICATION-RESULTS.md index 58e8c69a..9dafbef5 100644 --- a/openspec/changes/archive/2026-01-09-integrate-sidecar-validation/VERIFICATION-RESULTS.md +++ b/openspec/changes/archive/2026-01-09-integrate-sidecar-validation/VERIFICATION-RESULTS.md @@ -291,6 +291,7 @@ All sidecar validation commands tested successfully against validation repositor - No crashes or errors βœ… **Flask Detection Fixed** (2026-01-09): Flask framework detection now works correctly + - **Fix Applied**: Added Flask pattern detection before Django `urls.py` check - **Result**: Flask correctly detected as `PURE_PYTHON` (Flask doesn't have a dedicated extractor yet) - **Detection Logic**: Checks for `from flask import Flask`, `import flask`, or `Flask()` patterns before checking for Django `urls.py` files diff --git a/openspec/changes/archive/2026-01-09-integrate-sidecar-validation/proposal.md b/openspec/changes/archive/2026-01-09-integrate-sidecar-validation/proposal.md index fa0065b6..9618a4f0 100644 --- a/openspec/changes/archive/2026-01-09-integrate-sidecar-validation/proposal.md +++ b/openspec/changes/archive/2026-01-09-integrate-sidecar-validation/proposal.md @@ -160,7 +160,6 @@ This change proposal consolidates and implements several related sidecar validat **Note**: This proposal implements the core CLI integration (#54) in Phases 0-8. Features from #55, #56, and #57 are included as Phases 9-11 in the implementation plan. - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-01-16-implement-adapter-enhancement-recommendations/proposal.md b/openspec/changes/archive/2026-01-16-implement-adapter-enhancement-recommendations/proposal.md index 81167897..72c211da 100644 --- a/openspec/changes/archive/2026-01-16-implement-adapter-enhancement-recommendations/proposal.md +++ b/openspec/changes/archive/2026-01-16-implement-adapter-enhancement-recommendations/proposal.md @@ -54,11 +54,6 @@ These enhancements will complete the adapter bridge architecture, enabling full - SpecFact validation (contract enforcement) - Backlog adapters (GitHub first, future: ADO, Jira, Linear) - bidirectional sync - - - - - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-01-18-fix-backlog-import-openspec-creation/proposal.md b/openspec/changes/archive/2026-01-18-fix-backlog-import-openspec-creation/proposal.md index 1903ad68..df449d48 100644 --- a/openspec/changes/archive/2026-01-18-fix-backlog-import-openspec-creation/proposal.md +++ b/openspec/changes/archive/2026-01-18-fix-backlog-import-openspec-creation/proposal.md @@ -2,7 +2,6 @@ ## Why - When importing backlog items (GitHub Issues, ADO Work Items) as OpenSpec change proposals via `specfact sync bridge --adapter github --bidirectional --backlog-ids `, the system currently only creates a `ChangeProposal` object and stores it in the project bundle's `change_tracking.proposals`. However, it does NOT create the required OpenSpec change artifacts: - `proposal.md` file (with proper OpenSpec format) @@ -13,7 +12,6 @@ This creates incomplete OpenSpec changes that cannot be validated, applied, or p ## What Changes - - **FIX**: Extend `import_backlog_items_to_bundle()` in `BridgeSync` to create OpenSpec change directory structure after importing to bundle - **FIX**: Add `_write_openspec_change_from_proposal()` method to `BridgeSync` that creates `proposal.md`, `tasks.md`, and spec deltas from imported `ChangeProposal` - **FIX**: Ensure `proposal.md` follows OpenSpec format (title format, required sections: Why, What Changes, Impact) @@ -33,7 +31,6 @@ This creates incomplete OpenSpec changes that cannot be validated, applied, or p - **EXTEND**: Add validation step after OpenSpec file creation to ensure format compliance - **EXTEND**: Add error handling for file creation failures (permissions, disk space, etc.) - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-01-18-fix-backlog-import-openspec-creation/tasks.md b/openspec/changes/archive/2026-01-18-fix-backlog-import-openspec-creation/tasks.md index 393ff150..2d8db6cd 100644 --- a/openspec/changes/archive/2026-01-18-fix-backlog-import-openspec-creation/tasks.md +++ b/openspec/changes/archive/2026-01-18-fix-backlog-import-openspec-creation/tasks.md @@ -115,7 +115,7 @@ - [x] 2.3 Create Pull Request using gh CLI - [x] 2.3.1 Create PR: `gh pr create --repo nold-ai/specfact-cli --base dev --head bugfix/fix-backlog-import-openspec-creation --title "fix: create OpenSpec files when importing backlog items" --body-file "$PR_BODY_FILE"` - - [x] 2.3.2 Verify PR was created and capture PR number (βœ… PR #118 created: https://github.com/nold-ai/specfact-cli/pull/118) + - [x] 2.3.2 Verify PR was created and capture PR number (βœ… PR #118 created: ) - [ ] 2.3.3 Link PR to project: `gh project item-add 1 --owner nold-ai --url "https://github.com/nold-ai/specfact-cli/pull/118"` (TODO: Requires project permissions) - [ ] 2.3.4 Update project status for PR to "In Progress" (TODO: Requires project permissions) - [x] 2.3.5 Cleanup PR body file: `rm /tmp/pr-body-fix-backlog-import-openspec-creation.md` diff --git a/openspec/changes/archive/2026-01-19-implement-sso-device-code-auth/proposal.md b/openspec/changes/archive/2026-01-19-implement-sso-device-code-auth/proposal.md index 8e77804e..e67432df 100644 --- a/openspec/changes/archive/2026-01-19-implement-sso-device-code-auth/proposal.md +++ b/openspec/changes/archive/2026-01-19-implement-sso-device-code-auth/proposal.md @@ -2,19 +2,21 @@ ## Why - - ### Current Limitation + SpecFact CLI currently supports only PAT (Personal Access Token) authentication, requiring users to manually create tokens in GitHub/Azure DevOps web interfaces. This creates friction during onboarding and adds secret management burden to users. ### Enterprise Problem + Organizations with SSO requirements (Entra ID, Okta, SAML) cannot adopt SpecFact CLI because: + - PATs bypass corporate SSO/MFA policies - No centralized identity governance - Creates compliance gaps in audit trails - Users expect device code flow (matching `az cli`, `gh cli` UX) ### Business Value + - **Market Expansion**: Enables SSO-required organizations (enterprise segment) - **UX Parity**: Matches developer expectations set by Azure CLI and GitHub CLI - **Support Reduction**: Eliminates PAT-related onboarding questions @@ -23,8 +25,6 @@ Organizations with SSO requirements (Entra ID, Okta, SAML) cannot adopt SpecFact ## What Changes - - - **MODIFY**: Architecture Overview - This change adds device code authentication flows for both Azure DevOps and GitHub, with token storage and CLI integration. @@ -48,6 +48,7 @@ Organizations with SSO requirements (Entra ID, Okta, SAML) cannot adopt SpecFact - **NEW**: CLI Integration - New command group: `specfact auth` - **Commands:** + ```bash # Authenticate with Azure DevOps (zero-config) specfact auth azure-devops @@ -72,7 +73,6 @@ Organizations with SSO requirements (Entra ID, Okta, SAML) cannot adopt SpecFact - 4. **PAT fallback**: Users can still use `--pat` flag; existing workflows preserved - 5. **Provider detection**: Auto-detects configured provider; users can override with flags - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-01-21-add-debug-mode-and-ado-auth-improvements/proposal.md b/openspec/changes/archive/2026-01-21-add-debug-mode-and-ado-auth-improvements/proposal.md index c638f040..b90b754e 100644 --- a/openspec/changes/archive/2026-01-21-add-debug-mode-and-ado-auth-improvements/proposal.md +++ b/openspec/changes/archive/2026-01-21-add-debug-mode-and-ado-auth-improvements/proposal.md @@ -2,13 +2,11 @@ ## Why - - Recent improvements to the SpecFact CLI require proper documentation and specification: 1. **Global Debug Output**: Users need a way to see diagnostic information (URLs, authentication status, API details) without cluttering normal output. Currently, debug messages are always shown or hidden, with no user control. -2. **ADO Authentication Issues**: +2. **ADO Authentication Issues**: - OAuth tokens expire after ~1 hour, requiring frequent re-authentication - Missing API tokens in requests due to incorrect Authorization header construction - ADO adapter not using centralized authentication helper methods @@ -22,8 +20,6 @@ This change adds global debug mode, improves ADO authentication with automatic t ## What Changes - - - **ADD**: Global `--debug` CLI flag that enables debug output across all commands - **ADD**: `debug_print()` helper function in runtime module for conditional debug output - **ADD**: `set_debug_mode()` and `is_debug_mode()` functions for debug state management @@ -36,7 +32,6 @@ This change adds global debug mode, improves ADO authentication with automatic t - **MODIFY**: Debug console.print statements in init.py to use `debug_print()` helper - **MODIFY**: ADO adapter debug output (URLs, auth status) to use `debug_print()` helper - --- ## Source Tracking @@ -45,4 +40,4 @@ This change adds global debug mode, improves ADO authentication with automatic t - **GitHub Issue**: #133 - **Issue URL**: - **Last Synced Status**: proposed -- **Sanitized**: true \ No newline at end of file +- **Sanitized**: true diff --git a/openspec/changes/archive/2026-01-21-add-debug-mode-and-ado-auth-improvements/specs/bridge-adapter/spec.md b/openspec/changes/archive/2026-01-21-add-debug-mode-and-ado-auth-improvements/specs/bridge-adapter/spec.md index 395146a7..12fa319d 100644 --- a/openspec/changes/archive/2026-01-21-add-debug-mode-and-ado-auth-improvements/specs/bridge-adapter/spec.md +++ b/openspec/changes/archive/2026-01-21-add-debug-mode-and-ado-auth-improvements/specs/bridge-adapter/spec.md @@ -40,6 +40,7 @@ The ADO adapter SHALL ensure organization is always included before project in A **And** this applies even when collection is already in base_url (on-premise) **Example URLs**: + - Cloud: `https://dev.azure.com/myorg/myproject/_apis/wit/wiql?api-version=7.1` - On-premise: `https://server/myorg/myproject/_apis/wit/wiql?api-version=7.1` diff --git a/openspec/changes/archive/2026-01-21-add-generic-backlog-abstraction/tasks.md b/openspec/changes/archive/2026-01-21-add-generic-backlog-abstraction/tasks.md index 7a5b3cbe..0521bd2f 100644 --- a/openspec/changes/archive/2026-01-21-add-generic-backlog-abstraction/tasks.md +++ b/openspec/changes/archive/2026-01-21-add-generic-backlog-abstraction/tasks.md @@ -163,4 +163,4 @@ - [x] 14.1.3 Push to remote: `git push origin feature/add-generic-backlog-abstraction` (βœ… Integrated in feature/add-template-driven-backlog-refinement branch, ready for push) - [x] 14.2 Create Pull Request - [x] 14.2.1 Note: This is an internal repository (specfact-cli-internal), so PR creation is skipped per workflow rules (βœ… Integrated with `add-template-driven-backlog-refinement` in PR #126) - - [x] 14.2.2 Changes are ready for review in the branch (βœ… PR #126: https://github.com/nold-ai/specfact-cli/pull/126) + - [x] 14.2.2 Changes are ready for review in the branch (βœ… PR #126: ) diff --git a/openspec/changes/archive/2026-01-27-fix-ado-field-mapping-missing-fields/tasks.md b/openspec/changes/archive/2026-01-27-fix-ado-field-mapping-missing-fields/tasks.md index 0b5f242d..0c355636 100644 --- a/openspec/changes/archive/2026-01-27-fix-ado-field-mapping-missing-fields/tasks.md +++ b/openspec/changes/archive/2026-01-27-fix-ado-field-mapping-missing-fields/tasks.md @@ -4,7 +4,7 @@ - [x] 1.1.1 Ensure we're on dev and up to date: `git checkout dev && git pull origin dev` - [x] 1.1.2 Create branch with Development link to issue: `gh issue develop 144 --repo nold-ai/specfact-cli --name bugfix/fix-ado-field-mapping-missing-fields --checkout` - [x] 1.1.3 Verify branch was created: `git branch --show-current` - - [x] 1.1.4 Verify Development link appears on issue page: https://github.com/nold-ai/specfact-cli/issues/144 + - [x] 1.1.4 Verify Development link appears on issue page: ## 2. Fix Missing Acceptance Criteria Field Mapping @@ -187,7 +187,7 @@ - [x] 8.1 Improve interactive mapping command - [x] 8.1.1 Implement regex/fuzzy matching for potential field matches when no default exists - [x] 8.1.2 Pre-populate default mappings (checking which exist in fetched fields) - - [x] 8.1.3 Prefer Microsoft.VSTS.Common.* fields over System.* fields + - [x] 8.1.3 Prefer Microsoft.VSTS.Common.*fields over System.* fields - [x] 8.1.4 Add --reset parameter to restore default mappings - [x] 8.1.5 Improve token resolution to use stored tokens from `specfact auth azure-devops` @@ -271,6 +271,6 @@ - [x] 10.3.7.2 Update status: Project status updates are typically managed via GitHub web interface or require project scope authentication. PR is visible in project board (verified in task 10.3.9). - [x] 10.3.8 Verify Development link: PR and branch automatically linked to issue (check issue page "Development" section) - **Status**: PR #145 body contains "Fixes #144". Branch created with `gh issue develop 144`. Both should be linked automatically. - - [x] 10.3.9 Verify project link: PR appears in project board (https://github.com/orgs/nold-ai/projects/1) + - [x] 10.3.9 Verify project link: PR appears in project board () - **Status**: PR #145 is in SpecFact CLI project board (verified via web interface). Status: Todo. - [x] 10.3.10 Cleanup PR body file: `rm /tmp/pr-body-fix-ado-field-mapping-missing-fields.md` diff --git a/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/CHANGE_VALIDATION.md index 5d38e451..269245f1 100644 --- a/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/CHANGE_VALIDATION.md @@ -61,7 +61,8 @@ **Decision**: Proceed with implementation **Rationale**: All changes are internal security fixes with no breaking changes. No dependent code requires updates. -**Next Steps**: +**Next Steps**: + 1. Changes have already been implemented 2. OpenSpec validation passed 3. GitHub issue created (#147) diff --git a/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/proposal.md b/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/proposal.md index cd38028c..c675f858 100644 --- a/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/proposal.md +++ b/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/proposal.md @@ -15,7 +15,7 @@ GitHub Code Scanning identified 13 security vulnerabilities in the public `specf ## Impact - Affected specs: None (code quality improvements, no spec changes) -- Affected code: +- Affected code: - `src/specfact_cli/backlog/mappers/github_mapper.py` - `src/specfact_cli/adapters/github.py` - `src/specfact_cli/sync/bridge_sync.py` diff --git a/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/specs/code-quality/spec.md b/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/specs/code-quality/spec.md index 93b97a04..6f8e86ca 100644 --- a/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/specs/code-quality/spec.md +++ b/openspec/changes/archive/2026-01-27-fix-code-scanning-vulnerabilities/specs/code-quality/spec.md @@ -1,19 +1,23 @@ ## MODIFIED Requirements ### Requirement: Code Security and Quality Standards + The system SHALL implement security best practices and maintain code quality standards to prevent vulnerabilities and follow least-privilege security models. #### Scenario: ReDoS Vulnerability Mitigation + - **WHEN** processing markdown content with many newline repetitions - **THEN** the system SHALL use line-by-line processing instead of regex patterns that may cause exponential backtracking - **AND** the processing SHALL complete in reasonable time without denial of service #### Scenario: URL Validation Security + - **WHEN** validating URLs for GitHub or Azure DevOps repositories - **THEN** the system SHALL use proper URL parsing with `urllib.parse.urlparse()` to validate hostnames - **AND** the system SHALL match hostnames exactly (not as substrings) to prevent matching malicious domains like "evil-github.com" #### Scenario: GitHub Actions Least Privilege + - **WHEN** GitHub Actions workflows execute - **THEN** each job SHALL have explicit `permissions` blocks defined - **AND** permissions SHALL follow the least-privilege model (e.g., `contents: read` for read-only operations) diff --git a/openspec/changes/archive/2026-01-29-add-debug-logs-specfact-home/proposal.md b/openspec/changes/archive/2026-01-29-add-debug-logs-specfact-home/proposal.md index 473d5ae4..9793da6e 100644 --- a/openspec/changes/archive/2026-01-29-add-debug-logs-specfact-home/proposal.md +++ b/openspec/changes/archive/2026-01-29-add-debug-logs-specfact-home/proposal.md @@ -22,7 +22,7 @@ When `--debug` is enabled globally, users and developers need consistent debug l ## Impact - **Affected specs**: debug-logging (new capability). -- **Affected code**: `src/specfact_cli/runtime.py` (debug_print file routing, _append_debug_log, debug_log_operation, timestamp formatter, caller context); `src/specfact_cli/common/logger_setup.py` (get_specfact_home_logs_dir, plain_text_for_debug_log, format_debug_log_message); `src/specfact_cli/cli.py` (debug file init); `src/specfact_cli/adapters/ado.py`, `src/specfact_cli/adapters/github.py` (operation metadata with payload/response/status/error/reason); all command modules under `src/specfact_cli/commands/` for consistent debug_print/debug_log_operation (auth, init, backlog_commands already; analyze, contract_cmd, drift, enforce, generate, import_cmd, migrate, plan, project_cmd, repro, sdd, spec, sync, update, validate to be covered). +- **Affected code**: `src/specfact_cli/runtime.py` (debug_print file routing,_append_debug_log, debug_log_operation, timestamp formatter, caller context); `src/specfact_cli/common/logger_setup.py` (get_specfact_home_logs_dir, plain_text_for_debug_log, format_debug_log_message); `src/specfact_cli/cli.py` (debug file init); `src/specfact_cli/adapters/ado.py`, `src/specfact_cli/adapters/github.py` (operation metadata with payload/response/status/error/reason); all command modules under `src/specfact_cli/commands/` for consistent debug_print/debug_log_operation (auth, init, backlog_commands already; analyze, contract_cmd, drift, enforce, generate, import_cmd, migrate, plan, project_cmd, repro, sdd, spec, sync, update, validate to be covered). - **Integration points**: runtime.set_debug_mode / is_debug_mode (existing); LoggerSetup.redact_secrets (existing). ## Source Tracking diff --git a/openspec/changes/archive/2026-01-29-improve-ado-backlog-refine-error-logging/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-01-29-improve-ado-backlog-refine-error-logging/CHANGE_VALIDATION.md index e8ca5d67..5bfc6e25 100644 --- a/openspec/changes/archive/2026-01-29-improve-ado-backlog-refine-error-logging/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-01-29-improve-ado-backlog-refine-error-logging/CHANGE_VALIDATION.md @@ -41,7 +41,7 @@ None. Change only extends failure paths (capture response, log in debug, surface ### Recommendations for implementation (make it β€œreally good”) -1. **Actionable hint with doc link**: In implementation, include a concrete link in the hint when available (e.g. β€œSee https://github.com/nold-ai/specfact-cli/docs/... or ado_custom.yaml”) so corporate users can resolve mapping issues without searching. +1. **Actionable hint with doc link**: In implementation, include a concrete link in the hint when available (e.g. β€œSee ... or ado_custom.yaml”) so corporate users can resolve mapping issues without searching. 2. **Optional --debug pointer**: Consider appending to the hint: β€œRun with --debug and check ~/.specfact/logs for full response and patch paths.” so users know how to get more detail when needed. 3. **Log every failed attempt**: In the backlog-refine PATCH path there are multiple retries (omit multilineFieldsFormat, replace add with replace, HTML fallback). Call the logging helper before each retry and on final failure so the debug log shows the sequence of attempts and the final response/paths. 4. **Highlight field name in message**: When the ADO message contains a field reference (e.g. β€œSystem.AcceptanceCriteria”), consider quoting or emphasizing it in the user message (e.g. β€œField β€˜System.AcceptanceCriteria’ not found. Check custom field mapping…”) so the failing field is obvious at a glance. diff --git a/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/proposal.md b/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/proposal.md index 1bf0a845..52df6cb5 100644 --- a/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/proposal.md +++ b/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/proposal.md @@ -6,7 +6,7 @@ The `specfact backlog refine` command supports `--export-to-tmp` to export items ## What Changes -- **NEW**: Parser for the refined export markdown format (same structure as export: `## Item N:`, **ID**, **Body** in ```markdown ... ```, **Acceptance Criteria**, optional title/metrics). Parser returns a list of parsed blocks keyed by item ID for matching against fetched items. +- **NEW**: Parser for the refined export markdown format (same structure as export: `## Item N:`, **ID**, **Body** in ```markdown ...```, **Acceptance Criteria**, optional title/metrics). Parser returns a list of parsed blocks keyed by item ID for matching against fetched items. - **NEW**: Import branch in `specfact backlog refine`: when `--import-from-tmp` is set and the file exists, read and parse the file, match parsed blocks to currently fetched items by ID, update each matched `BacklogItem`'s `body_markdown` and `acceptance_criteria` (and optionally title/metrics), then call `adapter.update_backlog_item(item, update_fields=[...])` when `--write` is set. Without `--write`, show a preview (e.g. "Would update N items") and do not call the adapter. - **EXTEND**: Reuse existing refine flow: same adapter/fetch as export so `items` is in scope; reuse `_build_adapter_kwargs` and `adapter_registry.get_adapter` for write-back; reuse the same `update_fields` logic as interactive refine (title, body_markdown, acceptance_criteria, story_points, business_value, priority). - **NOTE**: Default import path remains `...-refined.md`; users are expected to pass `--tmp-file` to point to the file they edited (same path as export or a copy). No change to export path or naming. @@ -19,7 +19,7 @@ The `specfact backlog refine` command supports `--export-to-tmp` to export items - **Affected specs**: backlog-refinement (ADDED scenario for import-from-tmp). - **Affected code**: `src/specfact_cli/commands/backlog_commands.py` (import branch implementation); optionally `src/specfact_cli/backlog/refine_export_parser.py` (parser helper). -- **Integration points**: BacklogAdapter.update_backlog_item (existing); _fetch_backlog_items, _build_adapter_kwargs (existing). +- **Integration points**: BacklogAdapter.update_backlog_item (existing); _fetch_backlog_items,_build_adapter_kwargs (existing). ## Source Tracking diff --git a/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/specs/backlog-refinement/spec.md b/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/specs/backlog-refinement/spec.md index b20a6a5d..47390ae1 100644 --- a/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/specs/backlog-refinement/spec.md +++ b/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/specs/backlog-refinement/spec.md @@ -8,7 +8,7 @@ The system SHALL support importing refined backlog content from a temporary mark #### Scenario: Import refined content from temporary file -- **GIVEN** a markdown file in the same format as the export from `specfact backlog refine --export-to-tmp` (header, then per-item blocks with `## Item N:`, **ID**, **Body** in ```markdown ... ```, **Acceptance Criteria**) +- **GIVEN** a markdown file in the same format as the export from `specfact backlog refine --export-to-tmp` (header, then per-item blocks with `## Item N:`, **ID**, **Body** in ```markdown ...```, **Acceptance Criteria**) - **AND** the user runs `specfact backlog refine --import-from-tmp --tmp-file ` with the same adapter and filters as used for export (so the same set of items is fetched) - **WHEN** the import file exists and is readable - **THEN** the system parses the file and matches each block to a fetched item by **ID** diff --git a/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/tasks.md b/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/tasks.md index 8a818e7e..000d957f 100644 --- a/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/tasks.md +++ b/openspec/changes/archive/2026-01-30-implement-backlog-refine-import-from-tmp/tasks.md @@ -10,7 +10,7 @@ - [x] 2.1.1 Add function to parse refined markdown (e.g. `_parse_refined_export_markdown(content: str) -> dict[str, dict]` returning id β†’ {body_markdown, acceptance_criteria, title?, ...}) in `backlog_commands.py` or new module `src/specfact_cli/backlog/refine_export_parser.py` - [x] 2.1.2 Split content by `## Item` or `---` to get per-item blocks -- [x] 2.1.3 From each block extract **ID** (required), **Body** (from ```markdown ... ```), **Acceptance Criteria** (optional), optionally **title** and metrics +- [x] 2.1.3 From each block extract **ID** (required), **Body** (from ```markdown ...```), **Acceptance Criteria** (optional), optionally **title** and metrics - [x] 2.1.4 Add unit tests for parser (export-format sample, multiple items, missing optional fields) - [x] 2.1.5 Run `hatch run format` and `hatch run type-check` diff --git a/openspec/changes/archive/2026-02-02-daily-standup-progress-support/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-02-02-daily-standup-progress-support/CHANGE_VALIDATION.md index d1bc416e..7521d772 100644 --- a/openspec/changes/archive/2026-02-02-daily-standup-progress-support/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-02-02-daily-standup-progress-support/CHANGE_VALIDATION.md @@ -63,7 +63,7 @@ None. Change is additive: new standup view and optional post standup comment; ex ## Recommended Improvements Applied 1. **GitHub issue mandatory**: Task 2 explicitly creates issue in nold-ai/specfact-cli and updates proposal Source Tracking. -2. **Patch version and changelog**: Task 8 bumps patch version, syncs pyproject.toml/setup.py/src __init__.py, and adds CHANGELOG.md entry. Optional: Task 8.3 CHANGELOG line could mention `specfact backlog daily` for discoverability. +2. **Patch version and changelog**: Task 8 bumps patch version, syncs pyproject.toml/setup.py/src **init**.py, and adds CHANGELOG.md entry. Optional: Task 8.3 CHANGELOG line could mention `specfact backlog daily` for discoverability. 3. **TDD order**: TDD/SDD section at top of tasks.md; Section 4 (tests first, expect failure) before Section 5 (implement until tests pass). 4. **Integration note**: Design and validation note that existing `--add-progress-comment` in sync bridge can be aligned or extended for standup comment format to avoid duplication. 5. **Backlog harmonization**: All agile/standup behavior is under `specfact backlog daily`; no top-level `specfact standup` or scrum command. diff --git a/openspec/changes/archive/2026-02-02-daily-standup-progress-support/proposal.md b/openspec/changes/archive/2026-02-02-daily-standup-progress-support/proposal.md index b0e1a803..6d826428 100644 --- a/openspec/changes/archive/2026-02-02-daily-standup-progress-support/proposal.md +++ b/openspec/changes/archive/2026-02-02-daily-standup-progress-support/proposal.md @@ -2,12 +2,10 @@ ## Why - Bridge comments and sync already support exporting/updating change proposals and issues. For daily standup there is no structured "standup" view that aggregates my items, recent activity, and blockers; progress/standup notes are not first-class (e.g. yesterday/today/blockers format) that could be pushed to issue comments. Teams duplicate standup info in tools; SpecFact can surface progress from OpenSpec/bridge and optionally publish to GitHub/ADO so standup updates are visible where the team works. Daily standups should focus on current iteration/sprint, unassigned work for commitment, and early visibility of blockers and time-critical items so DevOps teams can deliver the right features. ## What Changes - - **NEW**: Add a lightweight standup/progress view under the backlog command group: list my change proposals or backlog items (by assignee or filter), with last-updated and status; optional one-line summary for yesterday/today/blockers from proposal or linked issue body. Expose as `specfact backlog daily` (no top-level `specfact standup`). - **NEW**: Optional mode to post standup summary as a comment on linked issues via `specfact backlog daily` (e.g. `--post`) or reuse of `specfact sync bridge --add-progress-comment` with standup format (e.g. GitHub issue comment). - **NEW**: Default standup scope for meaningful daily standups: default state filter (e.g. open/active so closed items are excluded), optional default assignee (e.g. current user / "me" when resolvable), and default limit (e.g. 20–30 items). Configurable via environment variables and/or `.specfact/standup.yaml` (or equivalent) so teams can tailor defaults. @@ -24,6 +22,7 @@ Bridge comments and sync already support exporting/updating change proposals and - **NEW**: `--summarize` flag: when set, the CLI produces a **prompt** (instructions plus applied filters and filtered standup output) suitable for use in an interactive slash-command prompt (e.g. `specfact.daily`) or copy-paste to Copilot, so an LLM can generate a meaningful **summary of the daily standup status**. Output includes filter context (adapter, state, sprint, assignee, limit) and the same per-item data as `--copilot-export`; format is prompt-ready for "generate a standup summary from this data." ## Capabilities + - **daily-standup**: Standup view (list my/filtered items with status and last activity; optional standup summary lines; default scope for state/assignee/limit; current iteration/sprint and sprint end date when supported; separate unassigned/pending items for commitment; optional blockers-first and priority/value for time-critical and value-driven focus; Kanban/Scrum/SAFe usage) and optional post standup comment to linked issue via adapter. - **daily-standup-interactive**: Interactive selection (arrow-key, e.g. questionary) for step-by-step walkthrough of user stories in scope; per-item detail view (refine-like: description, acceptance criteria, progress, existing comments); optional next-best-item suggestion using value score (e.g. business value / (story points Γ— priority)) when sprint goal/complexity/value/priority are available; alignment with sprint goal when defined and available. - **daily-standup-copilot-export**: Export to file (e.g. `--copilot-export `) of summarized progress per story (active/blocked/todo in current iteration/sprint) for Copilot slash-command use during standupβ€”next steps and current progress per story for team discussion; complementary aid, not replacement for backlog. diff --git a/openspec/changes/archive/2026-02-06-arch-03-module-lifecycle-management/proposal.md b/openspec/changes/archive/2026-02-06-arch-03-module-lifecycle-management/proposal.md index b032f0e6..3f420df9 100644 --- a/openspec/changes/archive/2026-02-06-arch-03-module-lifecycle-management/proposal.md +++ b/openspec/changes/archive/2026-02-06-arch-03-module-lifecycle-management/proposal.md @@ -2,14 +2,12 @@ ## Why - `arch-02` completed module package separation, but module lifecycle constraints are still unenforced at runtime. `module_dependencies` is currently declarative-only, module manifests do not constrain CLI core compatibility, and `init --disable-module` can disable required modules without preflight protection. This creates avoidable runtime breakage and weakens contract-first guarantees for modular command loading. We need registry-time lifecycle validation and safe-disable protection while preserving backward compatibility. ## What Changes - - **NEW**: Add module lifecycle validation at registration time for dependency existence/enabled state and `core_compatibility` version constraints. - **NEW**: Extend module manifests with `core_compatibility` (PEP 440 specifier string) across all module packages. - **NEW**: Add safe-disable checks in `specfact init` so disabling a module fails when enabled dependents require it, with explicit `--force` override. @@ -22,6 +20,7 @@ This creates avoidable runtime breakage and weakens contract-first guarantees fo - **EXTEND**: Update user-facing documentation and changelog/version synchronization for lifecycle management behavior and module manifest schema. ## Capabilities + - **module-lifecycle-management**: Enforce module dependency integrity, version compatibility, safe-disable semantics, and module boundary hygiene. --- diff --git a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/CHANGE_VALIDATION.md index d90a2c60..0e3e8162 100644 --- a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/CHANGE_VALIDATION.md @@ -27,6 +27,7 @@ ### New Interfaces Added #### ModuleIOContract Protocol + - **File**: `src/specfact_cli/contracts/module_interface.py` - **Type**: New Protocol (structural subtyping) - **Methods**: @@ -38,6 +39,7 @@ - **Breaking**: ❌ No #### ValidationReport Model + - **File**: `src/specfact_cli/models/validation.py` - **Type**: New Pydantic model - **Fields**: `status`, `violations`, `summary` @@ -47,6 +49,7 @@ ### Modified Interfaces #### ProjectBundle + - **Old Signature**: No schema_version field - **New Signature**: Adds `schema_version: str = "1"` field - **Impact**: Backward compatible (default value provided) @@ -54,6 +57,7 @@ - **Dependent Files**: All code using ProjectBundle continues to work #### ModulePackageMetadata + - **Old Signature**: No schema_version or protocol_operations fields - **New Signature**: Adds optional fields with defaults - `schema_version: str | None = None` @@ -96,6 +100,7 @@ All 5 modules are updated to **implement** ModuleIOContract, not modified: ### Registry Updated (Non-Breaking) **module_packages.py** (`src/specfact_cli/registry/module_packages.py`) + - **Change**: Add protocol compliance detection via hasattr() checks - **Impact**: New validation layer, existing modules work without protocol - **Breaking**: ❌ No @@ -151,6 +156,7 @@ All 5 modules are updated to **implement** ModuleIOContract, not modified: ### proposal.md Format βœ… **Pass** + - βœ… Title: `# Change: Core Contracts and Module Interface Formalization` - βœ… Section: `## Why` (motivation clear) - βœ… Section: `## What Changes` (NEW/MODIFY markers present) @@ -161,6 +167,7 @@ All 5 modules are updated to **implement** ModuleIOContract, not modified: ### tasks.md Format βœ… **Pass** + - βœ… TDD/SDD Order: Documented at top with enforcement note - βœ… Task Format: `- [ ] X.Y` checkboxes (83 tasks total) - βœ… Git Workflow: Task 1 = branch creation, Task 20 = PR creation @@ -174,6 +181,7 @@ All 5 modules are updated to **implement** ModuleIOContract, not modified: ### specs Format βœ… **Pass** + - βœ… 4 spec files created: - `specs/module-io-contract/spec.md` (new) - `specs/core-module-isolation/spec.md` (new) @@ -186,6 +194,7 @@ All 5 modules are updated to **implement** ModuleIOContract, not modified: ### design.md Format βœ… **Pass** + - βœ… Sections: Context, Goals/Non-Goals, Decisions, Risks/Trade-offs - βœ… Sequence diagram included (module registration flow) - βœ… Contract enforcement strategy documented @@ -195,6 +204,7 @@ All 5 modules are updated to **implement** ModuleIOContract, not modified: ### Config.yaml Compliance βœ… **Pass** + - βœ… TDD order enforced: Specs β†’ Tests (expect failure) β†’ Code - βœ… Contract requirements: All public APIs use @icontract + @beartype - βœ… Documentation: Research and review task included (Task 17) @@ -206,6 +216,7 @@ All 5 modules are updated to **implement** ModuleIOContract, not modified: ## OpenSpec Validation βœ… **Pass** + - **Status**: All artifacts complete (4/4) - **Command**: `openspec validate arch-04-core-contracts-interfaces --strict` - **Result**: Change 'arch-04-core-contracts-interfaces' is valid @@ -293,6 +304,7 @@ All 5 modules are updated to **implement** ModuleIOContract, not modified: This change introduces formal contracts for module interfaces in a fully backward-compatible way. All validations pass, no breaking changes detected, and the implementation path is clear with comprehensive TDD-first tasks. **Next Steps**: + 1. Create GitHub issue (Task 19) 2. Begin implementation following tasks.md 3. Use `/opsx:apply` to start task execution diff --git a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/design.md b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/design.md index e9e3d814..c141fd87 100644 --- a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/design.md +++ b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/design.md @@ -3,6 +3,7 @@ ## Context The modular architecture introduced in arch-01/02/03 (v0.27-0.29) provides: + - CommandRegistry with lazy loading (arch-01) - Module package separation with boundary guards (arch-02) - Module lifecycle management with dependency validation (arch-03) @@ -10,6 +11,7 @@ The modular architecture introduced in arch-01/02/03 (v0.27-0.29) provides: However, the core IO contract (ProjectBundle) lacks formal protocol definitions, and while boundary guards prevent cross-module imports, there's no enforcement preventing core code from importing modules. This creates coupling that would block marketplace adoption where 3rd-party modules must be truly pluggable without core modifications. **Current state:** + - ProjectBundle defined in `src/specfact_cli/models/project.py` as Pydantic BaseModel - Modules consume/produce ProjectBundle informally - No explicit `ModuleIOContract` protocol @@ -21,6 +23,7 @@ However, the core IO contract (ProjectBundle) lacks formal protocol definitions, ## Goals / Non-Goals **Goals:** + - Define formal `ModuleIOContract` protocol that all modules must implement - Enforce ProjectBundle as the ONLY IO contract between core and modules - Add static analysis to prevent coreβ†’module imports (inversion-of-control enforcement) @@ -28,6 +31,7 @@ However, the core IO contract (ProjectBundle) lacks formal protocol definitions, - Document ProjectBundle schema and module contract requirements for 3rd-party developers **Non-Goals:** + - Schema extension mechanism (deferred to arch-07) - Bridge registry for schema conversions (deferred to arch-05) - Cryptographic module signing (deferred to arch-06) @@ -41,12 +45,14 @@ However, the core IO contract (ProjectBundle) lacks formal protocol definitions, **Choice:** Use `typing.Protocol` for `ModuleIOContract` instead of ABC **Rationale:** + - Structural subtyping: Modules don't need explicit inheritance - Duck typing: Existing modules work without modification (opt-in formalization) - Static type checking: basedpyright verifies compliance without runtime overhead - Flexibility: Modules can implement subset of operations (e.g., sync-only modules) **Alternatives considered:** + - ABC with abstract methods: Requires explicit inheritance, breaks existing modules - No protocol: Informal contracts are error-prone and block marketplace verification @@ -55,12 +61,14 @@ However, the core IO contract (ProjectBundle) lacks formal protocol definitions, **Choice:** Define 4 required operations: `import_to_bundle`, `export_from_bundle`, `sync_with_bundle`, `validate_bundle` **Rationale:** + - **import_to_bundle**: External format β†’ ProjectBundle (e.g., ADO work items β†’ features) - **export_from_bundle**: ProjectBundle β†’ External format (e.g., features β†’ ADO work items) - **sync_with_bundle**: Bidirectional sync with conflict resolution - **validate_bundle**: Module-specific validation rules (e.g., backlog module validates feature IDs exist in ADO) **Alternatives considered:** + - Single `transform()` method: Too generic, loses operation semantics - Separate read/write protocols: Overcomplicates simple unidirectional modules @@ -69,17 +77,20 @@ However, the core IO contract (ProjectBundle) lacks formal protocol definitions, **Choice:** Parse core directory ASTs and fail if any `import specfact_cli.modules.*` found **Rationale:** + - Compile-time enforcement: Catches violations before PR merge - Zero runtime overhead: AST parsing in tests only - Clear error messages: Pinpoint file and line number of violation - CI-enforceable: Blocks PRs that add coreβ†’module coupling **Alternatives considered:** + - Runtime inspection: Overhead, detects after deployment - Import hooks: Complex, fragile, runtime cost - Manual code review: Error-prone, doesn't scale **Implementation:** + ```python # tests/unit/test_core_module_isolation.py def test_core_has_no_module_imports(): @@ -105,11 +116,13 @@ def test_core_has_no_module_imports(): **Choice:** Update 5 existing modules incrementally, mark protocol as optional initially **Rationale:** + - Non-breaking: Existing modules work without immediate changes - Incremental: Update backlog first (simplest), then sync, plan, generate, enforce - Validation: Module registration can check protocol compliance and warn if missing **Alternatives considered:** + - Big-bang migration: Risky, blocks PRs across modules - Never enforce: Defeats purpose, marketplace modules wouldn't be verifiable @@ -118,11 +131,13 @@ def test_core_has_no_module_imports(): **Choice:** Add `schema_version` field to ProjectBundle, document in reference docs **Rationale:** + - Forward compatibility: Future schema changes don't break old modules - Marketplace safety: 3rd-party modules declare compatible schema versions - Migration path: Old modules continue working, new modules use versioned features **Schema:** + ```python class ProjectBundle(BaseModel): schema_version: str = "1.0" # NEW @@ -133,6 +148,7 @@ class ProjectBundle(BaseModel): ``` **Alternatives considered:** + - No versioning: Schema changes break modules silently - Separate version file: Requires extra file management, error-prone @@ -143,6 +159,7 @@ class ProjectBundle(BaseModel): **Risk:** Updating 5 modules to implement protocol is time-consuming **Mitigation:** + - Start with simplest module (backlog) as template - Protocol is opt-in initially; registration warns but doesn't fail - Tasks include module-by-module migration with test validation @@ -152,6 +169,7 @@ class ProjectBundle(BaseModel): **Risk:** AST parsing might flag valid imports (e.g., type hints, if TYPE_CHECKING) **Mitigation:** + - Exclude `if TYPE_CHECKING:` blocks from analysis - Allow specific exceptions via config file if needed - Test against current codebase before enforcement @@ -161,6 +179,7 @@ class ProjectBundle(BaseModel): **Risk:** Future schema changes could break existing modules **Mitigation:** + - Schema versioning from day 1 - Extension fields (arch-07) allow backward-compatible additions - Deprecation policy for removals (2 minor versions notice) @@ -170,6 +189,7 @@ class ProjectBundle(BaseModel): **Risk:** Runtime protocol validation adds overhead **Mitigation:** + - Protocol is static type checking only (zero runtime cost) - Opt-in runtime checks via `isinstance(module, ModuleIOContract)` only in registration - CrossHair symbolic execution finds contract violations at CI time @@ -199,26 +219,31 @@ Per project's contract-first philosophy: ## Migration Plan **Phase 1: Foundation (Week 1, Days 1-2)** + - Create `src/specfact_cli/contracts/module_interface.py` with protocol - Add `tests/unit/test_core_module_isolation.py` static analysis test - Add CI enforcement in `.github/workflows/tests.yml` **Phase 2: Core Updates (Week 1, Days 3-4)** + - Add `schema_version` to ProjectBundle - Update module registration to check protocol compliance (warn only) - Document in `docs/reference/projectbundle-schema.md` **Phase 3: Module Migration (Week 1, Days 4-5)** + - Update backlog module (template for others) - Update sync, plan, generate, enforce modules - Validate with contract-first tests **Phase 4: Documentation (Week 1, Day 5)** + - Create `docs/reference/module-contracts.md` for 3rd-party developers - Update architecture docs with contract-first patterns - Update `docs/_layouts/default.html` navigation **Rollback Strategy:** + - Protocol is opt-in initially; disabling warnings reverts to pre-change behavior - Static analysis test can be skipped via `pytest -k "not test_core_module_isolation"` if needed - No breaking changes to existing module interfaces diff --git a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/proposal.md b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/proposal.md index b6fdd809..2e01068d 100644 --- a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/proposal.md +++ b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/proposal.md @@ -2,12 +2,10 @@ ## Why - The modular architecture (arch-01/02/03) provides strong encapsulation for parallel module development, but the core IO contract is not formally defined or enforced. Modules can still directly import from each other and core lacks explicit protocol definitions for module behavior. To enable a true marketplace for 3rd-party and community modules, we must formalize ProjectBundle as the ONLY IO contract and enforce zero coreβ†’module dependencies through static analysis. This establishes the contract foundation that all subsequent marketplace phases build upon. ## What Changes - - **NEW**: Create `src/specfact_cli/contracts/module_interface.py` with `ModuleIOContract` protocol defining the four core operations all modules must implement: `import_to_bundle()`, `export_from_bundle()`, `sync_with_bundle()`, and `validate_bundle()` - **NEW**: Add static analysis enforcement via `tests/unit/test_core_module_isolation.py` that parses AST and fails if core CLI code imports from `specfact_cli.modules.*` - **MODIFY**: Update existing modules (backlog, sync, plan, generate, enforce) to implement `ModuleIOContract` interface @@ -16,6 +14,7 @@ The modular architecture (arch-01/02/03) provides strong encapsulation for paral - **NEW**: Add CI enforcement of core isolation via static analysis test ## Capabilities + ### New Capabilities - `module-io-contract`: Protocol definition for module interfaces with ProjectBundle as the sole IO contract (import, export, sync, validate operations) @@ -40,7 +39,6 @@ The modular architecture (arch-01/02/03) provides strong encapsulation for paral - **Backward compatibility**: Non-breaking; adds formalized contracts to existing patterns. Existing modules work as-is but will be updated to explicitly implement the protocol. - **Release version**: Minor version bump (new feature/refactor, backward compatible) - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/core-module-isolation/spec.md b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/core-module-isolation/spec.md index bf799e01..e8979805 100644 --- a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/core-module-isolation/spec.md +++ b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/core-module-isolation/spec.md @@ -7,21 +7,25 @@ The system SHALL provide a pytest test `tests/unit/test_core_module_isolation.py` that parses AST of core CLI code and fails if any import from `specfact_cli.modules.*` is found. #### Scenario: Test scans core directories for module imports + - **WHEN** test_core_has_no_module_imports runs - **THEN** it SHALL scan all Python files in: cli.py, registry/, models/, utils/, contracts/ - **AND** SHALL parse each file's AST looking for Import and ImportFrom nodes #### Scenario: Test fails on direct module import + - **WHEN** core code contains `import specfact_cli.modules.backlog` - **THEN** test SHALL fail with message: ": imports specfact_cli.modules.backlog" - **AND** SHALL list exact file path and line number #### Scenario: Test fails on from-import of module code + - **WHEN** core code contains `from specfact_cli.modules.sync.src import commands` - **THEN** test SHALL fail with message: ": imports from specfact_cli.modules.sync.src" - **AND** SHALL prevent PR merge via CI #### Scenario: Test allows non-module imports + - **WHEN** core code contains `import specfact_cli.models` - **THEN** test SHALL pass - **AND** SHALL NOT flag imports from non-module core directories @@ -31,11 +35,13 @@ The system SHALL provide a pytest test `tests/unit/test_core_module_isolation.py The system SHALL exclude imports within `if TYPE_CHECKING:` blocks from static analysis violations. #### Scenario: Type hint import in TYPE_CHECKING block is allowed + - **WHEN** core code has `if TYPE_CHECKING: from specfact_cli.modules.backlog import BacklogAdapter` - **THEN** test SHALL pass - **AND** SHALL NOT flag as violation since it's only for type checking #### Scenario: Runtime import disguised as TYPE_CHECKING is caught + - **WHEN** code uses module import outside TYPE_CHECKING but has TYPE_CHECKING block elsewhere - **THEN** test SHALL still fail on the runtime import - **AND** SHALL distinguish between conditional type imports and runtime imports @@ -45,11 +51,13 @@ The system SHALL exclude imports within `if TYPE_CHECKING:` blocks from static a The system SHALL run `test_core_module_isolation.py` in `.github/workflows/tests.yml` and block PRs that violate core isolation. #### Scenario: CI runs isolation test on every PR + - **WHEN** PR is opened with core code changes - **THEN** GitHub Actions SHALL run `pytest tests/unit/test_core_module_isolation.py` - **AND** SHALL block merge if test fails #### Scenario: CI provides actionable error message on violation + - **WHEN** isolation test fails in CI - **THEN** GitHub Actions log SHALL show file path, line number, and import statement - **AND** SHALL guide developer to use registry pattern instead of direct import @@ -59,11 +67,13 @@ The system SHALL run `test_core_module_isolation.py` in `.github/workflows/tests The system SHALL format violation messages with file path, line number, and imported module name for easy debugging. #### Scenario: Violation message includes context + - **WHEN** violation is detected at src/specfact_cli/cli.py line 42 - **THEN** message SHALL be: "src/specfact_cli/cli.py:42 imports specfact_cli.modules.backlog.src.commands" - **AND** SHALL aggregate all violations before failing (not fail on first) #### Scenario: Multiple violations are reported together + - **WHEN** multiple core files import from modules - **THEN** test SHALL list all violations in a single failure message - **AND** SHALL show total count: "Found 3 core-to-module import violations" @@ -73,11 +83,13 @@ The system SHALL format violation messages with file path, line number, and impo The system SHALL ensure the static analysis test completes in under 2 seconds and requires no external dependencies beyond Python standard library. #### Scenario: Test parses AST efficiently + - **WHEN** test runs on full codebase - **THEN** it SHALL complete within 2 seconds - **AND** SHALL use ast.parse() from standard library (no external parsers) #### Scenario: Test core directories are configurable + - **WHEN** new core directories are added (e.g., contracts/) - **THEN** test SHALL have a CORE_DIRS constant at top of file - **AND** SHALL be easily updated by adding new Path to the list @@ -87,16 +99,19 @@ The system SHALL ensure the static analysis test completes in under 2 seconds an The system SHALL enforce that core CLI accesses modules only via CommandRegistry lazy loading, never via direct imports. #### Scenario: Core uses registry for module access + - **WHEN** core CLI needs to invoke a module command - **THEN** it SHALL call `CommandRegistry.get_typer(command_name)` - **AND** SHALL NOT import module code directly #### Scenario: Module code is loaded lazily on demand + - **WHEN** CommandRegistry.get_typer() is called - **THEN** module SHALL be loaded via importlib.util.module_from_spec - **AND** SHALL NOT be imported at CLI startup #### Scenario: Core-to-registry is allowed import + - **WHEN** core CLI imports from `specfact_cli.registry` - **THEN** static analysis test SHALL pass - **AND** SHALL distinguish between registry access (allowed) and module access (forbidden) @@ -106,11 +121,13 @@ The system SHALL enforce that core CLI accesses modules only via CommandRegistry The system SHALL document the core-module isolation principle in `docs/reference/module-contracts.md` for 3rd-party module developers. #### Scenario: Docs explain inversion-of-control architecture + - **WHEN** developer reads module-contracts.md - **THEN** docs SHALL explain that core never imports modules - **AND** SHALL illustrate registry pattern with code examples #### Scenario: Docs guide module developers on protocol implementation + - **WHEN** developer wants to create a marketplace module - **THEN** docs SHALL show how to implement ModuleIOContract - **AND** SHALL clarify that modules are discovered and loaded, not imported by core diff --git a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-io-contract/spec.md b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-io-contract/spec.md index 789529b3..2d1d0f9c 100644 --- a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-io-contract/spec.md +++ b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-io-contract/spec.md @@ -7,18 +7,22 @@ The system SHALL provide a `ModuleIOContract` Protocol in `src/specfact_cli/contracts/module_interface.py` that defines four required operations all modules must implement for interacting with ProjectBundle. #### Scenario: Protocol defines import_to_bundle operation + - **WHEN** a module implements ModuleIOContract - **THEN** it MUST provide `import_to_bundle(source: Path, config: dict) -> ProjectBundle` method that converts external format to ProjectBundle #### Scenario: Protocol defines export_from_bundle operation + - **WHEN** a module implements ModuleIOContract - **THEN** it MUST provide `export_from_bundle(bundle: ProjectBundle, target: Path, config: dict) -> None` method that converts ProjectBundle to external format #### Scenario: Protocol defines sync_with_bundle operation + - **WHEN** a module implements ModuleIOContract - **THEN** it MUST provide `sync_with_bundle(bundle: ProjectBundle, external_source: str, config: dict) -> ProjectBundle` method for bidirectional synchronization #### Scenario: Protocol defines validate_bundle operation + - **WHEN** a module implements ModuleIOContract - **THEN** it MUST provide `validate_bundle(bundle: ProjectBundle, rules: dict) -> ValidationReport` method for module-specific validation @@ -27,11 +31,13 @@ The system SHALL provide a `ModuleIOContract` Protocol in `src/specfact_cli/cont The system SHALL use `typing.Protocol` for ModuleIOContract to enable structural subtyping without requiring explicit inheritance. #### Scenario: Module without explicit inheritance satisfies protocol + - **WHEN** a module class implements all four protocol methods with correct signatures - **THEN** basedpyright type checker SHALL recognize it as implementing ModuleIOContract - **AND** no explicit inheritance or registration is required #### Scenario: Module with partial implementation is type-checked + - **WHEN** a module class implements only some protocol methods - **THEN** basedpyright SHALL report protocol violations during type checking - **AND** module registration SHALL detect missing methods via hasattr() checks @@ -41,16 +47,19 @@ The system SHALL use `typing.Protocol` for ModuleIOContract to enable structural The system SHALL enforce that all ModuleIOContract methods accept or return ProjectBundle as the data exchange format. #### Scenario: Import operation returns ProjectBundle + - **WHEN** import_to_bundle is called with valid external source - **THEN** it MUST return a ProjectBundle instance - **AND** the returned bundle SHALL have all required fields populated #### Scenario: Export operation accepts ProjectBundle + - **WHEN** export_from_bundle is called with ProjectBundle - **THEN** it MUST accept ProjectBundle as input - **AND** SHALL NOT require any other data structure for core export logic #### Scenario: Sync operation uses ProjectBundle bidirectionally + - **WHEN** sync_with_bundle is called - **THEN** it MUST accept ProjectBundle as input - **AND** MUST return ProjectBundle as output @@ -61,16 +70,19 @@ The system SHALL enforce that all ModuleIOContract methods accept or return Proj The system SHALL require all ModuleIOContract implementations to use `@icontract` and `@beartype` decorators for runtime validation. #### Scenario: Import method has preconditions + - **WHEN** import_to_bundle is implemented - **THEN** it MUST have `@require` decorator validating source path exists - **AND** MUST have `@beartype` decorator for type checking #### Scenario: Export method has postconditions + - **WHEN** export_from_bundle is implemented - **THEN** it MUST have `@ensure` decorator validating target file was created - **AND** MUST have `@beartype` decorator for type checking #### Scenario: Validate method returns ValidationReport + - **WHEN** validate_bundle is implemented - **THEN** it MUST return ValidationReport instance - **AND** MUST have `@ensure` decorator validating report structure @@ -80,16 +92,19 @@ The system SHALL require all ModuleIOContract implementations to use `@icontract The system SHALL allow modules to implement subsets of ModuleIOContract operations based on their functionality. #### Scenario: Import-only module omits export methods + - **WHEN** a module only supports importing from external systems - **THEN** it MAY implement only import_to_bundle and validate_bundle - **AND** module registration SHALL detect and log supported operations #### Scenario: Sync-only module implements full bidirectional operations + - **WHEN** a module supports bidirectional sync - **THEN** it MUST implement all four operations - **AND** sync_with_bundle SHALL use import_to_bundle and export_from_bundle internally #### Scenario: Validation-only module omits IO operations + - **WHEN** a module only validates bundles without external IO - **THEN** it MAY implement only validate_bundle - **AND** SHALL NOT be required to implement import/export/sync operations @@ -99,13 +114,16 @@ The system SHALL allow modules to implement subsets of ModuleIOContract operatio The system SHALL provide a `ValidationReport` Pydantic model for structured validation results. #### Scenario: ValidationReport has status field + - **WHEN** validate_bundle returns ValidationReport - **THEN** report MUST have `status` field with values: "passed", "failed", "warnings" #### Scenario: ValidationReport has violations list + - **WHEN** validation finds issues - **THEN** report MUST have `violations` list of dicts with keys: severity, message, location #### Scenario: ValidationReport has summary field + - **WHEN** validation completes - **THEN** report MUST have `summary` field with counts: total_checks, passed, failed, warnings diff --git a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-lifecycle-management/spec.md b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-lifecycle-management/spec.md index 90e4ead0..bf8d2650 100644 --- a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-lifecycle-management/spec.md +++ b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-lifecycle-management/spec.md @@ -7,21 +7,25 @@ The system SHALL extend registration-time validation to check if module implements ModuleIOContract and log protocol compliance status. #### Scenario: Registration checks for protocol implementation + - **WHEN** module package is registered - **THEN** system SHALL inspect module for ModuleIOContract implementation - **AND** SHALL use hasattr() to check for import_to_bundle, export_from_bundle, sync_with_bundle, validate_bundle methods #### Scenario: Full protocol implementation is logged + - **WHEN** module implements all four ModuleIOContract methods - **THEN** registration SHALL log at INFO level: "Module X: ModuleIOContract fully implemented" - **AND** SHALL store protocol_operations: ["import", "export", "sync", "validate"] in metadata #### Scenario: Partial protocol implementation is logged with operations + - **WHEN** module implements only import_to_bundle and validate_bundle - **THEN** registration SHALL log at INFO level: "Module X: ModuleIOContract partial (import, validate)" - **AND** SHALL store protocol_operations: ["import", "validate"] in metadata #### Scenario: No protocol implementation logs legacy mode + - **WHEN** module does not implement any ModuleIOContract methods - **THEN** registration SHALL log at WARNING level: "Module X: No ModuleIOContract (legacy mode)" - **AND** SHALL store protocol_operations: [] in metadata @@ -32,17 +36,20 @@ The system SHALL extend registration-time validation to check if module implemen The system SHALL extend registration validation to check ProjectBundle schema version compatibility if module declares schema_version in manifest. #### Scenario: Compatible schema version allows registration + - **WHEN** module declares schema_version: "1" and ProjectBundle.schema_version is "1" - **THEN** registration SHALL succeed - **AND** SHALL log: "Module X: Schema version 1 (compatible)" #### Scenario: Incompatible schema version skips registration + - **WHEN** module declares schema_version: "2" and ProjectBundle.schema_version is "1" - **THEN** registration SHALL skip module - **AND** SHALL log at WARNING level: "Module X: Schema version 2 required, but current is 1 (skipped)" - **AND** skipped module SHALL be listed in registration summary #### Scenario: Missing schema version assumes compatibility + - **WHEN** module omits schema_version from manifest - **THEN** registration SHALL assume current ProjectBundle schema - **AND** SHALL log at DEBUG level: "Module X: No schema version declared (assuming current)" @@ -53,11 +60,13 @@ The system SHALL extend registration validation to check ProjectBundle schema ve The system SHALL extend registration summary output to include protocol compliance statistics. #### Scenario: Summary counts protocol-compliant modules + - **WHEN** registration completes - **THEN** summary SHALL include counts: "Protocol-compliant: 4/5 modules" - **AND** SHALL list modules by status: Full (3), Partial (1), Legacy (1) #### Scenario: Summary warns about legacy modules + - **WHEN** registration finds modules without ModuleIOContract - **THEN** summary SHALL include warning: "1 module(s) in legacy mode (no ModuleIOContract)" - **AND** SHALL recommend updating to ModuleIOContract for marketplace compatibility diff --git a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-packages/spec.md b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-packages/spec.md index 2ccc040b..14654cd9 100644 --- a/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-packages/spec.md +++ b/openspec/changes/archive/2026-02-08-arch-04-core-contracts-interfaces/specs/module-packages/spec.md @@ -7,16 +7,19 @@ The system SHALL extend `ModulePackageMetadata` to include a `schema_version` field indicating which ProjectBundle schema version the module is compatible with. #### Scenario: Metadata declares schema compatibility + - **WHEN** module-package.yaml is loaded - **THEN** it MAY include `schema_version: "1"` field - **AND** module registration SHALL validate compatibility with ProjectBundle.schema_version #### Scenario: Missing schema_version defaults to current + - **WHEN** module-package.yaml omits schema_version - **THEN** registration SHALL assume current ProjectBundle schema version - **AND** SHALL log warning recommending explicit declaration #### Scenario: Incompatible schema_version blocks registration + - **WHEN** module declares schema_version: "2" but ProjectBundle is version "1" - **THEN** registration SHALL skip module with warning - **AND** SHALL log: "Module X requires schema version 2, but current is 1" @@ -26,21 +29,25 @@ The system SHALL extend `ModulePackageMetadata` to include a `schema_version` fi The system SHALL extend module discovery to check if module implements ModuleIOContract protocol and log supported operations. #### Scenario: Discovery detects protocol implementation + - **WHEN** module package is discovered and loaded - **THEN** registry SHALL check if module class implements ModuleIOContract - **AND** SHALL use hasattr() to detect which operations are supported #### Scenario: Module with protocol is logged as compliant + - **WHEN** module implements all four ModuleIOContract methods - **THEN** registration SHALL log: "Module X implements ModuleIOContract (full)" - **AND** SHALL store supported operations in module metadata #### Scenario: Module without protocol is logged as legacy + - **WHEN** module does not implement ModuleIOContract - **THEN** registration SHALL log warning: "Module X does not implement ModuleIOContract (legacy mode)" - **AND** SHALL still register module for backward compatibility #### Scenario: Module with partial protocol is logged with operations + - **WHEN** module implements import_to_bundle and validate_bundle only - **THEN** registration SHALL log: "Module X implements ModuleIOContract (partial: import, validate)" - **AND** SHALL allow partial implementation @@ -50,11 +57,13 @@ The system SHALL extend module discovery to check if module implements ModuleIOC The system SHALL update `src/specfact_cli/models/module_package.py` to include schema_version and protocol_compliance fields. #### Scenario: ModulePackageMetadata has schema_version field + - **WHEN** ModulePackageMetadata is instantiated - **THEN** it SHALL have optional `schema_version: str | None` field - **AND** default value SHALL be None (implying current schema) #### Scenario: ModulePackageMetadata tracks protocol operations + - **WHEN** module is discovered - **THEN** metadata SHALL have `protocol_operations: list[str]` field - **AND** SHALL contain names of implemented operations: ["import", "export", "sync", "validate"] diff --git a/openspec/changes/archive/2026-02-10-arch-05-bridge-registry/proposal.md b/openspec/changes/archive/2026-02-10-arch-05-bridge-registry/proposal.md index b4554d40..2c2a97b6 100644 --- a/openspec/changes/archive/2026-02-10-arch-05-bridge-registry/proposal.md +++ b/openspec/changes/archive/2026-02-10-arch-05-bridge-registry/proposal.md @@ -2,12 +2,10 @@ ## Why - `arch-04-core-contracts-interfaces` formalizes module IO contracts and core isolation, but modules still lack a standard way to expose reusable external-service schema converters. Without a bridge registry, modules either duplicate adapter logic or reintroduce coupling, which slows parallel development and blocks marketplace-ready interoperability. ## What Changes - - **NEW**: Add `src/specfact_cli/registry/bridge_registry.py` with `SchemaConverter` protocol and `BridgeRegistry` for converter registration/discovery. - **MODIFY**: Extend module package metadata to declare `service_bridges` in `module-package.yaml`. - **MODIFY**: Extend module lifecycle registration to validate and register declared service bridges without direct core-to-module imports. @@ -19,6 +17,7 @@ - **NEW**: Add tests for bridge registry behavior, manifest parsing, registration-time validation, and module integration. ## Capabilities + ### New Capabilities - `bridge-registry`: Contract-driven registry for service schema converters so modules can publish and consume conversion bridges without hardcoded core logic. diff --git a/openspec/changes/archive/2026-02-11-backlog-scrum-01-standup-exceptions-first/proposal.md b/openspec/changes/archive/2026-02-11-backlog-scrum-01-standup-exceptions-first/proposal.md index b415c8cb..6e64ed1c 100644 --- a/openspec/changes/archive/2026-02-11-backlog-scrum-01-standup-exceptions-first/proposal.md +++ b/openspec/changes/archive/2026-02-11-backlog-scrum-01-standup-exceptions-first/proposal.md @@ -4,7 +4,6 @@ ## Why - The archived change `daily-standup-progress-support` (#168) delivers standup view, interactive review, and Copilot export. Teams love tools that surface blockers and risks first. This delta extends standup with exceptions-first default section order, optional `--mode scrum|kanban|safe`, and integration with Policy Engine (policy-engine-01) and patch mode (patch-mode-01) so standup output highlights policy failures and aging/stalled work before normal status. This change is part of the **`backlog-scrum` module** β€” the Scrum-framework module providing sprint planning, standup enhancement, story refinement, and DoD capabilities. @@ -27,6 +26,7 @@ modules/backlog-scrum/ ``` **`module-package.yaml` declares:** + - `name: backlog-scrum` - `version: 0.1.0` - `commands: [backlog daily (enhanced), backlog sprint-summary, ...]` @@ -54,6 +54,7 @@ modules/backlog-scrum/ ``` **`module-package.yaml` declares:** + - `name: backlog-scrum` - `version: 0.1.0` - `commands: [backlog daily (enhanced), backlog sprint-summary, ...]` @@ -65,7 +66,6 @@ modules/backlog-scrum/ ## What Changes - - **EXTEND** (plan E1): Default section order for `specfact backlog daily` when `backlog-scrum` module is loaded: (1) blockers and dependency-critical items, (2) policy failures (DoR/DoD/flow β€” from policy-engine-01 when present), (3) aging items / stalled work (when data exists), (4) normal status. - **NEW**: Add `--mode scrum|kanban|safe` flag to `specfact backlog daily`; `scrum` is the default when this module is loaded. - **EXTEND** (policy-engine-01): When policy-engine-01 is present, query policy results for each item and surface failures in section (2); graceful no-op if not installed. @@ -80,6 +80,7 @@ modules/backlog-scrum/ - **EXTEND** (prompt/docs): Update slash prompt templates and user docs so comment context behavior and comment-windowing options are explicit for everyday team workflows. ## Capabilities + - **backlog-scrum** (standup): Exceptions-first section order (blockers, policy failures, aging, normal); `--mode scrum|kanban|safe`; optional patch integration for standup notes; Policy Engine integration for policy failure surfacing. - **backlog-scrum** (comment context): Full ADO comment retrieval for daily/refine, optional first/last comment limits, interactive last-comment-only rendering with export guidance, and aligned slash prompts/docs. diff --git a/openspec/changes/archive/2026-02-12-backlog-core-03-refine-writeback-field-splitting/proposal.md b/openspec/changes/archive/2026-02-12-backlog-core-03-refine-writeback-field-splitting/proposal.md index 36365423..eed66fc8 100644 --- a/openspec/changes/archive/2026-02-12-backlog-core-03-refine-writeback-field-splitting/proposal.md +++ b/openspec/changes/archive/2026-02-12-backlog-core-03-refine-writeback-field-splitting/proposal.md @@ -2,16 +2,12 @@ ## Why - - `specfact backlog refine --write` currently applies the raw copilot response as `body_markdown` and does not parse structured refinement output back into canonical fields before adapter writeback. For Azure DevOps this causes `System.Description` to receive a verbatim payload containing labels like `Description`, `Acceptance Criteria`, `Story Points`, `Business Value`, `Priority`, `Area Path`, and provider markers instead of updating separate fields. GitHub can exhibit the same issue when copilot output uses label-style sections instead of markdown headings. This breaks the provider-aware contract implied by refinement prompts and produces low-quality remote item updates. ## What Changes - - - **MODIFY**: Backlog refine write path to parse structured refinement content into canonical fields (`description`, `acceptance_criteria`, `story_points`, `business_value`, `priority`, `work_item_type`) before writeback. - **MODIFY**: Normalize label-style refinement output to canonical markdown sections so both ADO and GitHub writeback paths behave deterministically. - **MODIFY**: ADO/GitHub writeback behavior to prefer parsed refined values over stale pre-refinement values when `--write` is used. @@ -19,8 +15,8 @@ This breaks the provider-aware contract implied by refinement prompts and produc - **ADD**: Regression tests for ADO and GitHub writeback from label-style refinement output. ## Capabilities -- **backlog-refinement**: Provider-aware parsing and canonical field splitting for `specfact backlog refine --write`. +- **backlog-refinement**: Provider-aware parsing and canonical field splitting for `specfact backlog refine --write`. --- diff --git a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/CONTRACT-STRENGTHENING.md b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/CONTRACT-STRENGTHENING.md index d147bc0d..069d7e97 100644 --- a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/CONTRACT-STRENGTHENING.md +++ b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/CONTRACT-STRENGTHENING.md @@ -27,6 +27,7 @@ paths: ``` **Issues**: + - No required fields - No type constraints - No validation rules @@ -57,6 +58,7 @@ paths: ``` **Improvements**: + - Type constraints added - Still missing required fields - No validation rules @@ -95,6 +97,7 @@ paths: ``` **Improvements**: + - Required fields specified - Type constraints with validation - Multiple response codes @@ -176,6 +179,7 @@ schema: ``` **Generated Postcondition**: + ```python @ensure( lambda result: 'id' in result.get('data', {}) if isinstance(result.get('data'), dict) else True, @@ -201,6 +205,7 @@ properties: ``` **Generated Postconditions**: + ```python @ensure( lambda result: isinstance(result.get('data', {}).get('id'), int) @@ -234,6 +239,7 @@ schema: ``` **Generated Postcondition**: + ```python @ensure( lambda result: all(isinstance(item, dict) for item in result.get('data', [])) @@ -425,6 +431,7 @@ paths: ``` **Improvements**: + - Parameter validation (id must be β‰₯ 1) - Required fields specified - Type constraints with validation diff --git a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/CROSSHAIR-EXECUTION.md b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/CROSSHAIR-EXECUTION.md index c4e7d0b0..7bd64dc6 100644 --- a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/CROSSHAIR-EXECUTION.md +++ b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/CROSSHAIR-EXECUTION.md @@ -52,15 +52,18 @@ CrossHair was run with system Python, but Flask is only in sidecar venv: ### Fix Applied **Modified `crosshair_runner.py`**: + - Added `python_cmd` parameter to `run_crosshair()` - Use venv Python when available: `[venv_python, "-m", "crosshair", "check", ...]` - Fall back to system CrossHair if venv Python not available **Updated `dependency_installer.py`**: + - Added `crosshair-tool` to framework dependencies - CrossHair is now installed during sidecar venv setup **Updated `orchestrator.py`**: + - Pass `python_cmd` to `run_crosshair()` calls - Venv Python is now used for CrossHair execution @@ -81,11 +84,13 @@ Dependencies were not being installed in sidecar venv: ### Fix Applied **Venv Creation**: + - Uses `symlinks=False` to avoid libpython shared library issues - Validates venv Python can actually run (detects broken venvs) - Automatically recreates broken venvs **Dependency Installation**: + - Framework dependencies installed automatically (Flask, FastAPI, etc.) - Project dependencies detected and installed (requirements.txt, pyproject.toml) - Harness dependencies added (beartype, icontract) @@ -192,11 +197,13 @@ class TimeoutConfig(BaseModel): ### CrossHair Not Finding Violations **Possible Causes**: + 1. Contracts too weak (no validation rules) 2. Expected status codes not extracted correctly 3. CrossHair not executing Flask routes **Solutions**: + 1. Strengthen contracts with validation rules 2. Verify `_extract_expected_status_codes` is called 3. Check that Flask is available in venv @@ -206,6 +213,7 @@ class TimeoutConfig(BaseModel): **Expected Behavior**: Timeouts are normal for complex Flask applications **Solutions**: + 1. Check summary file for partial results 2. Increase timeout if needed (modify `TimeoutConfig`) 3. Focus on specific routes by generating smaller harness files @@ -215,6 +223,7 @@ class TimeoutConfig(BaseModel): **Issue**: `ModuleNotFoundError: No module named 'flask'` **Solutions**: + 1. Verify Flask is installed in venv: `.specfact/venv/bin/pip list | grep flask` 2. Check PYTHONPATH includes venv site-packages 3. Reinstall dependencies: Delete venv and re-run validation diff --git a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/DEPENDENCY-INSTALLATION.md b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/DEPENDENCY-INSTALLATION.md index 9244990b..77f6e8dd 100644 --- a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/DEPENDENCY-INSTALLATION.md +++ b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/DEPENDENCY-INSTALLATION.md @@ -24,6 +24,7 @@ specfact validate sidecar init **Venv Location**: `/.specfact/venv/` **Venv Configuration**: + - Uses `symlinks=False` to avoid libpython shared library issues - Includes pip by default - Validates venv Python can actually run (detects broken venvs) @@ -41,6 +42,7 @@ Framework-specific dependencies are installed automatically: | **All** | `crosshair-tool`, `beartype`, `icontract` | **Installation Order**: + 1. Framework dependencies (Flask, FastAPI, etc.) 2. CrossHair tool (for contract validation) 3. Harness dependencies (beartype, icontract) @@ -144,6 +146,7 @@ python3 -m venv .specfact/venv --copies **Issue**: Venv creation fails with permission errors **Solutions**: + - Check directory permissions - Ensure sufficient disk space - Try creating venv in a different location @@ -172,6 +175,7 @@ specfact validate sidecar run **Issue**: Dependencies fail to install from requirements.txt **Solutions**: + - Check requirements.txt format - Verify network connectivity - Check for conflicting dependencies @@ -182,6 +186,7 @@ specfact validate sidecar run **Issue**: CrossHair not available in venv **Solutions**: + - Verify CrossHair is in framework dependencies - Check installation logs for errors - Manually install: `.specfact/venv/bin/pip install crosshair-tool` @@ -191,6 +196,7 @@ specfact validate sidecar run **Issue**: `ModuleNotFoundError: No module named 'flask'` during validation **Solutions**: + - Verify Flask is installed: `.specfact/venv/bin/pip list | grep -i flask` - Check PYTHONPATH includes venv site-packages - Reinstall Flask: `.specfact/venv/bin/pip install flask` diff --git a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/FLASK-SIDECAR-USAGE.md b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/FLASK-SIDECAR-USAGE.md index 013bae37..0485c045 100644 --- a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/FLASK-SIDECAR-USAGE.md +++ b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/FLASK-SIDECAR-USAGE.md @@ -27,6 +27,7 @@ Flask applications are automatically detected by looking for: Flask route extraction supports: 1. **Application Routes**: + ```python @app.route('/users/', methods=['GET']) def get_user(id): @@ -34,6 +35,7 @@ Flask route extraction supports: ``` 2. **Blueprint Routes**: + ```python bp = Blueprint('api', __name__) @@ -206,6 +208,7 @@ specfact validate sidecar init microblog /path/to/microblog ``` **Output**: + ``` βœ“ Sidecar workspace initialized successfully Framework detected: FrameworkType.FLASK @@ -218,6 +221,7 @@ specfact validate sidecar run microblog /path/to/microblog --no-run-specmatic ``` **Output**: + ``` Validation Results: Framework: FrameworkType.FLASK @@ -249,6 +253,7 @@ cat .specfact/projects/microblog/reports/sidecar/crosshair-summary-*.json | jq **Issue**: Framework detected as `PURE_PYTHON` instead of `FLASK` **Solutions**: + - Ensure Flask imports are present: `from flask import Flask` - Check that Flask app is instantiated: `app = Flask(__name__)` - Verify repository path is correct @@ -258,6 +263,7 @@ cat .specfact/projects/microblog/reports/sidecar/crosshair-summary-*.json | jq **Issue**: `Routes extracted: 0` **Solutions**: + - Check that route decorators use `@app.route()` or `@bp.route()` - Verify routes are in Python files (not templates) - Check for syntax errors in route files @@ -267,6 +273,7 @@ cat .specfact/projects/microblog/reports/sidecar/crosshair-summary-*.json | jq **Issue**: `ModuleNotFoundError: No module named 'flask'` **Solutions**: + - Check that sidecar venv was created: `.specfact/venv/` exists - Verify dependencies were installed: Check `.specfact/venv/lib/python*/site-packages/` - Recreate venv if broken: Delete `.specfact/venv/` and run validation again @@ -276,11 +283,13 @@ cat .specfact/projects/microblog/reports/sidecar/crosshair-summary-*.json | jq **Issue**: CrossHair analysis times out **Explanation**: This is expected for complex Flask applications. Timeouts occur because: + - Symbolic execution of Flask routes is computationally expensive - Database dependencies, sessions, and external services add complexity - Multiple routes need to be analyzed **Solutions**: + - Check summary file for partial results - Increase timeout if needed (modify `TimeoutConfig` in code) - Focus on specific routes by generating smaller harness files diff --git a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/IMPLEMENTATION_STATUS.md b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/IMPLEMENTATION_STATUS.md index 0fd61ffc..51473c51 100644 --- a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/IMPLEMENTATION_STATUS.md +++ b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/IMPLEMENTATION_STATUS.md @@ -94,7 +94,7 @@ The sidecar venv was created with Python 3.12, but the system may not have the r ### Completed βœ… -2. **Add Response Structure Validation** (9.2.4): βœ… __COMPLETED__ +2. **Add Response Structure Validation** (9.2.4): βœ… **COMPLETED** - βœ… Parse OpenAPI response schemas - βœ… Validate required fields in response objects - βœ… Check response types match OpenAPI spec @@ -102,7 +102,7 @@ The sidecar venv was created with Python 3.12, but the system may not have the r - βœ… **Enhanced**: Added array item type validation (object items, string items) - **Result**: Contracts now validate nested object properties and array item types -3. **Add Detailed Violation Reporting** (9.3.4): βœ… __COMPLETED__ +3. **Add Detailed Violation Reporting** (9.3.4): βœ… **COMPLETED** - βœ… Parse CrossHair counterexample output using regex patterns - βœ… Extract input values that cause violations (parse key=value pairs with type inference) - βœ… Include counterexamples in summary reports (added to summary dict and JSON file) diff --git a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/proposal.md b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/proposal.md index 91029e74..9637ee59 100644 --- a/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/proposal.md +++ b/openspec/changes/archive/2026-02-12-sidecar-01-flask-support/proposal.md @@ -2,10 +2,10 @@ ## Why - During validation of Microblog (a Flask application), we discovered that **Flask route extraction is not implemented** in SpecFact CLI's sidecar validation. The framework detector finds Flask imports but returns `PURE_PYTHON`, and there's no `FlaskExtractor` class to extract routes from Flask applications. **Current State**: + - Framework detector detects Flask but returns `PURE_PYTHON` (see `framework_detector.py:96-97`) - No `FrameworkType.FLASK` in enum - No `FlaskExtractor` class in `frameworks/` directory @@ -13,6 +13,7 @@ During validation of Microblog (a Flask application), we discovered that **Flask - Result: **0 routes extracted** from Flask applications **Impact**: + - Cannot validate Flask applications using sidecar validation - Microblog validation blocked (Phase B cannot complete) - Missing support for a major Python web framework @@ -33,7 +34,6 @@ The sidecar validation is a **core framework capability**, not a new marketplace ## What Changes - - **NEW**: Add `FLASK = "flask"` to `FrameworkType` enum in `src/specfact_cli/validators/sidecar/models.py` - **NEW**: Create `FlaskExtractor` class in `src/specfact_cli/validators/sidecar/frameworks/flask.py` implementing: - `detect()` method: Check for Flask imports and `Flask()` instantiation @@ -46,6 +46,7 @@ The sidecar validation is a **core framework capability**, not a new marketplace - **NEW**: Create unit tests in `tests/unit/validators/sidecar/frameworks/test_flask.py` with β‰₯80% coverage ## Capabilities + - **sidecar-validation** (Flask): Flask route extraction (`@app.route()`, `@bp.route()`); `FrameworkType.FLASK` detection; parity with FastAPI and Django extractors. --- diff --git a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/CHANGE_VALIDATION.md index f75fed03..37666c3a 100644 --- a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ ## Breaking Changes Detected **None** - All changes are **additive only**: + - New `extensions` field with default value (empty dict) - New accessor methods (get_extension, set_extension) - New ModulePackageMetadata fields with defaults @@ -25,40 +26,49 @@ ### Feature Model (src/specfact_cli/models/plan.py) **ADDED field**: + ```python extensions: dict[str, Any] = Field(default_factory=dict) ``` + - Default value ensures backward compatibility - Existing Feature instances remain valid - Serialization/deserialization preserved **ADDED methods**: + ```python def get_extension(module_name: str, field: str, default: Any = None) -> Any: ... def set_extension(module_name: str, field: str, value: Any) -> None: ... ``` + - New methods, no existing signatures modified - Contracts enforce namespace format validation ### ProjectBundle Model (src/specfact_cli/models/project.py) **ADDED field**: + ```python extensions: dict[str, Any] = Field(default_factory=dict) ``` + - Default value ensures backward compatibility - Existing bundles load without migration **ADDED methods**: + ```python def get_extension(module_name: str, field: str, default: Any = None) -> Any: ... def set_extension(module_name: str, field: str, value: Any) -> None: ... ``` + - New methods, no existing signatures modified ### ModulePackageMetadata Model (src/specfact_cli/models/module_package.py) **ADDED model**: + ```python class SchemaExtension(BaseModel): target: str # "Feature" or "ProjectBundle" @@ -68,21 +78,25 @@ class SchemaExtension(BaseModel): ``` **ADDED field**: + ```python schema_extensions: list[SchemaExtension] = Field(default_factory=list) ``` + - Default value (empty list) ensures backward compatibility - Existing module manifests without schema_extensions remain valid ### New Module **ADDED**: `src/specfact_cli/registry/extension_registry.py` + - ExtensionRegistry class for collision detection - No existing code depends on this (new module) ## Dependencies Affected ### Files Using Feature Model (~15 files) + - **Impact**: None (additive change) - **Action Required**: None - **Optional**: Modules can adopt extensions when needed @@ -93,6 +107,7 @@ schema_extensions: list[SchemaExtension] = Field(default_factory=list) - `src/specfact_cli/adapters/speckit.py` ### Files Using ProjectBundle Model (~9 files) + - **Impact**: None (additive change) - **Action Required**: None - **Optional**: Can adopt extensions when needed @@ -102,11 +117,13 @@ schema_extensions: list[SchemaExtension] = Field(default_factory=list) - `src/specfact_cli/utils/progress.py` ### Module Registration Flow + - **File**: `src/specfact_cli/registry/module_packages.py` - **Impact**: Will be extended to parse schema_extensions - **Action**: Implementation task (new functionality) ### Test Files (~10 files) + - **Impact**: None (backward compatible) - **Recommendation**: Add new tests for extension functionality - **Action**: Test tasks included in implementation plan @@ -114,23 +131,27 @@ schema_extensions: list[SchemaExtension] = Field(default_factory=list) ## Impact Assessment ### Code Impact + - **Scope**: Core data models (Feature, ProjectBundle) - **Type**: Additive (new fields and methods) - **Backward Compatibility**: βœ… Full (defaults preserve existing behavior) - **Migration Required**: ❌ None ### Test Impact + - **Existing Tests**: βœ… Should pass without modification (backward compatible) - **New Tests Required**: βœ… Covered in tasks.md (TDD-first approach) - **Coverage**: Expect >80% for new functionality ### Documentation Impact + - **New Guide**: `docs/guides/extending-projectbundle.md` βœ… - **Updated Reference**: `docs/reference/architecture.md` βœ… - **Navigation**: `docs/_layouts/default.html` update βœ… - **Impact**: Well-documented in tasks.md ### Release Impact + - **Version Bump**: **Minor** (new feature, backward compatible) - **Semver**: Appropriate (additive API changes) - **Changelog**: Update required βœ… @@ -138,6 +159,7 @@ schema_extensions: list[SchemaExtension] = Field(default_factory=list) ## Format Validation ### proposal.md Format: βœ… PASS + - βœ… Title: `# Change: [description]` - βœ… Sections: Why, What Changes, Capabilities, Impact - βœ… Capabilities: New `schema-extension-system`, Modified `module-packages`, `module-lifecycle-management` @@ -146,6 +168,7 @@ schema_extensions: list[SchemaExtension] = Field(default_factory=list) - βœ… Rollback plan: Documented ### tasks.md Format: βœ… PASS + - βœ… TDD/SDD Order: Enforced with explicit header - βœ… Git Workflow: Branch creation first (Task 1), PR creation last (Task 10) - βœ… Task Structure: Hierarchical `## N.` sections with `- [ ] N.M` tasks @@ -156,6 +179,7 @@ schema_extensions: list[SchemaExtension] = Field(default_factory=list) - βœ… Contract requirements: @icontract/@beartype in task descriptions ### specs Format: βœ… PASS + - βœ… New spec: `schema-extension-system/spec.md` (8 requirements, 29 scenarios) - βœ… Delta specs: `module-packages/spec.md`, `module-lifecycle-management/spec.md` - βœ… Format: WHEN/THEN format (not Given/When/Then, per instructions) @@ -163,6 +187,7 @@ schema_extensions: list[SchemaExtension] = Field(default_factory=list) - βœ… References: Leverages existing patterns ### design.md Format: βœ… PASS + - βœ… Sections: Context, Goals/Non-Goals, Decisions, Risks/Trade-offs - βœ… Decisions: 4 key decisions with rationale and trade-offs - βœ… Alternatives: Considered for each decision @@ -170,6 +195,7 @@ schema_extensions: list[SchemaExtension] = Field(default_factory=list) - βœ… Migration Plan: Phased approach documented ### Config.yaml Compliance: βœ… PASS + - βœ… TDD-first: Tests before code (enforced in tasks.md) - βœ… Git workflow: Branch first, PR last - βœ… Quality gates: format, type-check, contract-test, full suite @@ -188,27 +214,32 @@ schema_extensions: list[SchemaExtension] = Field(default_factory=list) ## Dependencies and Prerequisites ### Required Changes (from plan) + - βœ… **arch-04** (ModuleIOContract): Foundation exists (archived) - βœ… **arch-05** (Bridge Registry): Active change, not a blocker for this change - βœ… **arch-06** (Enhanced Manifest Security): Active change, not a blocker ### Recommendation + This change (arch-07) can proceed independently. It does not depend on arch-05 or arch-06 being implemented first, as it only extends core models and module manifest schema without conflicting with security or bridge features. ## Risk Assessment ### Technical Risks + 1. **Namespace collision** - Mitigated by ExtensionRegistry validation at registration 2. **Type safety** - Mitigated by @beartype contracts on accessor methods 3. **Performance** - Low risk (dict lookups, optional metadata) 4. **Serialization** - Low risk (Pydantic handles new fields automatically) ### Process Risks + 1. **Incomplete testing** - Mitigated by TDD-first approach in tasks.md 2. **Documentation lag** - Mitigated by documentation task before PR 3. **Version sync** - Mitigated by explicit version update task ### Mitigation Verification + - βœ… All risks have documented mitigations in design.md - βœ… Tasks.md enforces mitigations through task ordering - βœ… Quality gates catch issues before merge @@ -231,6 +262,7 @@ This change (arch-07) can proceed independently. It does not depend on arch-05 o **Change arch-07-schema-extension-system is SAFE TO IMPLEMENT** βœ… ### Key Findings + 1. βœ… Zero breaking changes - all changes are additive 2. βœ… Full backward compatibility with existing code and bundles 3. βœ… Comprehensive task plan with TDD-first ordering @@ -240,11 +272,13 @@ This change (arch-07) can proceed independently. It does not depend on arch-05 o 7. βœ… OpenSpec validation passed ### Recommendation + **PROCEED WITH IMPLEMENTATION** following the task plan in tasks.md. Start with: `git checkout -b feature/arch-07-schema-extension-system` ### Next Steps + 1. Create feature branch (Task 1) 2. Begin TDD cycle: write tests for extensions field (Task 2.1) 3. Follow tasks.md sequentially through completion diff --git a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/design.md b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/design.md index 09aeb754..52930a53 100644 --- a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/design.md +++ b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/design.md @@ -5,11 +5,13 @@ The modular architecture (arch-01/02/03) enables independent module development, but modules currently cannot persist custom metadata in ProjectBundle without modifying core models. This blocks marketplace scenarios where backlog modules need to track external IDs (ADO work item IDs, Jira issue keys) or sync modules need to store last-sync timestamps. **Current State:** + - `Feature` and `ProjectBundle` models are fixed-schema Pydantic classes - Modules duplicating schema logic or introducing core-module coupling - No mechanism for modules to declare schema extensions in manifests **Constraints:** + - Must preserve backward compatibility (existing bundles remain valid) - Must prevent namespace collisions between modules - Must maintain type safety with Pydantic validation @@ -19,6 +21,7 @@ The modular architecture (arch-01/02/03) enables independent module development, ## Goals / Non-Goals **Goals:** + - Enable modules to extend Feature and ProjectBundle with custom fields - Provide type-safe accessors/mutators with namespace enforcement - Support declarative extension registration via module manifests @@ -26,6 +29,7 @@ The modular architecture (arch-01/02/03) enables independent module development, - Prevent module namespace collisions through prefixed field names **Non-Goals:** + - Dynamic model creation at bundle load time (extensions are optional metadata) - Schema migration for existing bundles (extensions default to empty dict) - Runtime type coercion beyond Pydantic's native capabilities @@ -36,6 +40,7 @@ The modular architecture (arch-01/02/03) enables independent module development, ### Decision 1: Extensions as dict field vs. dynamic model generation **Options:** + - **A**: Add `extensions: dict[str, Any]` field with namespace-prefixed keys - **B**: Use `pydantic.create_model()` to dynamically extend models at registration time - **C**: Use Python descriptors for field access interception @@ -43,6 +48,7 @@ The modular architecture (arch-01/02/03) enables independent module development, **Choice: A (dict field with namespace enforcement)** **Rationale:** + - Simpler serialization (standard Pydantic dict handling) - Backward compatible (field defaults to empty dict) - Type safety via accessor methods with @beartype validation @@ -50,6 +56,7 @@ The modular architecture (arch-01/02/03) enables independent module development, - Easier debugging (extensions visible in model repr) **Trade-offs:** + - Extensions not part of IDE autocomplete (acceptable for dynamic fields) - Requires explicit accessor calls vs. attribute access - No Pydantic Field() validation for extension values (module responsibility) @@ -57,6 +64,7 @@ The modular architecture (arch-01/02/03) enables independent module development, ### Decision 2: Namespace format **Options:** + - **A**: `module.field` (dot-separated) - **B**: `module__field` (double-underscore) - **C**: `module/field` (slash-separated) @@ -64,6 +72,7 @@ The modular architecture (arch-01/02/03) enables independent module development, **Choice: A (dot-separated)** **Rationale:** + - Consistent with Python package naming conventions - Human-readable and clear ownership - Easy to parse and validate @@ -72,6 +81,7 @@ The modular architecture (arch-01/02/03) enables independent module development, ### Decision 3: Extension declaration in manifest **Options:** + - **A**: Declarative in `module-package.yaml` (static metadata) - **B**: Programmatic via registration function (runtime) - **C**: Hybrid (manifest + runtime validation) @@ -79,12 +89,14 @@ The modular architecture (arch-01/02/03) enables independent module development, **Choice: A (declarative in manifest)** **Rationale:** + - Aligns with existing manifest pattern (commands, bridges, dependencies) - Static analysis possible (detect conflicts before installation) - Documentation-first approach (manifest is source of truth) - No code execution required to discover extensions **Example:** + ```yaml schema_extensions: - target: Feature @@ -100,6 +112,7 @@ schema_extensions: ### Decision 4: Accessor/mutator pattern **Options:** + - **A**: Direct dict access: `feature.extensions["backlog.ado_id"]` - **B**: Helper methods: `feature.get_extension("backlog", "ado_id")` - **C**: Attribute-style proxy: `feature.ext.backlog.ado_id` @@ -107,6 +120,7 @@ schema_extensions: **Choice: B (helper methods)** **Rationale:** + - Type-safe with @beartype validation - Clear separation from core fields - Explicit namespace handling (prevents typos) @@ -114,6 +128,7 @@ schema_extensions: - Enables future validation hooks **API:** + ```python def get_extension(self, module_name: str, field: str, default: Any = None) -> Any: return self.extensions.get(f"{module_name}.{field}", default) @@ -125,46 +140,57 @@ def set_extension(self, module_name: str, field: str, value: Any) -> None: ## Risks / Trade-offs ### Risk 1: Namespace collisions between modules + **Mitigation**: Static analysis test validates no duplicate `module.field` keys across all installed modules. Registration fails if collision detected. ### Risk 2: Type safety weaker than core fields + **Trade-off**: Extensions use `Any` type since modules define their own schemas. Modules responsible for validating extension values. Acceptable for marketplace extensibility. ### Risk 3: Extension fields not discoverable in IDE + **Trade-off**: IDE autocomplete won't suggest extension fields. Acceptable given dynamic nature. Documentation and manifest declarations serve as reference. ### Risk 4: Performance overhead of dict lookups + **Mitigation**: Extensions are optional metadata, accessed only when needed. No impact on core bundle operations. Profile if concerns arise. ### Risk 5: Backward compatibility with old bundles + **Mitigation**: `extensions` field defaults to empty dict. Old bundles without extensions remain valid. Serialization preserves extensions when present. ## Migration Plan ### Phase 1: Add extensions field to models + 1. Add `extensions: dict[str, Any] = Field(default_factory=dict)` to Feature (plan.py and project.py) 2. Add `extensions: dict[str, Any] = Field(default_factory=dict)` to ProjectBundle 3. Add `get_extension()` and `set_extension()` methods to both models 4. Add contracts (@require namespace format: `module_name.field_name`) ### Phase 2: Manifest schema extension + 1. Extend `ModulePackageMetadata` with `schema_extensions: list[SchemaExtension]` 2. Add `SchemaExtension` model (target, fields dict) 3. Update manifest parser to load schema_extensions ### Phase 3: Registration-time validation + 1. Extend module registration to collect schema_extensions from manifests 2. Build global extension registry (module β†’ extensions mapping) 3. Validate no namespace collisions at registration time 4. Log registered extensions for debugging ### Phase 4: Documentation and testing + 1. Add guide: `docs/guides/extending-projectbundle.md` 2. Add tests for namespace enforcement, accessor/mutator, serialization 3. Update architecture docs with extension pattern ### Rollback + If critical issues arise: + 1. Remove `extensions` field from models (breaking for modules using extensions) 2. OR: Keep field but disable manifest parsing (extensions become inert) 3. Existing bundles without extensions remain unaffected @@ -172,16 +198,19 @@ If critical issues arise: ## Open Questions **Q1: Should extension values be validated at bundle save time?** + - Current: No validation (module responsibility) - Alternative: Optional JSON Schema validation per module manifest - **Decision**: Defer to future enhancement (YAGNI principle) **Q2: Should extensions be indexed for fast lookup?** + - Current: Flat dict with `module.field` keys - Alternative: Nested dict `{"module": {"field": value}}` - **Decision**: Keep flat (simpler, sufficient for expected scale) **Q3: Should core provide extension migration helpers?** + - Current: Modules handle their own extension schema evolution - Alternative: Core provides version-aware migration framework - **Decision**: Defer to future (modules use standard Pydantic migration patterns) diff --git a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/module-lifecycle-management/spec.md b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/module-lifecycle-management/spec.md index ca00c4bf..7e16d7f8 100644 --- a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/module-lifecycle-management/spec.md +++ b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/module-lifecycle-management/spec.md @@ -7,27 +7,32 @@ The system SHALL extend module registration to load schema_extensions from manifests, validate namespace uniqueness, and populate the global extension registry. #### Scenario: Registration loads schema_extensions from manifest + - **WHEN** module registration loads module-package.yaml - **THEN** system SHALL parse schema_extensions section if present - **AND** SHALL extract target models, field names, types, descriptions #### Scenario: Registration validates extension namespace uniqueness + - **WHEN** module declares schema extension with field name - **THEN** system SHALL check global extension registry for conflicts - **AND** SHALL reject registration if `module.field` already declared by another module - **AND** SHALL log error with conflicting module name #### Scenario: Registration populates global extension registry + - **WHEN** module registration succeeds with schema_extensions - **THEN** system SHALL add extensions to global registry - **AND** registry SHALL map module_name β†’ extensions metadata #### Scenario: Registration logs registered extensions + - **WHEN** module with schema_extensions completes registration - **THEN** system SHALL log: "Module X registered N schema extensions for [Feature, ProjectBundle]" - **AND** SHALL log at debug level the specific fields registered #### Scenario: Registration skips invalid extension declarations + - **WHEN** module declares extension with malformed field name (e.g., contains dots) - **THEN** system SHALL log warning - **AND** SHALL skip that extension diff --git a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/module-packages/spec.md b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/module-packages/spec.md index 7b761f56..a090c43c 100644 --- a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/module-packages/spec.md +++ b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/module-packages/spec.md @@ -7,22 +7,26 @@ The system SHALL extend `ModulePackageMetadata` to include optional `schema_extensions` field declaring fields the module adds to core models. #### Scenario: Manifest schema includes schema_extensions + - **WHEN** module-package.yaml is parsed - **THEN** it MAY include `schema_extensions` array - **AND** each entry SHALL specify: target model name, field definitions with type/description #### Scenario: Schema extension for Feature model + - **WHEN** module declares schema_extensions for Feature - **THEN** manifest SHALL list fields being added - **AND** each field SHALL include type hint and description - **AND** module namespace is implicit from module name #### Scenario: Schema extension for ProjectBundle model + - **WHEN** module declares schema_extensions for ProjectBundle - **THEN** manifest SHALL list fields being added - **AND** each field SHALL include type hint and description #### Scenario: Module without schema_extensions remains valid + - **WHEN** module-package.yaml omits schema_extensions - **THEN** module SHALL load successfully - **AND** no extensions registered for that module diff --git a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/schema-extension-system/spec.md b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/schema-extension-system/spec.md index 51087d7a..d3c61f95 100644 --- a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/schema-extension-system/spec.md +++ b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/specs/schema-extension-system/spec.md @@ -11,18 +11,21 @@ Defines the contract-driven mechanism for modules to extend core data models (Fe The system SHALL add an `extensions` field to Feature and ProjectBundle models to store module-specific metadata as a dictionary with namespace-prefixed keys. #### Scenario: Feature model includes extensions field + - **WHEN** Feature model is instantiated - **THEN** it SHALL include `extensions: dict[str, Any]` field - **AND** extensions SHALL default to empty dict if not provided - **AND** extensions SHALL serialize/deserialize with YAML and JSON #### Scenario: ProjectBundle model includes extensions field + - **WHEN** ProjectBundle model is instantiated - **THEN** it SHALL include `extensions: dict[str, Any]` field - **AND** extensions SHALL default to empty dict if not provided - **AND** extensions SHALL serialize/deserialize with YAML and JSON #### Scenario: Backward compatibility with bundles without extensions + - **WHEN** existing bundle without extensions field is loaded - **THEN** extensions SHALL default to empty dict - **AND** bundle SHALL remain valid @@ -33,22 +36,26 @@ The system SHALL add an `extensions` field to Feature and ProjectBundle models t The system SHALL provide `get_extension()` and `set_extension()` methods on Feature and ProjectBundle models that enforce namespace-prefixed field access. #### Scenario: Get extension with namespace prefix + - **WHEN** code calls `feature.get_extension("backlog", "ado_work_item_id")` - **THEN** system SHALL look up `extensions["backlog.ado_work_item_id"]` - **AND** SHALL return the value if present - **AND** SHALL return None if not present (or provided default) #### Scenario: Set extension with namespace prefix + - **WHEN** code calls `feature.set_extension("backlog", "ado_work_item_id", "123456")` - **THEN** system SHALL store value at `extensions["backlog.ado_work_item_id"]` - **AND** SHALL enforce namespace format (module.field) #### Scenario: Invalid namespace format is rejected + - **WHEN** code calls `set_extension("backlog.submodule", "field", "value")` - **THEN** system SHALL raise ValueError with message "Invalid module name format" - **AND** SHALL require single-level namespace (no dots in module_name) #### Scenario: Get extension with default value + - **WHEN** code calls `feature.get_extension("backlog", "missing_field", default="default_value")` - **THEN** system SHALL return "default_value" if field not present - **AND** SHALL NOT modify extensions dict @@ -58,16 +65,19 @@ The system SHALL provide `get_extension()` and `set_extension()` methods on Feat The system SHALL extend module manifest schema to allow modules to declare schema extensions in `module-package.yaml`. #### Scenario: Manifest declares Feature extensions + - **WHEN** module-package.yaml includes schema_extensions section - **THEN** it MAY declare extensions for Feature model - **AND** each extension SHALL specify: target (Feature), field name, type hint, description #### Scenario: Manifest declares ProjectBundle extensions + - **WHEN** module-package.yaml includes schema_extensions section - **THEN** it MAY declare extensions for ProjectBundle model - **AND** each extension SHALL specify: target (ProjectBundle), field name, type hint, description #### Scenario: Extension field metadata is documented + - **WHEN** module declares schema extension - **THEN** manifest SHALL include human-readable description - **AND** description SHALL explain purpose and usage @@ -78,18 +88,21 @@ The system SHALL extend module manifest schema to allow modules to declare schem The system SHALL validate that no two modules declare conflicting extension field names during module registration. #### Scenario: Duplicate extension field is detected + - **WHEN** module A declares extension "backlog.ado_work_item_id" - **AND** module B also declares extension "backlog.ado_work_item_id" - **THEN** registration SHALL fail for second module - **AND** SHALL log error: "Extension field collision: backlog.ado_work_item_id already declared by module A" #### Scenario: Different modules use unique namespaces + - **WHEN** module backlog declares "backlog.ado_work_item_id" - **AND** module sync declares "sync.last_sync_timestamp" - **THEN** both registrations SHALL succeed - **AND** no collision detected #### Scenario: Same module declares multiple fields + - **WHEN** module backlog declares "backlog.ado_work_item_id" and "backlog.jira_issue_key" - **THEN** both extensions SHALL register successfully - **AND** namespace "backlog" is owned by backlog module @@ -99,11 +112,13 @@ The system SHALL validate that no two modules declare conflicting extension fiel The system SHALL maintain a global extension registry mapping module names to their declared schema extensions for debugging and documentation. #### Scenario: Registry populated at module registration + - **WHEN** module registration loads schema_extensions from manifest - **THEN** extensions SHALL be added to global registry - **AND** registry SHALL map: module_name β†’ list of (target, field, type, description) #### Scenario: Registry is queryable for debugging + - **WHEN** developer needs to inspect registered extensions - **THEN** registry SHALL provide method to list all extensions - **AND** SHALL show which module declared each extension @@ -114,12 +129,14 @@ The system SHALL maintain a global extension registry mapping module names to th The system SHALL use @icontract decorators to enforce namespace format and type safety for extension operations. #### Scenario: get_extension enforces namespace format + - **WHEN** get_extension() is called - **THEN** @require SHALL validate module_name matches pattern `[a-z][a-z0-9_-]*` - **AND** @require SHALL validate field matches pattern `[a-z][a-z0-9_]*` - **AND** SHALL use @beartype for type checking #### Scenario: set_extension enforces namespace format + - **WHEN** set_extension() is called - **THEN** @require SHALL validate module_name and field patterns - **AND** @ensure SHALL verify value was stored at correct key @@ -130,12 +147,14 @@ The system SHALL use @icontract decorators to enforce namespace format and type The system SHALL ensure extension functionality does not break existing code that does not use extensions. #### Scenario: Core operations work without extensions + - **WHEN** bundle is created without any extension usage - **THEN** all core operations SHALL function normally - **AND** extensions field SHALL be empty dict - **AND** no performance impact #### Scenario: Modules without schema_extensions work normally + - **WHEN** module manifest omits schema_extensions section - **THEN** module SHALL register successfully - **AND** module SHALL function normally diff --git a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/tasks.md b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/tasks.md index 7679637a..3414567b 100644 --- a/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/tasks.md +++ b/openspec/changes/archive/2026-02-18-arch-07-schema-extension-system/tasks.md @@ -3,6 +3,7 @@ ## TDD / SDD Order (Enforced) Per config.yaml, tests MUST come before implementation for any behavior-changing task. Order: + 1. Spec deltas (already created) 2. Tests from spec scenarios (expect failure) 3. Code implementation (until tests pass and behavior satisfies spec) @@ -114,7 +115,7 @@ Do not implement production code until tests exist and have been run (expecting - [x] 10.3 Create PR via gh CLI - [x] 10.3.1 `gh pr create --base dev --head feature/arch-07-schema-extension-system --title "feat: Schema Extension System for Modular ProjectBundle Extensions (arch-07)" --body-file tmp-pr-body.md` - - [x] 10.3.2 PR URL: https://github.com/nold-ai/specfact-cli/pull/265 + - [x] 10.3.2 PR URL: - [x] 10.4 Link to project - [x] 10.4.1 `gh project item-add 1 --owner nold-ai --url ` (optional; run if project board in use) diff --git a/openspec/changes/archive/2026-02-18-backlog-core-01-dependency-analysis-commands/TDD_EVIDENCE.md b/openspec/changes/archive/2026-02-18-backlog-core-01-dependency-analysis-commands/TDD_EVIDENCE.md index 99d6ee7e..af03799a 100644 --- a/openspec/changes/archive/2026-02-18-backlog-core-01-dependency-analysis-commands/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-02-18-backlog-core-01-dependency-analysis-commands/TDD_EVIDENCE.md @@ -274,6 +274,7 @@ Reduce `project regenerate` conflict noise by default and add explicit strict/ve - Exit successfully unless `--strict` is set. Updated production code: + - `src/specfact_cli/modules/project/src/commands.py` ## Post-Implementation Passing Run (Phase 3.4 regenerate UX) diff --git a/openspec/changes/archive/2026-02-18-backlog-core-01-dependency-analysis-commands/proposal.md b/openspec/changes/archive/2026-02-18-backlog-core-01-dependency-analysis-commands/proposal.md index c199fa68..0e99382c 100644 --- a/openspec/changes/archive/2026-02-18-backlog-core-01-dependency-analysis-commands/proposal.md +++ b/openspec/changes/archive/2026-02-18-backlog-core-01-dependency-analysis-commands/proposal.md @@ -2,7 +2,6 @@ ## Why - After implementing backlog adapters for ADO and GitHub with directional sync (v0.25.1), we need to extend the backlog capabilities beyond simple sync to enable dependency analysis, delta tracking, and integrated DevOps workflows. Without dependency graph analysis, teams cannot understand logical relationships between backlog items (epic β†’ feature β†’ story β†’ task hierarchies) or detect blockers and circular dependencies. Without dedicated backlog/delta command suites, users must use low-level bridge sync commands instead of intuitive backlog-focused workflows. This change establishes the **`backlog-core` module** β€” the foundational backlog module that all framework-specific modules (backlog-scrum, backlog-kanban, backlog-safe) depend on. It provides the provider-agnostic graph model, dependency analysis, and core backlog commands. @@ -41,6 +40,7 @@ modules/backlog-core/ ``` **`module-package.yaml` declares:** + - `name: backlog-core` - `version: 0.1.0` - `commands: [backlog]` (module extends the shared backlog command group with additional commands and `delta` subgroup) @@ -84,6 +84,7 @@ modules/backlog-core/ ``` **`module-package.yaml` declares:** + - `name: backlog-core` - `version: 0.1.0` - `commands: [backlog]` (module extends the shared backlog command group with additional commands and `delta` subgroup) @@ -95,7 +96,6 @@ Commands are auto-discovered by the registry and lazy-loaded; no registration in ## What Changes - - **NEW**: Implement provider-agnostic dependency graph model (`BacklogGraph`, `GraphBacklogItem`, `Dependency`) in `modules/backlog-core/src/backlog_core/graph/models.py` that abstracts epic β†’ feature β†’ story β†’ task hierarchies without locking to ADO/GitHub/Jira models, with full support for Kanban (work item types and states), Scrum (sprint-based hierarchies), and SAFe (Epic β†’ Feature β†’ Story β†’ Task with Value Points and WSJF). - **NEW**: Add template-driven mapping system (`BacklogGraphBuilder`) in `modules/backlog-core/src/backlog_core/graph/builder.py` that converts provider items (ADO/GitHub) into unified graph using pre-built templates (`ado_scrum`, `ado_safe`, `github_projects`, `jira_kanban`) stored in `modules/backlog-core/src/backlog_core/resources/backlog-templates/`. - **NEW**: Implement graph analyzers (`DependencyAnalyzer`) in `modules/backlog-core/src/backlog_core/analyzers/dependency.py` for transitive closure, cycle detection, critical path analysis, and impact analysis. @@ -112,6 +112,7 @@ Commands are auto-discovered by the registry and lazy-loaded; no registration in - **EXTEND** (arch-01 init-module-state): Align `specfact init` module discovery with command registration so workspace-level modules are included in central module management. Use the same discovery roots for init as for the registry (`discover_all_package_metadata()` / `get_modules_roots()`), so `specfact init --list-modules`, `--enable-module`, and `--disable-module` see and manage workspace-level modules (e.g. `modules/backlog-core/`) consistently with runtime command discovery. ## Arch-06 Marketplace Readiness + The `module-package.yaml` includes publisher and integrity metadata: ```yaml @@ -126,10 +127,12 @@ integrity: This enables integrity verification when installed via `specfact module install backlog-core`. ## Capabilities + - **backlog-core**: Provider-agnostic `BacklogGraph` model; `DependencyAnalyzer` (transitive closure, cycle detection, critical path, impact); `BacklogGraphBuilder` with template-driven mapping; `BacklogGraphProtocol` for bridge adapter extensions; CLI: `backlog analyze-deps`, `backlog sync`, `backlog diff`, `backlog promote`, `backlog verify-readiness`, `backlog generate-release-notes`; `backlog delta status`, `backlog delta impact`, `backlog delta cost-estimate`, `backlog delta rollback-analysis`. - **init-module-discovery-alignment**: `specfact init` uses the same module discovery roots as command registration (built-in + workspace-level + `SPECFACT_MODULES_ROOTS`), so `--list-modules`, `--enable-module`, and `--disable-module` operate on all discovered modules including external/workspace-level ones. ## Impact + - **Affected specs**: backlog-core (existing), init-module-state (extended via init-module-discovery-alignment). - **Affected code**: `modules/backlog-core/` (existing), `src/specfact_cli/modules/init/src/commands.py` (discovery alignment), `src/specfact_cli/registry/module_packages.py` (no API change; init will use existing `discover_all_package_metadata()`). - **Integration points**: Init command and module state persistence; registry discovery (unchanged). diff --git a/openspec/changes/archive/2026-02-18-ceremony-cockpit-01-ceremony-aliases/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-02-18-ceremony-cockpit-01-ceremony-aliases/CHANGE_VALIDATION.md index 163e79ad..99da8d03 100644 --- a/openspec/changes/archive/2026-02-18-ceremony-cockpit-01-ceremony-aliases/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-02-18-ceremony-cockpit-01-ceremony-aliases/CHANGE_VALIDATION.md @@ -19,6 +19,7 @@ None. `ceremony-cockpit` is a pure alias/delegation module. Existing `backlog da ## Key Design Note Ceremony commands are **dynamically available** based on installed backlog framework modules. The module probe uses the arch-05 bridge registry to detect which modules are present at runtime: + - `ceremony standup/refinement/planning` β€” present when `backlog-scrum` installed - `ceremony flow` β€” present only when `backlog-kanban` installed - `ceremony pi-summary` β€” present only when `backlog-safe` installed diff --git a/openspec/changes/archive/2026-02-18-ceremony-cockpit-01-ceremony-aliases/proposal.md b/openspec/changes/archive/2026-02-18-ceremony-cockpit-01-ceremony-aliases/proposal.md index 3bd325b5..16bca756 100644 --- a/openspec/changes/archive/2026-02-18-ceremony-cockpit-01-ceremony-aliases/proposal.md +++ b/openspec/changes/archive/2026-02-18-ceremony-cockpit-01-ceremony-aliases/proposal.md @@ -2,7 +2,6 @@ ## Why - Teams think in ceremonies ("standup," "refinement," "planning"). Without ceremony entry points, adoption friction remains high β€” users must remember `backlog daily`, `backlog refine`, `backlog sprint-summary` instead of `backlog ceremony standup`, `backlog ceremony refinement`, `backlog ceremony planning`. Ceremony aliases plus mode switch (scrum|kanban|safe) and exceptions-first defaults are a pure UX/ergonomics win with minimal implementation cost. This change establishes the **`ceremony-cockpit` module** β€” the UX alias layer that wraps all installed backlog framework modules (backlog-scrum, backlog-kanban, backlog-safe) with ceremony-oriented entry points. @@ -26,6 +25,7 @@ modules/ceremony-cockpit/ ``` **`module-package.yaml` declares:** + - `name: ceremony-cockpit` - `version: 0.1.0` - `commands: [backlog]` (module extends backlog command with `ceremony` subgroup aliases) @@ -54,6 +54,7 @@ modules/ceremony-cockpit/ ``` **`module-package.yaml` declares:** + - `name: ceremony-cockpit` - `version: 0.1.0` - `commands: [backlog]` (module extends backlog command with `ceremony` subgroup aliases) @@ -65,7 +66,6 @@ modules/ceremony-cockpit/ ## What Changes - - **NEW**: Ceremony-oriented entry points in `modules/ceremony-cockpit/src/ceremony_cockpit/commands/`: - `specfact backlog ceremony standup` β†’ `backlog daily` (delegates with `--mode scrum` default) - `specfact backlog ceremony refinement` β†’ `backlog refine` @@ -79,6 +79,7 @@ modules/ceremony-cockpit/ - **No changes to `cli.py`** β€” module extends existing `backlog` command group via module registry composition. ## Capabilities + - **ceremony-cockpit**: Backlog ceremony aliases (`backlog ceremony standup`, `backlog ceremony refinement`, optional planning/flow/pi-summary); dynamic availability based on installed modules; mode switch (scrum|kanban|safe); exceptions-first defaults; output format forwarding. --- diff --git a/openspec/changes/archive/2026-02-18-patch-mode-01-preview-apply/proposal.md b/openspec/changes/archive/2026-02-18-patch-mode-01-preview-apply/proposal.md index 3996d697..de692869 100644 --- a/openspec/changes/archive/2026-02-18-patch-mode-01-preview-apply/proposal.md +++ b/openspec/changes/archive/2026-02-18-patch-mode-01-preview-apply/proposal.md @@ -2,7 +2,6 @@ ## Why - Reporting findings is not enough; teams love tools that propose fixes they can safely apply. A patch pipeline that generates unified diffs for backlog body updates, OpenSpec proposal/spec updates, and config updates β€” with `--apply` (local) and `--write` (upstream) gating and idempotency for posted comments/updates β€” ensures zero accidental writes and trust by design. This change establishes the **`patch-mode` module** β€” a foundational cross-cutting module consumed by policy-engine-01 (suggest β†’ patch), backlog-scrum-01 (standup notes patch), backlog-scrum-03 (split proposal patch), and backlog-core-02 (interactive issue preview). @@ -27,6 +26,7 @@ modules/patch-mode/ ``` **`module-package.yaml` declares:** + - `name: patch-mode` - `version: 0.1.0` - `commands: [patch apply]` @@ -53,6 +53,7 @@ modules/patch-mode/ ``` **`module-package.yaml` declares:** + - `name: patch-mode` - `version: 0.1.0` - `commands: [patch apply]` @@ -61,7 +62,6 @@ modules/patch-mode/ ## What Changes - - **NEW**: Patch pipeline in `modules/patch-mode/src/patch_mode/pipeline/generator.py` β€” generates unified diffs for: backlog issue body updates (AC improvements, missing fields), OpenSpec proposal/spec updates, config updates (policy config, mapping templates). - **NEW**: `--apply` + `--write` gating in `modules/patch-mode/src/patch_mode/pipeline/applier.py`: default = generate patch only; `--apply` = apply locally; `--write` = push to GitHub/ADO only with explicit confirmation. - **NEW**: Idempotency in `modules/patch-mode/src/patch_mode/pipeline/idempotency.py` β€” no duplicate posted comments/updates. @@ -74,7 +74,9 @@ modules/patch-mode/ - **No changes to `cli.py`** β€” `patch` command group declared in `module-package.yaml`. ## PatchModeProtocol (arch-05) + Other modules consume patch generation via bridge registry: + ```python # In backlog-scrum-03 (story complexity splitting): patch_mode = bridge_registry.resolve(PatchModeProtocol) @@ -85,6 +87,7 @@ if patch_mode: Graceful no-op when patch-mode module is not installed. ## Capabilities + - **patch-mode**: Patch pipeline (generate diffs for backlog body, OpenSpec, config); `--apply` (local) and `--write` (upstream) gating; idempotent posts; `patch apply `, `patch apply --write` with confirmation; `PatchModeProtocol` for bridge registry consumers. --- diff --git a/openspec/changes/archive/2026-02-18-policy-engine-01-unified-framework/proposal.md b/openspec/changes/archive/2026-02-18-policy-engine-01-unified-framework/proposal.md index 8cd6167c..43f1c115 100644 --- a/openspec/changes/archive/2026-02-18-policy-engine-01-unified-framework/proposal.md +++ b/openspec/changes/archive/2026-02-18-policy-engine-01-unified-framework/proposal.md @@ -2,7 +2,6 @@ ## Why - Teams love tools that enforce working agreements consistently. Today DoR/DoD are fragmented across features; Kanban/SAFe policies are not first-class. A single Policy framework with `policy.validate` (hard failures; deterministic) and `policy.suggest` (AI-assisted; confidence-scored; patch-ready) gives one mechanism for DoR, DoD, Kanban entry/exit, and SAFe PI readiness so refinement, planning, and standup share the same quality gates. This change establishes the **`policy-engine` module** β€” a foundational cross-cutting module that all backlog framework modules (backlog-scrum, backlog-kanban, backlog-safe) depend on for policy evaluation. It must be available before those modules can use it. @@ -31,6 +30,7 @@ modules/policy-engine/ ``` **`module-package.yaml` declares:** + - `name: policy-engine` - `version: 0.1.0` - `commands: [policy validate, policy suggest]` @@ -64,6 +64,7 @@ modules/policy-engine/ ``` **`module-package.yaml` declares:** + - `name: policy-engine` - `version: 0.1.0` - `commands: [policy validate, policy suggest]` @@ -75,7 +76,6 @@ Commands are auto-discovered by the registry and lazy-loaded; no registration in ## What Changes - - **NEW**: Policy framework in `modules/policy-engine/src/policy_engine/engine/`: - `policy.validate` (hard failures; deterministic; offline-capable against snapshots) - `policy.suggest` (AI-assisted; confidence-scored; patch-ready output for patch-mode-01) @@ -99,6 +99,7 @@ Commands are auto-discovered by the registry and lazy-loaded; no registration in - **EXTEND** (arch-07 schema extensions): Register `policy_engine.policy_status` extension on `BacklogItem` via `module-package.yaml` β€” stores last policy validation result (pass/fail, failed rules) for each item; access via `item.get_extension("policy_engine", "policy_status")`. ## Arch-06 Marketplace Readiness + ```yaml publisher: name: nold-ai @@ -108,6 +109,7 @@ integrity: ``` ## Capabilities + - **policy-engine**: Policy framework (validate, suggest); DoR/DoD/Flow/PI policies; JSON and Markdown output; config-driven rules; evidence and recommended action per result; `PolicyRegistryProtocol` for module-contributed policies; arch-07 schema extension on BacklogItem. --- diff --git a/openspec/changes/archive/2026-02-18-validation-01-deep-validation/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-02-18-validation-01-deep-validation/CHANGE_VALIDATION.md index a481337e..9fd1d098 100644 --- a/openspec/changes/archive/2026-02-18-validation-01-deep-validation/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-02-18-validation-01-deep-validation/CHANGE_VALIDATION.md @@ -23,6 +23,7 @@ This is a **core CLI extension**, not a new marketplace module. Validation capab ## Dependencies Affected ### Optional Mode Extensions + - `src/specfact_cli/commands/repro.py` β€” optional `--validation deep` flag (no breaking change) - `src/specfact_cli/validators/repro_checker.py` β€” optional CrossHair target list / timeout override diff --git a/openspec/changes/archive/2026-02-18-validation-01-deep-validation/proposal.md b/openspec/changes/archive/2026-02-18-validation-01-deep-validation/proposal.md index 3b0fc551..c3e9d3c6 100644 --- a/openspec/changes/archive/2026-02-18-validation-01-deep-validation/proposal.md +++ b/openspec/changes/archive/2026-02-18-validation-01-deep-validation/proposal.md @@ -2,7 +2,6 @@ ## Why - Runtime bugs often slip past contract decorators and tests because: (a) contracts only fire at decorated boundaries when those paths are executed; (b) CrossHair in `specfact repro` shares a single time budget over all source and may not reach deep paths; (c) Semgrep is pattern-based and does not reason about logic. Users need a reliable way to validate codebases in three modes: (a) **Sidecar** β€” unmodified original code; (b) **Existing codebases** β€” when code already uses `@icontract`/`@beartype`, run full contract exploration and scenario tests; (c) **Dogfooding** β€” use SpecFact CLI to validate SpecFact CLI itself. This change extends the existing core validation capabilities. It does **not** require a new module; it extends the `specfact repro` and `specfact validate sidecar` commands already in the CLI core. @@ -21,7 +20,6 @@ If the existing validation code lives in `src/specfact_cli/validators/`, no modu ## What Changes - - **EXTEND**: Document and wire a single "thorough validation" path that supports: 1. Sidecar for unmodified code (existing `specfact repro --sidecar --sidecar-bundle`) 2. Contract-decorated codebases via `hatch run contract-test-full` (contracts + CrossHair exploration + scenarios) @@ -32,6 +30,7 @@ If the existing validation code lives in `src/specfact_cli/validators/`, no modu - **EXTEND**: Document dogfooding: how to run full validation (repro + contract-test-full or equivalent) on specfact-cli; add or reference a CI job or local checklist so specfact-cli validates itself before release. ## Capabilities + - **codebase-validation-depth**: Thorough in-depth validation supporting sidecar (unmodified code), contract-decorated codebases (full contract-test stack), and dogfooding (specfact-cli on itself) with clear workflows and optional deep CrossHair/Semgrep usage. --- diff --git a/openspec/changes/archive/2026-02-18-workflow-01-git-worktree-management/proposal.md b/openspec/changes/archive/2026-02-18-workflow-01-git-worktree-management/proposal.md index 62eb2ab0..6773325b 100644 --- a/openspec/changes/archive/2026-02-18-workflow-01-git-worktree-management/proposal.md +++ b/openspec/changes/archive/2026-02-18-workflow-01-git-worktree-management/proposal.md @@ -2,18 +2,17 @@ ## Why - The repository needs a consistent, low-risk way to run multiple feature streams in parallel using `git worktree`. Without a standard lifecycle, teams can accidentally use protected branches (`dev`, `main`) in worktrees, collide on branch-to-folder mappings, or leave stale worktrees that cause confusion and merge friction. ## What Changes - - **NEW**: Add a repository helper script (`scripts/worktree.sh`) that standardizes worktree operations: create, list, and cleanup. - **NEW**: Enforce branch-type policy in helper workflow: only `feature/*`, `bugfix/*`, `hotfix/*`, and `chore/*` are allowed for worktree creation; `dev` and `main` are blocked. - **NEW**: Document deterministic path layout and cleanup rules in `AGENTS.md` and script usage output. - **NEW**: Add unit tests that validate helper behavior and guardrails (including forbidden branch handling). ## Capabilities + - **git-worktree-lifecycle**: Developers can create, inspect, and remove branch-specific worktrees with a standardized command flow that reduces branch/path conflicts and protects `dev`/`main` usage. --- diff --git a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/proposal.md b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/proposal.md index 7277fbc4..cf9a0f27 100644 --- a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/proposal.md +++ b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/proposal.md @@ -36,7 +36,7 @@ No new application code or CLI behavior; documentation and spec-doc alignment on - **Backward compatibility**: N/A (documentation only). - **Rollback plan**: Revert documentation commits. -**Documentation impact (per config.yaml):** All changes are documentation-only and improve accuracy and discoverability at https://docs.specfact.io. New pages will have correct Jekyll front-matter and be linked from `docs/_layouts/default.html`. +**Documentation impact (per config.yaml):** All changes are documentation-only and improve accuracy and discoverability at . New pages will have correct Jekyll front-matter and be linked from `docs/_layouts/default.html`. ## Clarifications for implementation @@ -48,6 +48,6 @@ No new application code or CLI behavior; documentation and spec-doc alignment on - **GitHub Issue**: #291 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/291 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: synced-2026-02-22 diff --git a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/adapter-development-guide/spec.md b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/adapter-development-guide/spec.md index 2b1b4f33..98e083c0 100644 --- a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/adapter-development-guide/spec.md +++ b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/adapter-development-guide/spec.md @@ -9,6 +9,7 @@ Adapter developers have a guide that describes the full BridgeAdapter interface, The adapter development guide (or extended creating-custom-bridges) SHALL document the full BridgeAdapter interface: detect, import_artifact, export_artifact, load_change_tracking, save_change_tracking (or equivalent), with contracts and usage notes. #### Scenario: Developer implements adapter + - **GIVEN** the adapter development guide (or extended creating-custom-bridges) - **WHEN** a developer implements an adapter - **THEN** the full BridgeAdapter interface is documented @@ -19,6 +20,7 @@ The adapter development guide (or extended creating-custom-bridges) SHALL docume The ToolCapabilities model and its role in adapter selection (e.g. sync modes) SHALL be documented, with reference to code (e.g. models/bridge.py) if needed. #### Scenario: Developer declares or uses capabilities + - **GIVEN** the adapter documentation - **WHEN** a developer needs to declare or use adapter capabilities - **THEN** ToolCapabilities model is documented @@ -29,6 +31,7 @@ The ToolCapabilities model and its role in adapter selection (e.g. sync modes) S The adapter guide SHALL provide at least one code reference or minimal example (e.g. base adapter, existing OpenSpec/SpecKit adapter) so that implementation is clear. #### Scenario: Developer follows adapter guide + - **GIVEN** the adapter guide - **WHEN** a developer follows the guide - **THEN** at least one code reference or minimal example is provided @@ -39,6 +42,7 @@ The adapter guide SHALL provide at least one code reference or minimal example ( The adapter development content SHALL be reachable from the docs navigation and from bridge/architecture documentation. #### Scenario: User looks for adapter development + - **GIVEN** the published docs - **WHEN** a user looks for adapter or bridge development - **THEN** the adapter development content is reachable from the docs navigation diff --git a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/adr-template/spec.md b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/adr-template/spec.md index d44fd6a7..91fcdec4 100644 --- a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/adr-template/spec.md +++ b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/adr-template/spec.md @@ -9,6 +9,7 @@ Architecture Decision Records (ADRs) are available so that major architectural d The docs SHALL provide an ADR template with at least: title, status, context, decision, consequences. #### Scenario: Maintainer records new decision + - **GIVEN** the docs repository - **WHEN** a maintainer wants to record a new architectural decision - **THEN** an ADR template exists (e.g. in docs/architecture/adr/template.md) @@ -19,6 +20,7 @@ The docs SHALL provide an ADR template with at least: title, status, context, de The ADR directory SHALL contain at least one ADR (e.g. for module-first architecture) following the template. #### Scenario: Reader opens architecture docs + - **GIVEN** the ADR directory - **WHEN** a reader opens the architecture documentation - **THEN** at least one ADR is present following the template @@ -29,6 +31,7 @@ The ADR directory SHALL contain at least one ADR (e.g. for module-first architec ADRs SHALL be linked from docs/architecture/README.md or docs/reference/architecture.md so they can be found without searching the repo. #### Scenario: User navigates architecture docs + - **GIVEN** the docs site (e.g. docs.specfact.io) - **WHEN** a user navigates architecture or reference docs - **THEN** ADRs are linked diff --git a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/documentation-alignment/spec.md b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/documentation-alignment/spec.md index 2341db4e..bfc9e865 100644 --- a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/documentation-alignment/spec.md +++ b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/documentation-alignment/spec.md @@ -9,6 +9,7 @@ Documentation accurately reflects current implementation so that contributors an The published architecture documentation SHALL state that the module system is production-ready (e.g. since v0.27) and SHALL NOT describe it as "transitioning" or "experimental." #### Scenario: Reader checks module system status + - **GIVEN** the published architecture documentation (e.g. docs/reference/architecture.md, docs/architecture/module-system.md) - **WHEN** a reader looks for the current state of the module system - **THEN** the docs state production-ready status @@ -19,6 +20,7 @@ The published architecture documentation SHALL state that the module system is p The adapter documentation SHALL include the full BridgeAdapter interface: detect, import_artifact, export_artifact, load_change_tracking, save_change_tracking (or equivalent), with current behavior and contracts. #### Scenario: Developer implements BridgeAdapter + - **GIVEN** the adapter documentation - **WHEN** a developer implements or extends a BridgeAdapter - **THEN** the documented interface includes all methods above @@ -29,6 +31,7 @@ The adapter documentation SHALL include the full BridgeAdapter interface: detect The architecture overview SHALL describe the actual layers (Specification, Contract, Enforcement, and where relevant Adapter, Analysis, Module layers) so they match the codebase structure. #### Scenario: Reader learns layer structure + - **GIVEN** the architecture overview - **WHEN** a reader learns the high-level layer structure - **THEN** the docs describe actual layers present in code @@ -39,6 +42,7 @@ The architecture overview SHALL describe the actual layers (Specification, Contr The documentation for CI/CD and CoPilot modes SHALL clarify current mode detection and any limitations (e.g. mode-specific behavior as planned), and SHALL NOT imply full mode implementations that do not exist. #### Scenario: Reader checks mode implementation + - **GIVEN** the documentation for CI/CD and CoPilot modes - **WHEN** a reader checks what is implemented - **THEN** current detector behavior is stated @@ -49,6 +53,7 @@ The documentation for CI/CD and CoPilot modes SHALL clarify current mode detecti The architecture or module docs SHALL describe lazy loading, metadata caching, and the required module package structure (e.g. module-package.yaml, src//main.py) and naming conventions. #### Scenario: Developer needs registry or module layout details + - **GIVEN** the architecture or module docs - **WHEN** a developer needs implementation details for the command registry or module layout - **THEN** lazy loading and metadata caching are described @@ -59,6 +64,7 @@ The architecture or module docs SHALL describe lazy loading, metadata caching, a The ToolCapabilities model and adapter selection SHALL be documented; error handling patterns (custom exceptions, logging) SHALL be described in reference or adapter documentation. #### Scenario: Developer looks for capabilities or error handling + - **GIVEN** the reference or adapter documentation - **WHEN** a developer looks for adapter capabilities or error handling - **THEN** ToolCapabilities and adapter selection are documented @@ -69,6 +75,7 @@ The ToolCapabilities model and adapter selection SHALL be documented; error hand The documentation set SHALL use consistent terminology (e.g. Project Bundle, Plan Bundle) and SHALL standardize or remove version references that cause confusion. #### Scenario: Same concept referenced across docs + - **GIVEN** the documentation set - **WHEN** the same concept is referenced - **THEN** terminology is consistent @@ -79,6 +86,7 @@ The documentation set SHALL use consistent terminology (e.g. Project Bundle, Pla Any Mermaid or component diagram in the docs SHALL show only components that exist in the codebase or are clearly marked as planned; non-existent components (e.g. unimplemented "DevOps Adapters") SHALL be removed or relabeled. #### Scenario: Reader interprets diagram + - **GIVEN** any Mermaid or component diagram in the docs - **WHEN** a reader interprets the diagram - **THEN** only existing or clearly planned components are shown @@ -89,6 +97,7 @@ Any Mermaid or component diagram in the docs SHALL show only components that exi Any stated performance or timing in the docs SHALL reflect current benchmarks or SHALL be removed if outdated. #### Scenario: Docs publish performance claims + - **GIVEN** any stated performance or timing (e.g. "typical execution: < 10s") - **WHEN** the docs are published - **THEN** metrics reflect current benchmarks or are removed if outdated diff --git a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/implementation-status-docs/spec.md b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/implementation-status-docs/spec.md index f8550572..cb5ea87c 100644 --- a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/implementation-status-docs/spec.md +++ b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/implementation-status-docs/spec.md @@ -9,6 +9,7 @@ A single, maintained place describes what is implemented versus planned and poin The implementation status documentation SHALL clearly mark each feature (e.g. architecture commands, protocol FSM, change tracking) as implemented or planned, with brief notes on scope where relevant. #### Scenario: Reader checks feature status + - **GIVEN** the implementation status documentation (e.g. docs/architecture/implementation-status.md) - **WHEN** a reader checks the status of a feature - **THEN** each feature is clearly marked as implemented or planned @@ -19,6 +20,7 @@ The implementation status documentation SHALL clearly mark each feature (e.g. ar For planned or partially implemented features, the implementation status doc SHALL link or reference the relevant OpenSpec change (e.g. architecture-01-solution-layer for architecture derive/validate/trace). #### Scenario: Reader finds spec for planned feature + - **GIVEN** a planned or partially implemented feature - **WHEN** the implementation status doc describes it - **THEN** it links or references the relevant OpenSpec change @@ -29,6 +31,7 @@ For planned or partially implemented features, the implementation status doc SHA Current limitations for change tracking and protocol/FSM behavior SHALL be stated (e.g. no FSM engine, partial adapter support for change tracking) so that expectations match reality. #### Scenario: Reader checks limitations + - **GIVEN** change tracking and protocol/FSM behavior - **WHEN** a user or contributor reads the implementation status - **THEN** current limitations are stated @@ -39,6 +42,7 @@ Current limitations for change tracking and protocol/FSM behavior SHALL be state The implementation status page SHALL be linked from the architecture README or reference architecture page so it can be found without searching. #### Scenario: User navigates architecture docs + - **GIVEN** the docs site - **WHEN** a user navigates architecture docs - **THEN** the implementation status page is linked diff --git a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/module-development-guide/spec.md b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/module-development-guide/spec.md index 27647e8a..ffda8976 100644 --- a/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/module-development-guide/spec.md +++ b/openspec/changes/archive/2026-02-22-arch-08-documentation-discrepancies-remediation/specs/module-development-guide/spec.md @@ -9,27 +9,30 @@ A single, discoverable guide explains how to develop and package new modules so The module development guide SHALL describe the required directory structure (e.g. modules//, module-package.yaml, src//__init__.py, main.py, commands) and file roles. #### Scenario: Developer creates new module -- **GIVEN** the module development guide -- **WHEN** a developer creates a new module -- **THEN** the guide describes the required directory structure -- **AND** file roles are explained + +- __GIVEN__ the module development guide +- __WHEN__ a developer creates a new module +- __THEN__ the guide describes the required directory structure +- __AND__ file roles are explained ### Requirement: Manifest and contract requirements documented The module development guide SHALL document the module-package.yaml schema (name, version, commands, dependencies, schema_extensions, service_bridges) and SHALL mention contract requirements (@icontract, @beartype) for public APIs. #### Scenario: Developer configures module -- **GIVEN** the module development guide -- **WHEN** a developer configures a module -- **THEN** the guide documents the module-package.yaml schema -- **AND** contract requirements for public APIs are mentioned + +- __GIVEN__ the module development guide +- __WHEN__ a developer configures a module +- __THEN__ the guide documents the module-package.yaml schema +- __AND__ contract requirements for public APIs are mentioned ### Requirement: Module guide discoverable The module development guide SHALL be reachable from the docs navigation (e.g. Guides or Reference) and from the architecture or module system documentation. #### Scenario: User looks for module development -- **GIVEN** the published docs (e.g. docs.specfact.io) -- **WHEN** a user looks for how to develop modules -- **THEN** the guide is reachable from the docs navigation -- **AND** from the architecture or module system documentation + +- __GIVEN__ the published docs (e.g. docs.specfact.io) +- __WHEN__ a user looks for how to develop modules +- __THEN__ the guide is reachable from the docs navigation +- __AND__ from the architecture or module system documentation diff --git a/openspec/changes/archive/2026-02-22-backlog-core-02-interactive-issue-creation/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-02-22-backlog-core-02-interactive-issue-creation/CHANGE_VALIDATION.md index d32a69e7..5b7cac89 100644 --- a/openspec/changes/archive/2026-02-22-backlog-core-02-interactive-issue-creation/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-02-22-backlog-core-02-interactive-issue-creation/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ ## Breaking Changes Detected No breaking API/interface changes were detected from the proposed delta: + - `load_backlog_config_from_backlog_file()` is additive. - Existing `load_backlog_config_from_spec()` remains available for compatibility fallback. - `backlog map-fields` CLI enhancements are backward compatible for existing ADO usage. @@ -22,13 +23,16 @@ No breaking API/interface changes were detected from the proposed delta: ## Dependencies Affected ### Critical Updates Required + - None ### Recommended Updates + - `modules/backlog-core/src/backlog_core/graph/builder.py`: consider reading `.specfact/backlog-config.yaml` first in a follow-up for full consistency. - docs pages referencing `backlog map-fields` options should include provider-based flow. ### Directly Scanned Dependencies + - `modules/backlog-core/src/backlog_core/commands/add.py` - `modules/backlog-core/src/backlog_core/graph/builder.py` - `modules/backlog-core/tests/unit/test_schema_extensions.py` @@ -48,6 +52,7 @@ No breaking API/interface changes were detected from the proposed delta: **Decision**: Implement now **Rationale**: Align backlog provider configuration under dedicated `.specfact/backlog-config.yaml` and keep module metadata in sync with marketplace updates. **Next Steps**: + 1. Implement `specfact backlog init-config` scaffold. 2. Extend `specfact backlog map-fields` for provider selection and provider-specific persistence. 3. Run quality gates (format/type/contract) and targeted tests for modified test modules. diff --git a/openspec/changes/archive/2026-02-22-backlog-core-02-interactive-issue-creation/specs/backlog-add/spec.md b/openspec/changes/archive/2026-02-22-backlog-core-02-interactive-issue-creation/specs/backlog-add/spec.md index ac099b09..3ed0e576 100644 --- a/openspec/changes/archive/2026-02-22-backlog-core-02-interactive-issue-creation/specs/backlog-add/spec.md +++ b/openspec/changes/archive/2026-02-22-backlog-core-02-interactive-issue-creation/specs/backlog-add/spec.md @@ -233,7 +233,6 @@ The system SHALL prompt for sprint/iteration and parent assignment in interactiv **And**: Parent compatibility follows creation hierarchy rules with no hardcoded provider-only assumptions - ### Requirement: Centralized retry policy for backlog adapter write operations The system SHALL apply a shared retry policy for transient failures in backlog adapter create operations so command behavior is consistent across providers. @@ -258,7 +257,6 @@ The system SHALL apply a shared retry policy for transient failures in backlog a **And**: The failure is surfaced immediately to the caller with context - #### Scenario: Non-idempotent create avoids ambiguous automatic retry **Given**: A create operation is non-idempotent and the transport fails ambiguously (for example timeout/connection drop after request may have reached provider) @@ -283,7 +281,6 @@ The system SHALL default template selection by adapter when user does not explic **And**: Epic/feature/story hierarchy candidates are resolved consistently for parent selection - ### Requirement: Shared retry policy applied consistently across adapter write operations The system SHALL apply centralized retry policy to backlog adapter write operations beyond create, with operation-specific ambiguity safety. @@ -306,7 +303,6 @@ The system SHALL apply centralized retry policy to backlog adapter write operati **And**: Non-transient failures are surfaced immediately - ### Requirement: Parent candidate discovery must not exclude valid hierarchy parents by implicit sprint defaults The system SHALL avoid implicit current-iteration filtering when loading parent candidates for interactive parent selection. @@ -335,7 +331,6 @@ The system SHALL display a user-facing warning when non-idempotent create fails **And**: CLI advises verifying backlog before retrying manually - #### Scenario: ADO sprint selection resolves iterations using project_id context **Given**: User runs `backlog add` with `--adapter ado --project-id /` and adapter defaults do not already include org/project @@ -346,7 +341,6 @@ The system SHALL display a user-facing warning when non-idempotent create fails **And**: Available iterations are listed for selection when accessible - #### Scenario: GitHub backlog add forwards Projects Type field configuration **Given**: `backlog add` runs with GitHub adapter and template/custom config contains GitHub Projects v2 type field mapping metadata @@ -355,7 +349,6 @@ The system SHALL display a user-facing warning when non-idempotent create fails **Then**: It forwards provider field metadata in payload (for example `provider_fields.github_project_v2`) so the adapter can set the Projects `Type` field in addition to labels - #### Scenario: GitHub ProjectV2 Type mapping can come from repo backlog provider settings **Given**: `.specfact/backlog-config.yaml` defines `backlog_config.providers.github.settings.provider_fields.github_project_v2` @@ -364,7 +357,6 @@ The system SHALL display a user-facing warning when non-idempotent create fails **Then**: The command forwards that provider field configuration in create payload so adapter ProjectV2 Type mapping can run - #### Scenario: GitHub add warns when ProjectV2 Type mapping config is absent **Given**: User runs `backlog add` with GitHub adapter and no ProjectV2 Type mapping metadata is available @@ -373,7 +365,6 @@ The system SHALL display a user-facing warning when non-idempotent create fails **Then**: The command prints a warning that GitHub ProjectV2 Type field will not be set automatically and labels/body fallback is used - #### Scenario: GitHub custom mapping file auto-applies when present **Given**: `--adapter github` and no `--custom-config` flag is provided @@ -383,7 +374,6 @@ The system SHALL display a user-facing warning when non-idempotent create fails **And**: Parent validation and candidate filtering use those overrides **And**: If the file does not exist, the command falls back to default `github_projects` mapping behavior. - #### Scenario: GitHub parent is linked using native sub-issue relationship **Given**: A GitHub parent issue is selected during `backlog add` diff --git a/openspec/changes/archive/2026-02-22-bundle-mapper-01-mapping-strategy/proposal.md b/openspec/changes/archive/2026-02-22-bundle-mapper-01-mapping-strategy/proposal.md index 0398cccd..276e32ee 100644 --- a/openspec/changes/archive/2026-02-22-bundle-mapper-01-mapping-strategy/proposal.md +++ b/openspec/changes/archive/2026-02-22-bundle-mapper-01-mapping-strategy/proposal.md @@ -2,7 +2,6 @@ ## Why - Teams need intelligent spec-to-bundle assignment with confidence scoring and user confirmation to prevent mis-bundled specs. Currently, bundle assignment is manual or based on simple heuristics, leading to specs landing in wrong bundles and making conflict detection unreliable. This change establishes the **`bundle-mapper` module** β€” providing confident, history-aware spec-to-bundle assignment with interactive review for ambiguous mappings. Per the SpecFact Backlog & OpenSpec Implementation Roadmap (2026-01-18), this implements Plan C with three confidence signals. @@ -27,6 +26,7 @@ modules/bundle-mapper/ ``` **`module-package.yaml` declares:** + - `name: bundle-mapper` - `version: 0.1.0` - `commands: []` (no top-level commands; enhances existing `backlog refine/import` via hooks) @@ -54,6 +54,7 @@ modules/bundle-mapper/ ``` **`module-package.yaml` declares:** + - `name: bundle-mapper` - `version: 0.1.0` - `commands: []` (no top-level commands; enhances existing `backlog refine/import` via hooks) @@ -63,7 +64,6 @@ modules/bundle-mapper/ ## What Changes - - **NEW**: `BundleMapper` engine in `modules/bundle-mapper/src/bundle_mapper/mapper/engine.py` β€” confidence-based mapping with three signals: explicit labels, historical patterns, content similarity. - **NEW**: `BundleMapping` model in `modules/bundle-mapper/src/bundle_mapper/models/bundle_mapping.py` β€” result model with `bundle_id`, `confidence`, `candidates`, `explanation`. - **NEW**: Mapping history persistence in `modules/bundle-mapper/src/bundle_mapper/mapper/history.py` β€” auto-learned rules from user confirmations stored in `.specfact/config.yaml`. @@ -78,6 +78,7 @@ modules/bundle-mapper/ - **EXTEND**: OpenSpec generation pipeline accepts `BundleMapping` parameter and records mapping decisions via schema extension. ## Capabilities + - **bundle-mapper**: `BundleMapper` with three confidence signals (labels, history, similarity); `BundleMapping` result model; mapping history persistence (auto-learned rules); interactive UI with confidence visualization; `--auto-bundle` flag for `backlog refine/import`; arch-07 SourceTracking extensions for mapping metadata. --- diff --git a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/CHANGE_VALIDATION.md index 5f24d43d..f2e2dc35 100644 --- a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/CHANGE_VALIDATION.md @@ -1,6 +1,6 @@ # Change Validation Report: marketplace-01-central-module-registry -**Validation Date**: 2026-02-20 23:25:03 +**Validation Date**: 2026-02-20 23:25:03 **Change Proposal**: [proposal.md](./proposal.md) **Validation Method**: Dry-run simulation in temporary workspace + dependency scan with `rg` @@ -57,11 +57,13 @@ **Decision**: Adjust change for backward compatibility **Rationale**: + - Keep existing workflows and scripts stable - Avoid conflict with existing canonical OpenSpec specs - Preserve migration path to canonical `specfact module` lifecycle UX without forcing immediate breaking changes **Next Steps**: + 1. Implement deprecation-compatible alias behavior in `init` lifecycle options (no hard removal) 2. Keep `specfact module` command group as canonical lifecycle command surface 3. Add/adjust tests to lock compatibility + lazy-loader entrypoint reliability diff --git a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/design.md b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/design.md index caea183c..f6727df8 100644 --- a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/design.md +++ b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/design.md @@ -5,12 +5,14 @@ The modular architecture (arch-01 through arch-07) provides strong encapsulation for module development, but all modules remain bundled with the CLI. To enable ecosystem growth and allow users to install/uninstall modules independently, we need marketplace infrastructure. **Current State:** + - All modules in `src/specfact_cli/modules/` (built-in) - Module discovery scans single location - No installation/uninstallation capability - No external module registry **Constraints:** + - Must remain offline-first (marketplace access is optional, not required) - Must support built-in modules for offline usage - Must verify module integrity (checksums from arch-06) @@ -20,6 +22,7 @@ The modular architecture (arch-01 through arch-07) provides strong encapsulation ## Goals / Non-Goals **Goals:** + - Enable module installation from central registry (official NOLD AI modules) - Support multi-location discovery (built-in, marketplace, custom) - Implement checksum verification on install @@ -27,6 +30,7 @@ The modular architecture (arch-01 through arch-07) provides strong encapsulation - Establish registry infrastructure foundation (index.json schema) **Non-Goals:** + - Third-party publishing (marketplace-01 is official modules only) - Dependency resolution (deferred to marketplace-02) - Module sandboxing or permissions (future enhancement) @@ -38,6 +42,7 @@ The modular architecture (arch-01 through arch-07) provides strong encapsulation ### Decision 1: Registry Location and Schema **Options:** + - **A**: GitHub repository with index.json (static files, no backend) - **B**: Dedicated registry service with API - **C**: PyPI-style package index @@ -45,6 +50,7 @@ The modular architecture (arch-01 through arch-07) provides strong encapsulation **Choice: A (GitHub repository)** **Rationale:** + - Simple, no infrastructure overhead - Aligns with offline-first philosophy (registry is optional) - Version control for registry changes @@ -52,11 +58,13 @@ The modular architecture (arch-01 through arch-07) provides strong encapsulation - Easy to mirror or fork **Trade-offs:** + - No search API (client-side filtering) - Manual publishing workflow (acceptable for MVP with official modules only) - Download rate limits (GitHub raw content) **Schema:** + ```json { "schema_version": "1.0", @@ -81,6 +89,7 @@ The modular architecture (arch-01 through arch-07) provides strong encapsulation ### Decision 2: Module Installation Paths **Options:** + - **A**: ~/.specfact/marketplace-modules/ (separate from built-in) - **B**: ~/.local/share/specfact-cli/modules/ (XDG standard) - **C**: Same location as built-in (site-packages/specfact_cli/modules/) @@ -88,12 +97,14 @@ The modular architecture (arch-01 through arch-07) provides strong encapsulation **Choice: A (separate marketplace path)** **Rationale:** + - Clear separation: built-in vs marketplace vs custom - No conflicts with package manager (pip/uvx) - Easy uninstall (delete directory) - Preserves built-in modules on CLI upgrade **Paths:** + - Built-in: `{site-packages}/specfact_cli/modules/` - Marketplace: `~/.specfact/marketplace-modules/` - Custom: `~/.specfact/custom-modules/` @@ -101,6 +112,7 @@ The modular architecture (arch-01 through arch-07) provides strong encapsulation ### Decision 3: Multi-Location Discovery Order **Options:** + - **A**: Built-in β†’ Marketplace β†’ Custom (priority order) - **B**: Marketplace β†’ Built-in β†’ Custom (marketplace takes precedence) - **C**: Scan all, warn on duplicates @@ -108,12 +120,14 @@ The modular architecture (arch-01 through arch-07) provides strong encapsulation **Choice: A (built-in first)** **Rationale:** + - Guarantees core functionality even if marketplace unreachable - Offline-first: built-in modules always available - Predictable: built-in modules can't be shadowed - Users can override with custom path if needed **Discovery Algorithm:** + ```python def discover_all_modules(): discovered = {} @@ -138,6 +152,7 @@ def discover_all_modules(): ### Decision 4: Installation Workflow **Options:** + - **A**: Download β†’ Verify β†’ Extract β†’ Register (single-step) - **B**: Download β†’ Verify β†’ Prompt β†’ Extract β†’ Register (confirm before install) - **C**: Download β†’ Extract β†’ Verify β†’ Register (verify after extraction) @@ -145,12 +160,14 @@ def discover_all_modules(): **Choice: A (verify before extract)** **Rationale:** + - Fail fast on integrity issues - No partial installs (atomic operation) - Matches arch-06 security model - Simpler rollback **Workflow:** + ``` 1. Fetch index.json from registry 2. Look up module by ID @@ -165,6 +182,7 @@ def discover_all_modules(): ### Decision 5: Offline Behavior **Options:** + - **A**: Fail on network unavailable - **B**: Warn and continue with cached index - **C**: Warn and continue with built-in modules only @@ -172,11 +190,13 @@ def discover_all_modules(): **Choice: C (graceful degradation)** **Rationale:** + - Offline-first philosophy - Built-in modules remain functional - User can install from local tarball (future: `specfact module install ./module.tar.gz`) **Fallback Strategy:** + - Network unavailable β†’ log warning, use built-in modules - Index fetch fails β†’ log warning, use cached index if available - Module download fails β†’ error, suggest offline installation @@ -184,6 +204,7 @@ def discover_all_modules(): ### Decision 6: Module Namespace Enforcement **Options:** + - **A**: Enforce namespace prefix (specfact/*, acme-corp/*) - **B**: Allow flat names (backlog, sync) - **C**: Enforce for marketplace, allow flat for custom @@ -191,6 +212,7 @@ def discover_all_modules(): **Choice: C (enforce for marketplace)** **Rationale:** + - Official modules use `specfact/*` namespace - Custom modules can use flat names (user responsibility) - Future-proof for third-party publishing @@ -198,48 +220,60 @@ def discover_all_modules(): ## Risks / Trade-offs ### Risk 1: Registry unavailable (network outage, GitHub down) + **Mitigation**: Offline-first design, built-in modules remain functional, cached index support (future) ### Risk 2: Malicious modules in marketplace + **Mitigation**: Official modules only (MVP scope), checksum verification (arch-06), signature verification (arch-06) ### Risk 3: Module conflicts (name collision) + **Mitigation**: Namespace enforcement (specfact/*), discovery order (built-in first) ### Risk 4: Incomplete uninstall (orphaned files) + **Mitigation**: Modules in isolated directories, uninstall removes entire directory, no shared dependencies (MVP) ### Risk 5: Version compatibility issues + **Mitigation**: core_compatibility field in index.json, skip incompatible modules during discovery ### Risk 6: Large download sizes + **Trade-off**: Acceptable for MVP (official modules are small), future: delta updates ## Migration Plan ### Phase 1: Repository Setup + 1. Create `nold-ai/specfact-cli-modules` repository 2. Set up registry/ structure (index.json, modules/, signatures/) 3. Add publishing scripts ### Phase 2: CLI Implementation + 1. Implement multi-location discovery 2. Create `module` module with commands 3. Implement marketplace client 4. Implement module installer ### Phase 3: Built-in Module Packaging + 1. Package existing modules as tarballs 2. Generate checksums 3. Update index.json ### Phase 4: Testing and Documentation + 1. Test install/uninstall workflows 2. Test offline behavior 3. Write user guides ### Rollback + If critical issues arise: + 1. Disable marketplace client (feature flag) 2. Revert to built-in-only discovery 3. Modules already installed remain functional @@ -247,22 +281,25 @@ If critical issues arise: ## Open Questions **Q1: Should we cache the registry index locally?** + - **Recommendation**: Yes (future enhancement) - cache with TTL, refresh on `module search` - **MVP**: Fetch on every operation, fail gracefully if offline **Q2: How to handle module upgrades with breaking changes?** + - **Recommendation**: core_compatibility field prevents incompatible installs - **Future**: Warn on major version upgrades **Q3: Should uninstall require confirmation?** + - **Recommendation**: Yes for built-in modules (error: can't uninstall), No for marketplace/custom - **MVP**: Simple uninstall, no confirmation **Q4: How to handle module dependencies (module A requires module B)?** + - **Recommendation**: Deferred to marketplace-02 (dependency resolution) - **MVP**: module_dependencies field exists but not enforced during install - ### Decision 7: Lifecycle UX Harmonization (init vs module command) **Context:** `specfact init` already owns module enable/disable/list lifecycle flags from prior architecture changes. This marketplace change introduces a new canonical module management command group (`specfact module ...`), creating potential UX duplication. @@ -270,11 +307,13 @@ If critical issues arise: **Choice:** Keep `init` lifecycle flags as backward-compatible aliases while standardizing user guidance and documentation on `specfact module` as canonical lifecycle surface. **Rationale:** + - Avoids breaking existing automation and user workflows built on `specfact init --enable-module/--disable-module/--list-modules`. - Preserves behavior required by existing canonical specs and tests while still reducing UX ambiguity. - Enables phased deprecation instead of disruptive removal. **Implementation constraints:** + - No hard removal of init lifecycle flags in this change. - Alias behavior must remain functionally equivalent for state management operations. - Help text and docs should steer new users to `specfact module` lifecycle commands. diff --git a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-installation/spec.md b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-installation/spec.md index 2ed1d362..e338e48f 100644 --- a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-installation/spec.md +++ b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-installation/spec.md @@ -11,6 +11,7 @@ Defines CLI commands and infrastructure for installing, uninstalling, searching, The system SHALL provide `specfact module install ` command that downloads, verifies, and installs modules from the registry. #### Scenario: Install module from marketplace + - **WHEN** user runs `specfact module install specfact/backlog` - **THEN** system SHALL fetch registry index - **AND** SHALL download module tarball @@ -20,11 +21,13 @@ The system SHALL provide `specfact module install ` command that down - **AND** SHALL display success message #### Scenario: Install specific version + - **WHEN** user runs `specfact module install specfact/backlog --version 0.29.0` - **THEN** system SHALL install specified version - **AND** SHALL verify core_compatibility with current CLI version #### Scenario: Install module already installed + - **WHEN** user installs module that is already installed - **THEN** system SHALL display message "Module already installed (version X)" - **AND** SHALL suggest using upgrade command @@ -34,6 +37,7 @@ The system SHALL provide `specfact module install ` command that down The system SHALL provide `specfact module uninstall ` command that removes modules from marketplace path. #### Scenario: Uninstall marketplace module + - **WHEN** user runs `specfact module uninstall backlog` - **THEN** system SHALL check if module is from marketplace - **AND** SHALL remove ~/.specfact/marketplace-modules/backlog/ directory @@ -41,6 +45,7 @@ The system SHALL provide `specfact module uninstall ` command that - **AND** SHALL display success message #### Scenario: Attempt to uninstall built-in module + - **WHEN** user attempts to uninstall built-in module - **THEN** system SHALL display error "Cannot uninstall built-in module" - **AND** SHALL NOT modify module @@ -50,6 +55,7 @@ The system SHALL provide `specfact module uninstall ` command that The system SHALL provide `specfact module search ` command that searches registry index by name, description, or tags. #### Scenario: Search modules by keyword + - **WHEN** user runs `specfact module search backlog` - **THEN** system SHALL fetch registry index - **AND** SHALL filter modules matching query in name, description, or tags @@ -60,11 +66,13 @@ The system SHALL provide `specfact module search ` command that searches The system SHALL provide `specfact module list` command that displays modules from all sources with source indicators. #### Scenario: List all modules + - **WHEN** user runs `specfact module list` - **THEN** system SHALL show modules from built-in, marketplace, and custom paths - **AND** SHALL indicate source (built-in/marketplace/custom) for each module #### Scenario: List marketplace modules only + - **WHEN** user runs `specfact module list --source marketplace` - **THEN** system SHALL show only marketplace-installed modules @@ -73,6 +81,7 @@ The system SHALL provide `specfact module list` command that displays modules fr The system SHALL provide `specfact module upgrade ` command that upgrades marketplace modules to latest version. #### Scenario: Upgrade marketplace module + - **WHEN** user runs `specfact module upgrade backlog` - **THEN** system SHALL fetch registry index - **AND** SHALL check if newer version available @@ -80,6 +89,7 @@ The system SHALL provide `specfact module upgrade ` command that up - **AND** SHALL remove old version after successful install #### Scenario: Upgrade reinstalls when module already exists + - **WHEN** user runs `specfact module upgrade backlog` and backlog is already installed - **THEN** system SHALL replace existing installed files with the upgraded package - **AND** SHALL NOT no-op due to existing install marker files @@ -89,6 +99,7 @@ The system SHALL provide `specfact module upgrade ` command that up The system SHALL reject archive members that escape the intended extraction root. #### Scenario: Installer blocks path traversal entries + - **WHEN** a downloaded marketplace tarball contains absolute paths or `..` traversal - **THEN** install SHALL fail before extraction - **AND** SHALL raise a validation error indicating unsafe archive content diff --git a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-lifecycle-management/spec.md b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-lifecycle-management/spec.md index bb9fd8f7..8085a386 100644 --- a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-lifecycle-management/spec.md +++ b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-lifecycle-management/spec.md @@ -7,31 +7,35 @@ The system SHALL extend registration to handle modules from built-in, marketplace, and custom sources with appropriate lifecycle rules. #### Scenario: Marketplace modules can be uninstalled + - **WHEN** module from marketplace is registered - **THEN** system SHALL mark it as uninstallable - **AND** SHALL allow removal via uninstall command #### Scenario: Built-in modules cannot be uninstalled + - **WHEN** module from built-in source is registered - **THEN** system SHALL mark it as non-uninstallable - **AND** SHALL prevent removal via uninstall command #### Scenario: Registration validates namespace for marketplace modules + - **WHEN** marketplace module is registered - **THEN** system SHALL validate id uses "namespace/name" format - **AND** SHALL log warning if flat name used - ### Requirement: Lifecycle command harmonization remains backward compatible The system SHALL keep existing init-based lifecycle flags functional while introducing `specfact module` as the canonical lifecycle command surface. #### Scenario: init lifecycle flags remain functional + - **WHEN** user runs `specfact init --list-modules` or `--enable-module/--disable-module` - **THEN** system SHALL preserve current lifecycle behavior and state updates - **AND** SHALL provide deprecation guidance toward `specfact module` commands #### Scenario: module command is canonical lifecycle surface + - **WHEN** user runs `specfact module list` or lifecycle operations - **THEN** system SHALL provide equivalent lifecycle management capabilities - **AND** documentation SHALL reference `specfact module` as primary UX diff --git a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-marketplace-registry/spec.md b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-marketplace-registry/spec.md index 0bf9b86c..1a13ef08 100644 --- a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-marketplace-registry/spec.md +++ b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-marketplace-registry/spec.md @@ -11,17 +11,20 @@ Defines the central module registry infrastructure with index schema, module met The system SHALL define an index.json schema for the central registry containing module metadata including ID, namespace, version, download URLs, and checksums. #### Scenario: Index includes module with full metadata + - **WHEN** registry index.json is parsed - **THEN** it SHALL include schema_version field - **AND** SHALL include modules array with module entries - **AND** each module SHALL have: id, namespace, name, description, latest_version, core_compatibility, download_url, checksum_sha256 #### Scenario: Module ID uses namespace format + - **WHEN** module is listed in registry - **THEN** id SHALL use format "namespace/name" (e.g., "specfact/backlog") - **AND** namespace SHALL match separate namespace field #### Scenario: Core compatibility uses PEP 440 specifier + - **WHEN** module declares core_compatibility - **THEN** it SHALL use PEP 440 specifier format (e.g., ">=0.28.0,<1.0.0") - **AND** SHALL be validated during module installation @@ -31,18 +34,21 @@ The system SHALL define an index.json schema for the central registry containing The system SHALL implement a registry client that fetches index.json from the GitHub repository. #### Scenario: Client fetches registry index + - **WHEN** client calls fetch_registry_index() - **THEN** it SHALL request index.json from GitHub raw content URL - **AND** SHALL parse JSON response - **AND** SHALL return dict with schema_version and modules #### Scenario: Network unavailable during fetch + - **WHEN** client attempts to fetch index but network is unavailable - **THEN** it SHALL log warning "Registry unavailable, using offline mode" - **AND** SHALL NOT raise exception - **AND** SHALL return None or empty index #### Scenario: Invalid JSON in registry index + - **WHEN** registry index contains invalid JSON - **THEN** client SHALL log error with parse details - **AND** SHALL raise ValueError with message "Invalid registry index format" @@ -52,18 +58,21 @@ The system SHALL implement a registry client that fetches index.json from the Gi The system SHALL download module tarballs from registry URLs and verify checksums before extraction. #### Scenario: Download module tarball + - **WHEN** download_module() is called with module_id and version - **THEN** system SHALL look up module in registry index - **AND** SHALL download tarball from download_url to temp directory - **AND** SHALL verify checksum matches checksum_sha256 from index #### Scenario: Checksum mismatch detected + - **WHEN** downloaded tarball checksum does not match index - **THEN** system SHALL delete downloaded file - **AND** SHALL raise SecurityError with message "Checksum mismatch for module X" - **AND** SHALL NOT proceed with installation #### Scenario: Module not found in registry + - **WHEN** download_module() is called with non-existent module_id - **THEN** system SHALL raise ValueError with message "Module 'X' not found in registry" - **AND** SHALL suggest using `specfact module search` to find available modules @@ -73,12 +82,14 @@ The system SHALL download module tarballs from registry URLs and verify checksum The system SHALL support offline operation with graceful degradation when registry is unavailable. #### Scenario: Registry fetch fails gracefully + - **WHEN** registry fetch fails due to network issues - **THEN** system SHALL log warning - **AND** SHALL continue with built-in modules only - **AND** SHALL NOT block CLI functionality #### Scenario: Install command fails offline + - **WHEN** user runs install command but registry unavailable - **THEN** system SHALL display error "Cannot install from marketplace (offline)" - **AND** SHALL suggest installing from local tarball (future feature) diff --git a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-packages/spec.md b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-packages/spec.md index 5ca14729..812f5d03 100644 --- a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-packages/spec.md +++ b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/module-packages/spec.md @@ -7,11 +7,13 @@ The system SHALL extend module discovery to scan built-in, marketplace, and custom paths with source tracking. #### Scenario: Discovery function returns source information + - **WHEN** discover_package_metadata() finds a module - **THEN** it SHALL include source field in metadata - **AND** source SHALL be "builtin", "marketplace", or "custom" #### Scenario: Registry stores module source + - **WHEN** module is registered - **THEN** registry SHALL persist source information - **AND** SHALL be queryable via module list command diff --git a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/multi-location-discovery/spec.md b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/multi-location-discovery/spec.md index 8186babb..0659e077 100644 --- a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/multi-location-discovery/spec.md +++ b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/specs/multi-location-discovery/spec.md @@ -11,17 +11,20 @@ Defines module discovery across built-in, marketplace, and custom installation p The system SHALL discover modules from built-in, marketplace, and custom paths in priority order. #### Scenario: Discovery scans all three locations + - **WHEN** module discovery runs - **THEN** system SHALL scan {site-packages}/specfact_cli/modules/ - **AND** SHALL scan ~/.specfact/marketplace-modules/ if exists - **AND** SHALL scan ~/.specfact/custom-modules/ if exists #### Scenario: Built-in modules take priority + - **WHEN** module "backlog" exists in both built-in and marketplace - **THEN** system SHALL use built-in version - **AND** SHALL log warning about shadowed marketplace module #### Scenario: Marketplace modules discovered when no built-in + - **WHEN** module exists in marketplace but not built-in - **THEN** system SHALL discover and register marketplace module @@ -30,11 +33,13 @@ The system SHALL discover modules from built-in, marketplace, and custom paths i The system SHALL track the source (built-in/marketplace/custom) for each discovered module. #### Scenario: Module metadata includes source + - **WHEN** module is discovered - **THEN** system SHALL record source in module metadata - **AND** source SHALL be one of: "builtin", "marketplace", "custom" #### Scenario: List command shows module source + - **WHEN** user runs `specfact module list` - **THEN** each module SHALL display source indicator - **AND** built-in modules SHALL be marked as "[built-in]" @@ -44,11 +49,13 @@ The system SHALL track the source (built-in/marketplace/custom) for each discove The system SHALL handle missing marketplace or custom paths without errors. #### Scenario: Marketplace path does not exist + - **WHEN** ~/.specfact/marketplace-modules/ does not exist - **THEN** discovery SHALL continue with built-in modules only - **AND** SHALL NOT log warning (normal state) #### Scenario: Custom path does not exist + - **WHEN** ~/.specfact/custom-modules/ does not exist - **THEN** discovery SHALL continue normally - **AND** SHALL NOT raise exception diff --git a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/tasks.md b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/tasks.md index 7984a593..b02df9e7 100644 --- a/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/tasks.md +++ b/openspec/changes/archive/2026-02-22-marketplace-01-central-module-registry/tasks.md @@ -3,6 +3,7 @@ ## TDD / SDD Order (Enforced) Per config.yaml, tests MUST come before implementation for any behavior-changing task. Order: + 1. Spec deltas (already created) 2. Tests from spec scenarios (expect failure) 3. Code implementation (until tests pass and behavior satisfies spec) diff --git a/openspec/changes/archive/2026-02-23-backlog-core-04-installed-runtime-discovery-and-add-prompt/proposal.md b/openspec/changes/archive/2026-02-23-backlog-core-04-installed-runtime-discovery-and-add-prompt/proposal.md index 190bfcc9..abda3be8 100644 --- a/openspec/changes/archive/2026-02-23-backlog-core-04-installed-runtime-discovery-and-add-prompt/proposal.md +++ b/openspec/changes/archive/2026-02-23-backlog-core-04-installed-runtime-discovery-and-add-prompt/proposal.md @@ -2,14 +2,12 @@ ## Why - `specfact backlog add` and related backlog-core commands are available in development (`hatch run specfact`) but can be missing from PyPI-installed runtime command surfaces, even at the same version. This creates a production usability regression where documented commands are unavailable after upgrade. Additionally, `backlog add` currently lacks a dedicated slash-command prompt in `resources/prompts/`, resulting in inconsistent IDE workflow coverage compared to `backlog refine` and `backlog daily`. ## What Changes - - **MODIFY**: Module discovery root fallback logic so installed runtime can discover workspace-level `modules/` when invoked from a repo checkout, restoring parity with development command surfaces. - **MODIFY**: Add tests for installed-runtime discovery path behavior and command-surface parity assumptions. - **NEW**: Add `resources/prompts/specfact.backlog-add.md` slash prompt for the new `backlog add` workflow. @@ -17,6 +15,7 @@ Additionally, `backlog add` currently lacks a dedicated slash-command prompt in - **MODIFY**: Add/update tests that verify IDE template installation includes backlog-add prompt. ## Capabilities + - **backlog-core** (extended): Installed runtime command-surface parity for workspace module discovery. - **backlog** (extended): Backlog add slash-command prompt parity with existing backlog prompt workflows. diff --git a/openspec/changes/archive/2026-03-03-backlog-auth-01-backlog-auth-commands/proposal.md b/openspec/changes/archive/2026-03-03-backlog-auth-01-backlog-auth-commands/proposal.md index d7a6ffec..95e229a5 100644 --- a/openspec/changes/archive/2026-03-03-backlog-auth-01-backlog-auth-commands/proposal.md +++ b/openspec/changes/archive/2026-03-03-backlog-auth-01-backlog-auth-commands/proposal.md @@ -2,12 +2,10 @@ ## Why - Module-migration-03 removes the auth module from core and keeps only a central auth interface (token storage by provider_id). Auth for DevOps providers (GitHub, Azure DevOps) belongs with the backlog domain: users who install the backlog bundle need `specfact backlog auth azure-devops` and `specfact backlog auth github`, not a global `specfact auth`. This change implements those commands in the specfact-cli-modules backlog bundle so that after migration-03, backlog users get auth under `specfact backlog auth`. ## What Changes - - **specfact-cli-modules (backlog bundle)**: Add a `backlog auth` subgroup to the backlog Typer app with subcommands: - `specfact backlog auth azure-devops` (options: `--pat`, `--use-device-code`; same behaviour as former `specfact auth azure-devops`) - `specfact backlog auth github` (device code flow; same as former `specfact auth github`) @@ -17,6 +15,7 @@ Module-migration-03 removes the auth module from core and keeps only a central a - **specfact-cli**: No code changes in this repo; migration-03 already provides the central auth interface and removes the auth module. ## Capabilities + - `backlog-auth-commands`: When the specfact-backlog bundle is installed, the CLI exposes `specfact backlog auth` with subcommands azure-devops, github, status, clear. Each subcommand uses the core auth interface for persistence. Existing tokens stored by a previous `specfact auth` (pre–migration-03) continue to work because the storage path and provider_ids are unchanged. --- diff --git a/openspec/changes/archive/2026-03-03-backlog-core-05-user-modules-bootstrap/design.md b/openspec/changes/archive/2026-03-03-backlog-core-05-user-modules-bootstrap/design.md index 75d1cd96..aefa719c 100644 --- a/openspec/changes/archive/2026-03-03-backlog-core-05-user-modules-bootstrap/design.md +++ b/openspec/changes/archive/2026-03-03-backlog-core-05-user-modules-bootstrap/design.md @@ -26,6 +26,7 @@ Installed runtime behavior currently depends on discovery of repository-local `m ## Approach 1. Discovery / installer alignment + - Add canonical user root constant shared by discovery + installer logic. - Make installer default to user root. - Keep legacy `marketplace-modules`/`custom-modules` roots discoverable as compatibility paths. @@ -33,6 +34,7 @@ Installed runtime behavior currently depends on discovery of repository-local `m - Ensure project scope is discovered before user scope to give repository-local intent precedence. 2. Module init bootstrap + - Add `specfact module init` sync that copies shipped module packages into user root when absent or outdated. - Copy safely (create/update module directories) and avoid destructive deletion of unrelated user modules. - Keep bootstrap explicit under the `module` command group and leave top-level `init` behavior unchanged. @@ -40,6 +42,7 @@ Installed runtime behavior currently depends on discovery of repository-local `m - For project scope, default repo to CWD and allow explicit repo override. 3. Startup freshness checks + - Extend startup check pipeline with module freshness inspection. - Reuse current cadence policy style: - run on CLI version change; @@ -50,28 +53,33 @@ Installed runtime behavior currently depends on discovery of repository-local `m - Print scope-specific guidance commands when stale/missing modules are detected. 4. Module list bundled availability + - Add a `module list` switch that computes bundled modules available from package/workspace bundle sources. - Diff bundled module names against active discovered modules. - Render bundled-not-installed modules as a separate section/table with install hints for user and project scope init commands. 5. Scoped install/uninstall consistency + - `module install` accepts explicit scope (`user` default, `project` optional with repo path). - Install resolution checks bundled sources first for exact module name, then marketplace fallback. - `module uninstall` accepts explicit scope; when module exists in both scopes and no scope is set, command errors and requires explicit selection. - Uninstall operation removes only the selected scope artifact. 6. Local trust and signature hardening + - Add denylist file support (for example under user config) checked before any install/bootstrap copy operation. - Add one-time trust acknowledgments for non-official publishers (persisted in user config); non-interactive mode must require explicit trust flag. - Verify shipped/bundled module integrity/signature metadata before install/bootstrap; fail closed on verification errors unless explicit override is provided for developer workflows. - Treat publisher string (`nold-ai`) as informational only; authenticity is derived from signature verification. 7. Release-time signing of bundled modules + - Add a repository-local signing step that generates signatures/checksums for bundled modules during release orchestration. - Private signing key remains externalized (CI secret or secure signing service); never committed in repository. - Signing outputs are committed as module metadata/signature artifacts consumed by runtime verifier. 3. Resource parity + - Keep prompt resources sourced from packaged `resources/prompts` in installed runtime. - Validate that prompt copy to repo-local IDE targets works independently of CWD and module root. diff --git a/openspec/changes/archive/2026-03-03-backlog-core-05-user-modules-bootstrap/proposal.md b/openspec/changes/archive/2026-03-03-backlog-core-05-user-modules-bootstrap/proposal.md index 692682c4..d439a519 100644 --- a/openspec/changes/archive/2026-03-03-backlog-core-05-user-modules-bootstrap/proposal.md +++ b/openspec/changes/archive/2026-03-03-backlog-core-05-user-modules-bootstrap/proposal.md @@ -2,24 +2,12 @@ ## Why - - - - - - `specfact backlog add` is still missing in installed-runtime contexts when command discovery depends on repository-local `modules/` folders. This makes behavior vary by working directory and machine. For production usage, shipped modules and their resources should be managed as user-level artifacts. We need a reliable path where `specfact module init` prepares a per-user module root (not repo-local) so command availability is stable. ## What Changes - - - - - - - **MODIFY**: Add a canonical user module root at `/.specfact/modules` for installed module artifacts. - **MODIFY**: Ensure discovery and installer flows prefer `/.specfact/modules` and stop treating workspace `./modules` as an automatic discovery root. - **MODIFY**: Add workspace-local module discovery only under `/.specfact/modules` to avoid claiming ownership of non-SpecFact repository paths. @@ -44,11 +32,11 @@ For production usage, shipped modules and their resources should be managed as u - **MODIFY**: Strengthen prompt resource detection/copy tests so `specfact init ide` consistently finds bundled prompt resources and installs them to project target locations. ## Capabilities + - **backlog-core** (extended): User-level module availability for `backlog add` and related command surface. - **init/module-registry** (extended): Stable user-root module lifecycle behavior and bootstrap. - **ide setup** (extended): Prompt resource detection/copy parity for project prompt targets. - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/CHANGE_VALIDATION.md index 6571ef92..c727b688 100644 --- a/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/CHANGE_VALIDATION.md @@ -1,9 +1,11 @@ # CHANGE VALIDATION: backlog-core-06-refine-custom-field-writeback ## Date + 2026-02-25 ## Scope Reviewed + - Proposal, design, tasks, and spec delta for custom field writeback reliability and tmp import ID contract. - Impacted runtime surfaces: - `src/specfact_cli/backlog/mappers/ado_mapper.py` @@ -12,6 +14,7 @@ - `resources/prompts/specfact.backlog-refine.md` ## Breaking-Change Analysis + - External CLI flags: no additions/removals. - Behavior changes: - ADO writeback target field selection becomes deterministic and honors custom mapping precedence. @@ -19,11 +22,13 @@ - Compatibility: low risk and backward-compatible for valid refine artifacts. Invalid artifacts now fail faster with guidance. ## Dependency Analysis + - Adapter dependency: confined to existing `AdoFieldMapper` and `AdoAdapter.update_backlog_item` interaction. - Command dependency: confined to `backlog refine --import-from-tmp` flow and prompt/export guidance. - No new package/runtime dependencies introduced. ## Validation Commands + ```bash openspec validate backlog-core-06-refine-custom-field-writeback --strict ``` @@ -31,7 +36,9 @@ openspec validate backlog-core-06-refine-custom-field-writeback --strict Result: `Change 'backlog-core-06-refine-custom-field-writeback' is valid`. ## Conclusion + Change is safe to implement with focused tests covering: + 1. custom mapping precedence for canonical write targets, 2. adapter patch paths for mapped fields, 3. tmp import ID mismatch failure behavior. @@ -39,11 +46,13 @@ Change is safe to implement with focused tests covering: ## Validation Addendum (2026-02-26) ### Delta Scope + - Added ADO comment endpoint API-version compatibility coverage: - comment activities (`/workitems/{id}/comments`) use `7.1-preview.4` - standard work-item and WIQL operations remain on stable `7.1` ### Validation Commands + ```bash openspec validate backlog-core-06-refine-custom-field-writeback --strict ``` diff --git a/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/design.md b/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/design.md index 0d1c133a..aa359e9f 100644 --- a/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/design.md +++ b/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/design.md @@ -6,32 +6,38 @@ Separately, ADO comment endpoints (`/workitems/{id}/comments`) are versioned dif ## Goals / Non-Goals **Goals:** + - Make canonical-to-provider writeback field resolution deterministic and custom-mapping-safe for ADO. - Ensure refine tmp import contract explicitly requires preserving `**ID**` for lookup. - Fail fast with actionable diagnostics when parsed IDs do not map to fetched items. - Ensure ADO comment read/write operations consistently use endpoint-compatible preview API versioning, without changing stable `7.1` usage for standard operations. **Non-Goals:** + - No redesign of adapter registry or template detection. - No change to provider-independent refine output structure beyond explicit ID contract text. ## Decisions 1. Introduce mapper-level write-target resolution helper. + - Add a dedicated method in `AdoFieldMapper` that resolves the preferred ADO field for a canonical field. - Precedence: custom mapping key(s) first, then provider-present mapped fields (from current item/provider_fields), then default/framework fallback. - Rationale: centralizes precedence in mapper and avoids duplicated, order-sensitive logic in adapters. 2. Update `AdoAdapter.update_backlog_item` to use resolved canonical targets. + - Replace ad-hoc reverse mapping and membership checks with mapper-resolved targets for each canonical field. - Rationale: guarantees consistency and removes dependence on Python dict insertion order side effects. 3. Strengthen tmp import contract and mismatch handling. + - Update prompt/export guidance to state `**ID**` is mandatory and must be unchanged. - Add explicit command error when parsed blocks exist but zero IDs match fetched items. - Rationale: prevents silent no-op writeback and improves Copilot workflow reliability. 4. Split ADO API version selection by endpoint class. + - Keep standard work-item and WIQL operations on `api-version=7.1`. - Route comment endpoint reads/writes (`/comments`) to `api-version=7.1-preview.4`. - Rationale: aligns with observed tenant compatibility for comment activities while preserving stable API on core operations. diff --git a/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/proposal.md b/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/proposal.md index e43573d4..51268a01 100644 --- a/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/proposal.md +++ b/openspec/changes/archive/2026-03-03-backlog-core-06-refine-custom-field-writeback/proposal.md @@ -19,9 +19,11 @@ Additionally, `specfact backlog daily --post` and related comment-write paths ca ## Capabilities ### New Capabilities + - `backlog-refine-writeback-mapping`: deterministic write-target selection for mapped provider fields during backlog refine writeback. ### Modified Capabilities + - `backlog-refinement`: refine export/import contract and failure handling for mandatory item IDs. - `format-abstraction`: normalize ADO rich text/HTML backlog fields to markdown-like text for canonical backlog model usage. - `backlog-daily`: ADO comment posting/readback paths use endpoint-appropriate API versions. diff --git a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/TDD_EVIDENCE.md index 60611661..c943aa3c 100644 --- a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/TDD_EVIDENCE.md @@ -35,5 +35,3 @@ These failures confirm current behavior violates the new spec delta: summarize p - Normalizes HTML-based `body_markdown` values to Markdown-friendly text (no `

`, `
` tags or `&` entities). - Normalizes HTML comments before including them under the "Comments (annotations)" section. - New helper `_normalize_markdown_text` (with `@beartype` and `@ensure`) enforces that the returned text does not contain raw HTML tags, satisfying the updated `daily-standup` summarize requirements. - - diff --git a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/design.md b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/design.md index 12612990..d5baefca 100644 --- a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/design.md +++ b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/design.md @@ -37,4 +37,3 @@ At the same time, SpecFact needs to support both interactive, rich terminal sess - HTML-to-Markdown conversion can be lossy if not carefully tuned; we must verify typical ADO HTML patterns (paragraphs, lists, bold, links) produce acceptable Markdown for standup prompts. - Rich or similar libraries must be used in a way that does not leak ANSI control codes into `--summarize-to` files or CI logs; separation between rendered view and underlying text needs to be clear in implementation. - Normalization adds a processing step per item/comment; for very large backlogs this can affect performance, so implementation should be efficient and optionally short-circuit when input is already clean Markdown. - diff --git a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/proposal.md b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/proposal.md index bfc2e9f4..b45cfd0a 100644 --- a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/proposal.md +++ b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/proposal.md @@ -2,14 +2,10 @@ ## Why - - The current `specfact backlog daily --summarize/--summarize-to` output often contains raw HTML fragments and entities from ADO work item comments, mixed with Markdown-formatted text from GitHub and ADO. This makes the standup summary prompt hard to read for humans and noisy for LLMs, even though the underlying data is correct. ## What Changes - - - Normalize backlog comments and descriptions used by `specfact backlog daily --summarize/--summarize-to` so that: - HTML-formatted content is converted into clean Markdown before it is included in the prompt. - Existing Markdown content is preserved as Markdown (no lossy reformatting). @@ -22,15 +18,17 @@ The current `specfact backlog daily --summarize/--summarize-to` output often con - Non-interactive/CI usage (plain Markdown text, no color/control codes). ## Capabilities + ### New Capabilities + - `backlog-daily-markdown-normalization`: Normalize backlog item bodies and comments into Markdown-only text for daily standup summarize prompts, with environment-aware rendering (rich Markdown view in interactive terminals, plain Markdown in CI/non-interactive mode). ### Modified Capabilities + - `daily-standup`: Clarify that the `--summarize/--summarize-to` scenarios must: - Include only Markdown (no raw HTML fragments or entities) in per-item body/comment fields. - Prefer a Markdown-formatted view in interactive terminals while keeping the underlying output prompt-ready for LLMs. - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/specs/backlog-daily-markdown-normalization/spec.md b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/specs/backlog-daily-markdown-normalization/spec.md index f634e8ce..580ebe77 100644 --- a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/specs/backlog-daily-markdown-normalization/spec.md +++ b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/specs/backlog-daily-markdown-normalization/spec.md @@ -5,16 +5,19 @@ The system SHALL normalize all backlog item descriptions and comments included in `specfact backlog daily --summarize` and `--summarize-to` output so that the resulting prompt contains **only Markdown-formatted text** (no raw HTML tags or HTML entities), regardless of whether the underlying provider stores content as HTML (e.g. ADO) or Markdown (e.g. GitHub, Markdown-style ADO comments). #### Scenario: HTML comments from ADO are converted to Markdown + - **WHEN** `specfact backlog daily --summarize` or `--summarize-to` includes work items whose description or comments are stored as HTML (e.g. ADO discussion/comments) - **THEN** the system converts that HTML content into readable Markdown before including it in the summarize prompt - **AND** the resulting output does not contain raw HTML tags or un-decoded HTML entities (e.g. `<div>`, `

`, `
`) #### Scenario: Existing Markdown comments are preserved as Markdown + - **WHEN** `specfact backlog daily --summarize` or `--summarize-to` includes items whose description or comments are already stored as Markdown (e.g. GitHub issues, Markdown-formatted ADO comments) - **THEN** the system preserves the original Markdown semantics when building the summarize prompt (headings, lists, code fences, emphasis) - **AND** the system does not degrade Markdown into a less structured format (e.g. by stripping list markers or collapsing headings) #### Scenario: Mixed HTML and Markdown sources produce a consistent Markdown prompt + - **WHEN** the daily summarize command aggregates items from sources that use different underlying formats (HTML and Markdown) - **THEN** the combined summarize output is a single, consistent Markdown document suitable for LLM consumption - **AND** no raw HTML tags or entities appear anywhere in the per-item body or comments sections @@ -24,18 +27,20 @@ The system SHALL normalize all backlog item descriptions and comments included i The system SHALL render the same normalized Markdown summarize content differently depending on whether it is running in an interactive terminal session or in a non-interactive / CI environment, while always preserving a prompt-ready Markdown representation that tools can consume. #### Scenario: Interactive terminal shows rich Markdown view + - **WHEN** a user runs `specfact backlog daily --summarize` in an interactive terminal that supports rich output (e.g. TTY, not redirected to a file) - **THEN** the CLI MAY render the summarize content using a Markdown-aware terminal view (for example, Rich Markdown rendering) - **AND** the user sees a readable, formatted standup summary prompt (headings, lists, emphasis) instead of raw Markdown or HTML - **AND** the underlying content remains logically the same as the Markdown text used for `--summarize-to` (same sections and text, just rendered differently) #### Scenario: Non-interactive or CI environments emit plain Markdown + - **WHEN** `specfact backlog daily --summarize` or `--summarize-to` is run in a non-interactive environment (e.g. CI/CD job, output redirected to a file or piped) - **THEN** the system emits plain, prompt-ready Markdown text without ANSI color codes or interactive formatting controls - **AND** the output still satisfies the existing summarize requirement to include instruction text, filter context, and per-item data (including normalized body and comments) #### Scenario: Summarize-to file output is always Markdown-only + - **WHEN** the user runs `specfact backlog daily --summarize-to ` - **THEN** the file at `` contains only normalized Markdown content (no raw HTML tags or entities, no terminal control codes) - **AND** the file is suitable for direct copy/paste into IDE slash commands or Copilot prompts without additional cleanup - diff --git a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/specs/daily-standup/spec.md b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/specs/daily-standup/spec.md index b9af1ceb..4cd5a73e 100644 --- a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/specs/daily-standup/spec.md +++ b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/specs/daily-standup/spec.md @@ -5,9 +5,9 @@ The system SHALL support a `--summarize` flag on `specfact backlog daily` that produces a **prompt** (instructions plus applied filters and filtered standup output) suitable for use in an interactive slash command (e.g. `specfact.daily`) or copy-paste to Copilot, so an LLM can generate a meaningful **summary of the daily standup status**. The prompt content for item bodies and comments SHALL be provided as normalized Markdown text only (no raw HTML tags or entities), regardless of how the underlying provider stores or formats those fields. #### Scenario: --summarize outputs prompt with filters and data (Markdown-only content) + - **Given**: Backlog items in the current scope (same as standup: state, iteration/sprint, assignee, limit) and the user runs `specfact backlog daily --summarize` (stdout) or `--summarize-to ` (write to file) - **When**: The command runs with the same filters as the standup view - **Then**: The system outputs (to stdout or to the given path) a prompt that includes: (1) brief instruction that the following data is the current standup view and the LLM should generate a concise standup summary; (2) the applied filter context (adapter, state, sprint, assignee, limit); (3) per-item data including **body (description)** and **comments (annotations)** when available, plus ID, title, status, assignees, last updated, progress, blockers, optional value score, so the LLM can produce a **meaningful** summary - **And**: The per-item body and comment fields in the prompt are formatted as Markdown without raw HTML tags or HTML entities (e.g. no `

`, `
`, `<div>`) - **And**: The output is formatted so it can be pasted into Copilot or used as input to a slash command (e.g. `specfact.daily`) to produce a standup summary - diff --git a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/tasks.md b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/tasks.md index 7be45a93..a8a2f845 100644 --- a/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/tasks.md +++ b/openspec/changes/archive/2026-03-03-backlog-scrum-05-summarize-markdown-output/tasks.md @@ -23,4 +23,3 @@ - [x] 4.1 Update any relevant docs/guides that mention `specfact backlog daily --summarize` / `--summarize-to` to note Markdown-only, normalized behavior - [x] 4.2 Run `openspec validate backlog-scrum-05-summarize-markdown-output --strict` and fix any validation issues - [x] 4.3 Run `/wf-validate-change backlog-scrum-05-summarize-markdown-output` from the specfact-openspec workflow and record results in CHANGE_VALIDATION.md - diff --git a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/CHANGE_VALIDATION.md index 4346fc40..091363c4 100644 --- a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ ## Breaking Changes Detected **None** - All changes are **additive only**: + - New dependency_resolver.py for pip-compile integration - New alias_manager.py for command aliases - New custom_registries.py for multi-registry support @@ -27,10 +28,12 @@ ### New Module: dependency_resolver.py **NEW functions**: + ```python def resolve_dependencies(all_modules: list[ModulePackageMetadata]) -> list[str]: ... def install_module_with_deps(module_id: str, version: str) -> None: ... ``` + - Aggregates and resolves pip_dependencies - Uses pip-compile with fallback to basic resolver - No existing code depends on this (new module) @@ -38,12 +41,14 @@ def install_module_with_deps(module_id: str, version: str) -> None: ... ### New Module: alias_manager.py **NEW functions**: + ```python def create_alias(alias: str, module_id: str, force: bool = False) -> None: ... def list_aliases() -> dict[str, str]: ... def remove_alias(alias: str) -> None: ... def resolve_command(cmd_name: str) -> str: ... ``` + - JSON storage (~/.specfact/registry/aliases.json) - Command resolution with alias lookup - No existing dependencies @@ -51,12 +56,14 @@ def resolve_command(cmd_name: str) -> str: ... ### New Module: custom_registries.py **NEW functions**: + ```python def add_registry(id: str, url: str, priority: int | None, trust: str) -> None: ... def list_registries() -> list[dict]: ... def remove_registry(id: str) -> None: ... def fetch_all_indexes() -> list[dict]: ... ``` + - YAML storage (~/.specfact/config/registries.yaml) - Multi-registry management - No existing dependencies @@ -64,6 +71,7 @@ def fetch_all_indexes() -> list[dict]: ... ### Extended Module: module_installer.py **EXTENDED**: Adds dependency resolution pre-flight check + - New parameter: `skip_deps: bool = False` - Calls resolve_dependencies() before install - Backward compatible (resolution optional via flag) @@ -72,6 +80,7 @@ def fetch_all_indexes() -> list[dict]: ... ### Extended Module: module commands (src/commands.py) **NEW subcommands**: + ```python @app.command() def alias(...): ... # create/list/remove aliases @@ -85,22 +94,26 @@ def list_registries(...): ... @app.command() def remove_registry(...): ... ``` + - All new commands, no modifications to existing ### Extended Module: marketplace_client.py **EXTENDED**: Supports multi-registry queries + - New parameter on fetch_registry_index(): `registry_id: str | None = None` - Backward compatible (defaults to official registry) ## Dependencies Affected ### Files Extended (Additive Only) + - **module_installer.py**: Dependency resolution integration (optional) - **module commands**: New subcommands added - **marketplace_client.py**: Multi-registry support added ### New External Dependencies + - **pip-tools** (optional): For pip-compile functionality - Fallback to basic pip resolver if unavailable - Not a hard dependency @@ -108,17 +121,20 @@ def remove_registry(...): ... ## Impact Assessment ### Code Impact + - **Scope**: Marketplace enhancement (dependency resolution, aliases, custom registries) - **Type**: Additive (new modules + optional extensions) - **Backward Compatibility**: βœ… Full (all features are opt-in) - **Migration Required**: ❌ None ### Test Impact + - **Existing Tests**: βœ… Should pass without modification - **New Tests Required**: βœ… Covered in tasks.md (TDD-first) - **Coverage**: Expect >80% for new functionality ### Documentation Impact + - **New Guides**: - `docs/guides/publishing-modules.md` βœ… - `docs/guides/custom-registries.md` βœ… @@ -127,6 +143,7 @@ def remove_registry(...): ... - **Navigation**: `docs/_layouts/default.html` βœ… ### Release Impact + - **Version Bump**: **Minor** (new features, backward compatible) - **Semver**: Appropriate - **Changelog**: Update required βœ… @@ -134,12 +151,14 @@ def remove_registry(...): ... ## Format Validation ### proposal.md Format: βœ… PASS + - βœ… Title, Why, What Changes, Capabilities, Impact sections - βœ… Capabilities: 4 new, 2 modified (correctly identified) - βœ… External dependency documented (pip-tools optional) - βœ… Backward compatibility stated ### tasks.md Format: βœ… PASS + - βœ… TDD/SDD order enforced - βœ… Git workflow: Branch first (Task 1), PR last (Task 10) - βœ… Task structure: `## N.` with `- [ ] N.M` format @@ -148,18 +167,21 @@ def remove_registry(...): ... - βœ… Version/changelog: Task 9 ### specs Format: βœ… PASS + - βœ… 4 new specs: dependency-resolution, module-aliasing, custom-registries, module-publishing - βœ… 2 delta specs: module-installation, module-lifecycle-management - βœ… WHEN/THEN format (24+ scenarios total) - βœ… Clear requirement statements with SHALL/MUST ### design.md Format: βœ… PASS + - βœ… Context, Goals/Non-Goals, Decisions, Risks/Trade-offs sections - βœ… 6 key decisions with rationale - βœ… Migration plan included - βœ… Open questions addressed ### Config.yaml Compliance: βœ… PASS + - βœ… TDD-first enforced - βœ… Contract requirements (@icontract/@beartype) - βœ… Documentation requirements @@ -176,15 +198,18 @@ def remove_registry(...): ... ## Dependencies and Prerequisites ### Required Changes + - βœ… **marketplace-01** (Central Module Registry): Provides base infrastructure (in progress) - ℹ️ **arch-06** (Enhanced Manifest Security): Used for signing in publishing pipeline (optional, graceful fallback) ### Recommendation + This change can proceed after marketplace-01 is implemented. All features are additive extensions to the marketplace infrastructure. ## Risk Assessment ### Technical Risks + 1. **pip-tools dependency** - Mitigated by optional dependency with fallback to basic resolver 2. **Dependency conflicts too restrictive** - Mitigated by --skip-deps and --force flags 3. **Alias collisions** - Mitigated by built-in shadowing warnings @@ -192,6 +217,7 @@ This change can proceed after marketplace-01 is implemented. All features are ad 5. **Publishing pipeline complexity** - Mitigated by manual fallback script ### Process Risks + 1. **Complex feature interaction** - Mitigated by modular design, independent features 2. **Documentation lag** - Mitigated by comprehensive doc tasks @@ -216,6 +242,7 @@ This change can proceed after marketplace-01 is implemented. All features are ad **Change marketplace-02-advanced-marketplace-features is SAFE TO IMPLEMENT** βœ… ### Key Findings + 1. βœ… Zero breaking changes - all additive enhancements 2. βœ… Full backward compatibility with marketplace-01 3. βœ… Comprehensive task plan with TDD-first ordering @@ -225,11 +252,13 @@ This change can proceed after marketplace-01 is implemented. All features are ad 7. βœ… OpenSpec validation passed ### Recommendation + **PROCEED WITH IMPLEMENTATION** after marketplace-01 is complete. Implementation order: dependency-resolution β†’ alias system β†’ custom registries β†’ namespace enforcement β†’ publishing automation ### Next Steps + 1. Wait for marketplace-01 completion 2. Create feature branch (Task 1) 3. Begin TDD cycle: dependency resolution tests (Task 2.1) diff --git a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/TDD_EVIDENCE.md index 4de57f0c..345a86df 100644 --- a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/TDD_EVIDENCE.md @@ -19,8 +19,7 @@ - **Command**: `hatch test tests/unit/registry/test_dependency_resolver.py tests/unit/registry/test_module_installer.py -v` - **Result**: 28 passed. -- **Summary**: Extended `install_module()` with `skip_deps` and `force`; added dependency resolution after manifest parse (discover_all_modules + new module, then resolve_dependencies; on DependencyConflictError re-raise unless force). Added `--skip-deps` and `--force` to module registry install command. Tests patched to mock _pip_tools_available where pip-tools path is required. - +- **Summary**: Extended `install_module()` with `skip_deps` and `force`; added dependency resolution after manifest parse (discover_all_modules + new module, then resolve_dependencies; on DependencyConflictError re-raise unless force). Added `--skip-deps` and `--force` to module registry install command. Tests patched to mock_pip_tools_available where pip-tools path is required. ## 3. Alias system (alias_manager + module_registry + CLI resolution) @@ -29,7 +28,6 @@ - **Tests**: `hatch test tests/unit/registry/test_alias_manager.py -v` β€” 9 passed. - **Summary**: Added `src/specfact_cli/registry/alias_manager.py` (get_aliases_path, create_alias, list_aliases, remove_alias, resolve_command; JSON under ~/.specfact/registry/aliases.json; shadow built-in check with --force). Added alias subcommand group to module_registry (alias create/list/remove). Integrated resolve_command into cli.py lazy delegate so aliased command names resolve to module command before get_typer. - ## 4. Custom registries (custom_registries + marketplace_client + commands) ### Post-implementation (passing) @@ -37,14 +35,12 @@ - **Tests**: `hatch test tests/unit/registry/test_custom_registries.py -v` β€” 8 passed. - **Summary**: Added `src/specfact_cli/registry/custom_registries.py` (get_registries_config_path, add_registry, list_registries, remove_registry, fetch_all_indexes; YAML at ~/.specfact/config/registries.yaml; official registry always first; trust always/prompt/never). Extended `marketplace_client.fetch_registry_index(index_url=None, registry_id=None)` to resolve registry by id from custom_registries. Added add-registry, list-registries, remove-registry commands to module_registry. Search command uses fetch_all_indexes() and shows Registry column. - ## 5. Namespace enforcement (module_installer) ### Post-implementation (passing) - **Tests**: New tests in test_module_installer.py: test_install_module_rejects_invalid_namespace_format, test_install_module_accepts_valid_namespace_format, test_install_module_namespace_collision_raises. All 26 module_installer tests pass. -- **Summary**: Added _validate_marketplace_namespace_format(module_id) (regex ^[a-z][a-z0-9-]*/[a-z][a-z0-9-]+$), _check_namespace_collision(module_id, final_path, reinstall) using .specfact-registry-id; call both in install_module(); write REGISTRY_ID_FILE after successful install. Marketplace modules must use namespace/name; collision raises ValueError with message suggesting alias or uninstall. - +- **Summary**: Added _validate_marketplace_namespace_format(module_id) (regex ^[a-z][a-z0-9-]*/[a-z][a-z0-9-]+$),_check_namespace_collision(module_id, final_path, reinstall) using .specfact-registry-id; call both in install_module(); write REGISTRY_ID_FILE after successful install. Marketplace modules must use namespace/name; collision raises ValueError with message suggesting alias or uninstall. ## 6. Module publishing automation (publish-module.py + workflow) @@ -67,7 +63,6 @@ - Index update command: `python scripts/update-registry-index.py --index-path /registry/index.json --entry-fragment /registry-entry.yaml --changed-flag /changed.txt` - Result: `CHANGED=true`, branch `auto/publish-nold-ai-backlog-test`, commit `chore(registry): publish nold-ai/backlog v0.1.0`, index contains `nold-ai/backlog@0.1.0`. - ### Re-signing module_registry for full tests To run `test_cli_module_help_exits_zero` and `test_module_discovery_registers_commands_from_manifests` without skip/verify patch (e.g. after changing module_registry), set: diff --git a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/design.md b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/design.md index 54196009..4cfc4b78 100644 --- a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/design.md +++ b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/design.md @@ -3,18 +3,21 @@ ## Context marketplace-01 provides basic module installation but doesn't handle: + - Dependency conflicts between modules (e.g., module A needs requests>=2.28, module B needs requests<2.27) - Namespace collisions (multiple "backlog" modules from different publishers) - Custom enterprise registries - Command aliases for user convenience **Current State:** + - Single registry (official NOLD AI) - No dependency resolution (modules install their pip_dependencies independently) - Flat command names (no namespace support) - No custom registry support **Constraints:** + - Must detect conflicts before installation (don't break existing installs) - Must remain offline-first (dependency resolution optional) - Must support multiple registries with trust levels @@ -23,6 +26,7 @@ marketplace-01 provides basic module installation but doesn't handle: ## Goals / Non-Goals **Goals:** + - Resolve pip dependency conflicts across all modules - Support module aliases (user shortcuts) - Enable custom registries for enterprises @@ -30,6 +34,7 @@ marketplace-01 provides basic module installation but doesn't handle: - Provide module publishing automation **Non-Goals:** + - Module sandboxing or permissions (future) - Per-module virtualenvs (too heavy for MVP) - Automatic dependency updates (explicit only) @@ -40,6 +45,7 @@ marketplace-01 provides basic module installation but doesn't handle: ### Decision 1: Dependency Resolution Strategy **Options:** + - **A**: pip-tools (pip-compile) integration - **B**: Poetry resolver integration - **C**: Custom constraint solver @@ -47,18 +53,21 @@ marketplace-01 provides basic module installation but doesn't handle: **Choice: A (pip-tools with fallback)** **Rationale:** + - pip-compile is standard Python tooling - Handles PEP 440 specifiers correctly - Fallback to basic pip resolver if pip-tools unavailable - Familiar workflow for Python developers **Trade-offs:** + - External dependency (mitigated by optional fallback) - Conflict detection adds install time (acceptable for safety) ### Decision 2: Alias Storage and Resolution **Options:** + - **A**: JSON file (~/.specfact/registry/aliases.json) - **B**: YAML in config - **C**: Database (SQLite) @@ -66,12 +75,14 @@ marketplace-01 provides basic module installation but doesn't handle: **Choice: A (JSON file)** **Rationale:** + - Simple, human-readable - Easy to edit manually - Fast lookups (small dataset) - Consistent with existing registry storage **Format:** + ```json { "backlog": "acme-corp/backlog-pro", @@ -82,6 +93,7 @@ marketplace-01 provides basic module installation but doesn't handle: ### Decision 3: Custom Registry Configuration **Options:** + - **A**: YAML config file with registry list - **B**: Command-based registration (like git remote) - **C**: Registry discovery via DNS @@ -89,11 +101,13 @@ marketplace-01 provides basic module installation but doesn't handle: **Choice: Hybrid (A + B)** **Rationale:** + - YAML for persistence (~/.specfact/config/registries.yaml) - Commands for user-friendly management - Priority ordering for conflict resolution **Schema:** + ```yaml registries: - id: official @@ -109,6 +123,7 @@ registries: ### Decision 4: Namespace Enforcement **Options:** + - **A**: Enforce namespace/name format for all modules - **B**: Enforce for marketplace only, allow flat for custom - **C**: Advisory only (warn but allow) @@ -116,11 +131,13 @@ registries: **Choice: B (enforce marketplace, allow custom)** **Rationale:** + - Marketplace modules must use namespace (prevent collisions) - Custom modules can use flat names (user responsibility) - Backward compatible with built-in modules **Validation:** + - marketplace modules: MUST match `^[a-z][a-z0-9-]+/[a-z][a-z0-9-]+$` - custom modules: MAY use flat names - Collision detection: warn if flat name conflicts with namespaced module @@ -128,6 +145,7 @@ registries: ### Decision 5: Dependency Resolution Timing **Options:** + - **A**: Resolve before install (dry-run first) - **B**: Resolve during install (fail midway if conflict) - **C**: Resolve on demand (user triggers) @@ -135,11 +153,13 @@ registries: **Choice: A (resolve before install)** **Rationale:** + - Fail fast (don't start install if conflict) - User can abort without partial state - Clear error messages before changes **Workflow:** + 1. User runs `specfact module install X` 2. Download module metadata (not tarball yet) 3. Simulate: all_modules = current + X @@ -150,6 +170,7 @@ registries: ### Decision 6: Publishing Pipeline **Options:** + - **A**: Manual (user runs script, commits to registry repo) - **B**: GitHub Actions (automated on release tag) - **C**: Dedicated registry service @@ -157,6 +178,7 @@ registries: **Choice: B (GitHub Actions automation)** **Rationale:** + - Automated on git tag (e.g., `backlog-v0.29.0`) - Validation, packaging, signing all in CI - Index.json updated automatically @@ -165,49 +187,61 @@ registries: ## Risks / Trade-offs ### Risk 1: Dependency conflicts too restrictive (false positives) + **Mitigation**: Allow --force flag to bypass, log warning, provide clear conflict messages ### Risk 2: pip-compile unavailable + **Mitigation**: Fallback to basic pip resolver (less sophisticated but functional) ### Risk 3: Custom registry security (malicious modules) + **Mitigation**: Trust levels (always/prompt/never), checksum verification, signature verification ### Risk 4: Alias collisions (user creates alias that shadows built-in) + **Mitigation**: Warn when alias shadows built-in, require confirmation ### Risk 5: Publishing pipeline complexity + **Trade-off**: Manual fallback available (scripts/publish-module.py can run locally) ## Migration Plan ### Phase 1: Dependency Resolution + 1. Add dependency_resolver.py with pip-compile integration 2. Extend install command to resolve dependencies 3. Add --skip-deps flag for bypass ### Phase 2: Alias System + 1. Add alias_manager.py 2. Extend module commands: alias, alias list, alias remove 3. Update command resolution to check aliases first ### Phase 3: Custom Registries + 1. Add custom_registries.py 2. Extend module commands: add-registry, list-registries, remove-registry 3. Update fetch_registry_index() to support multiple registries ### Phase 4: Namespace Enforcement + 1. Add namespace validation to module_installer.py 2. Enforce for marketplace modules 3. Add collision detection ### Phase 5: Publishing Pipeline + 1. Create scripts/publish-module.py 2. Add .github/workflows/publish-modules.yml 3. Document publishing process ### Rollback + If issues arise: + 1. Disable dependency resolver (--skip-deps becomes default) 2. Remove aliases.json (revert to direct resolution) 3. Revert registries.yaml (single registry) @@ -215,13 +249,16 @@ If issues arise: ## Open Questions **Q1: Should dependency resolution be opt-in or default?** + - **Recommendation**: Default (with --skip-deps for bypass) - Safer to resolve by default **Q2: How to handle conflicting aliases from team members?** + - **Recommendation**: Aliases are local (~/.specfact/), not version controlled - Teams can share recommended aliases in docs **Q3: Should custom registries support authentication?** + - **Recommendation**: Yes (future enhancement) - support API keys in registries.yaml - MVP: Public registries only diff --git a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/custom-registries/spec.md b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/custom-registries/spec.md index 43169cbe..1bcf69c7 100644 --- a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/custom-registries/spec.md +++ b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/custom-registries/spec.md @@ -11,17 +11,20 @@ Defines support for multiple registry sources with trust levels and priority ord The system SHALL manage multiple registries with configurable priority and trust levels. #### Scenario: Add custom registry + - **WHEN** user runs `specfact module add-registry https://registry.company.com/index.json --id enterprise` - **THEN** system SHALL add registry to ~/.specfact/config/registries.yaml - **AND** SHALL assign next priority number - **AND** SHALL set trust level to "prompt" by default #### Scenario: List registries + - **WHEN** user runs `specfact module list-registries` - **THEN** system SHALL display all configured registries - **AND** SHALL show: id, url, priority, trust level #### Scenario: Remove registry + - **WHEN** user runs `specfact module remove-registry enterprise` - **THEN** system SHALL remove registry from config - **AND** SHALL NOT affect modules already installed from that registry @@ -31,6 +34,7 @@ The system SHALL manage multiple registries with configurable priority and trust The system SHALL search across all configured registries in priority order. #### Scenario: Search returns results from multiple registries + - **WHEN** user runs `specfact module search backlog` - **THEN** system SHALL fetch indexes from all registries - **AND** SHALL aggregate results @@ -41,10 +45,12 @@ The system SHALL search across all configured registries in priority order. The system SHALL enforce trust levels during module installation. #### Scenario: Install from trusted registry (always) + - **WHEN** installing module from trust=always registry - **THEN** system SHALL proceed without prompt #### Scenario: Install from untrusted registry (prompt) + - **WHEN** installing module from trust=prompt registry - **THEN** system SHALL display warning and registry info - **AND** SHALL prompt user for confirmation diff --git a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/dependency-resolution/spec.md b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/dependency-resolution/spec.md index 6821082c..de2408df 100644 --- a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/dependency-resolution/spec.md +++ b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/dependency-resolution/spec.md @@ -11,12 +11,14 @@ Defines pip-compile style dependency resolution across all installed modules wit The system SHALL aggregate pip_dependencies from all installed modules and resolve constraints using pip-compile or fallback resolver. #### Scenario: Dependencies resolved without conflicts + - **WHEN** module installation triggers dependency resolution - **THEN** system SHALL collect pip_dependencies from all modules - **AND** SHALL resolve constraints using pip-compile - **AND** SHALL return list of resolved package versions #### Scenario: Dependency conflict detected + - **WHEN** new module introduces conflicting pip dependency - **THEN** system SHALL detect conflict before installation - **AND** SHALL display error with conflicting packages and versions @@ -24,6 +26,7 @@ The system SHALL aggregate pip_dependencies from all installed modules and resol - **AND** SHALL NOT proceed with installation #### Scenario: Fallback to basic pip resolver + - **WHEN** pip-tools is not available - **THEN** system SHALL log warning "pip-tools not found, using basic resolver" - **AND** SHALL attempt resolution with pip's built-in resolver @@ -34,6 +37,7 @@ The system SHALL aggregate pip_dependencies from all installed modules and resol The system SHALL extend install command to resolve dependencies as pre-flight check. #### Scenario: Install with dependency resolution + - **WHEN** user runs `specfact module install X` - **THEN** system SHALL download module metadata - **AND** SHALL simulate: all_modules = current + X @@ -41,6 +45,7 @@ The system SHALL extend install command to resolve dependencies as pre-flight ch - **AND** SHALL proceed only if resolution succeeds #### Scenario: Skip dependency resolution with flag + - **WHEN** user runs `specfact module install X --skip-deps` - **THEN** system SHALL skip dependency resolution - **AND** SHALL install module and its pip_dependencies independently @@ -51,6 +56,7 @@ The system SHALL extend install command to resolve dependencies as pre-flight ch The system SHALL provide actionable error messages when dependency conflicts occur. #### Scenario: Conflict error message format + - **WHEN** dependency conflict is detected - **THEN** error SHALL include: conflicting packages, required versions, affected modules - **AND** SHALL suggest: uninstall conflicting module, use --force, or skip conflicting module diff --git a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-aliasing/spec.md b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-aliasing/spec.md index 8aaf458b..a673fc4e 100644 --- a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-aliasing/spec.md +++ b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-aliasing/spec.md @@ -11,17 +11,20 @@ Defines user-configurable aliases mapping command names to namespaced module IDs The system SHALL provide alias commands to create, list, and remove command-to-module mappings. #### Scenario: Create alias + - **WHEN** user runs `specfact module alias backlog acme-corp/backlog-pro` - **THEN** system SHALL store mapping in ~/.specfact/registry/aliases.json - **AND** SHALL display success message - **AND** SHALL resolve "backlog" command to "acme-corp/backlog-pro" module #### Scenario: List aliases + - **WHEN** user runs `specfact module alias list` - **THEN** system SHALL display all configured aliases - **AND** SHALL show format: "alias -> namespaced-id" #### Scenario: Remove alias + - **WHEN** user runs `specfact module alias remove backlog` - **THEN** system SHALL delete alias from aliases.json - **AND** SHALL revert to default resolution (specfact/backlog) @@ -31,11 +34,13 @@ The system SHALL provide alias commands to create, list, and remove command-to-m The system SHALL resolve command names through alias system before falling back to defaults. #### Scenario: Aliased command resolved + - **WHEN** alias "backlog" maps to "acme-corp/backlog-pro" - **AND** user runs backlog command - **THEN** system SHALL load acme-corp/backlog-pro module #### Scenario: Alias warns when shadowing built-in + - **WHEN** user creates alias for built-in module name - **THEN** system SHALL warn "Alias will shadow built-in module" - **AND** SHALL require --force flag to proceed diff --git a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-installation/spec.md b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-installation/spec.md index 7f4968bf..f4b8d905 100644 --- a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-installation/spec.md +++ b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-installation/spec.md @@ -7,12 +7,14 @@ The system SHALL extend install command to resolve pip dependencies across all modules before installation. #### Scenario: Install with dependency resolution + - **WHEN** user installs module with pip_dependencies - **THEN** system SHALL resolve dependencies with existing modules - **AND** SHALL fail if conflicts detected - **AND** SHALL install resolved dependencies if resolution succeeds #### Scenario: Force install bypasses dependency resolution + - **WHEN** user runs install with --force flag - **THEN** system SHALL skip dependency resolution - **AND** SHALL log warning about potential conflicts diff --git a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-lifecycle-management/spec.md b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-lifecycle-management/spec.md index f984a280..c4822905 100644 --- a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-lifecycle-management/spec.md +++ b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-lifecycle-management/spec.md @@ -7,12 +7,14 @@ The system SHALL validate namespace format during module registration for marketplace-sourced modules. #### Scenario: Marketplace module must use namespace format + - **WHEN** module from marketplace is registered - **THEN** id SHALL match format "namespace/name" - **AND** namespace SHALL be alphanumeric with hyphens - **AND** name SHALL be alphanumeric with hyphens #### Scenario: Namespace collision detected + - **WHEN** registering module with id that conflicts with existing module - **THEN** system SHALL log error "Module namespace collision: {id}" - **AND** SHALL prevent registration diff --git a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-publishing/spec.md b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-publishing/spec.md index cfa06b02..374e7a23 100644 --- a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-publishing/spec.md +++ b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/specs/module-publishing/spec.md @@ -11,12 +11,14 @@ Defines automated pipeline for validating, packaging, and publishing modules to The system SHALL provide scripts/publish-module.py that validates module before publishing. #### Scenario: Validate module structure + - **WHEN** publish script runs on module directory - **THEN** it SHALL verify module-package.yaml exists and is valid - **AND** SHALL verify namespace format for marketplace modules - **AND** SHALL verify all required files present #### Scenario: Create module tarball + - **WHEN** validation passes - **THEN** script SHALL create tarball with format: {module-name}-{version}.tar.gz - **AND** SHALL include only necessary files (exclude tests, .git, etc.) @@ -26,6 +28,7 @@ The system SHALL provide scripts/publish-module.py that validates module before The system SHALL provide .github/workflows/publish-modules.yml that automates publishing. #### Scenario: Publish on release tag + - **WHEN** git tag matches pattern `{module}-v{version}` is pushed - **THEN** workflow SHALL run publish-module.py for that module - **AND** SHALL generate checksum diff --git a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/tasks.md b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/tasks.md index 8a8ab48d..f15e2b48 100644 --- a/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/tasks.md +++ b/openspec/changes/archive/2026-03-03-marketplace-02-advanced-marketplace-features/tasks.md @@ -3,6 +3,7 @@ ## TDD / SDD Order (Enforced) Per config.yaml, tests MUST come before implementation for any behavior-changing task. Order: + 1. Spec deltas (already created) 2. Tests from spec scenarios (expect failure) 3. Code implementation (until tests pass and behavior satisfies spec) @@ -74,7 +75,7 @@ Do not implement production code until tests exist and have been run (expecting ## 4. Implement custom registries (TDD) -- [x] 4.1 +- [x] 4.1 - [x] 4.1.1 Create tests/unit/registry/test_custom_registries.py - [x] 4.1.2 Test add_registry() stores config - [x] 4.1.3 Test list_registries() returns all configured @@ -84,7 +85,7 @@ Do not implement production code until tests exist and have been run (expecting - [x] 4.1.7 Mock HTTP requests for multiple registries - [x] 4.1.8 Run tests (expect failures) -- [x] 4.2 +- [x] 4.2 - [x] 4.2.1 Create src/specfact_cli/registry/custom_registries.py - [x] 4.2.2 Implement YAML config storage (~/.specfact/config/registries.yaml) - [x] 4.2.3 Implement add_registry() with priority and trust @@ -95,13 +96,13 @@ Do not implement production code until tests exist and have been run (expecting - [x] 4.2.8 Add @beartype decorators - [x] 4.2.9 Verify tests pass -- [x] 4.3 +- [x] 4.3 - [x] 4.3.1 Modify marketplace_client.py to use custom_registries - [x] 4.3.2 Update fetch_registry_index() to support registry parameter - [x] 4.3.3 Implement search across all registries - [x] 4.3.4 Verify integration with install/search commands -- [x] 4.4 +- [x] 4.4 - [x] 4.4.1 Add add-registry command - [x] 4.4.2 Add list-registries command - [x] 4.4.3 Add remove-registry command @@ -109,14 +110,14 @@ Do not implement production code until tests exist and have been run (expecting ## 5. Implement namespace enforcement (TDD) -- [x] 5.1 +- [x] 5.1 - [x] 5.1.1 Add tests to test_module_lifecycle_management.py - [x] 5.1.2 Test namespace format validation for marketplace modules - [x] 5.1.3 Test namespace collision detection - [x] 5.1.4 Test custom modules allowed with flat names - [x] 5.1.5 Run tests (expect failures) -- [x] 5.2 +- [x] 5.2 - [x] 5.2.1 Add namespace validation to module_installer.py - [x] 5.2.2 Enforce namespace/name format for marketplace modules - [x] 5.2.3 Add collision detection with clear error messages @@ -254,7 +255,7 @@ Do not implement production code until tests exist and have been run (expecting - [x] 11.2 Create release PR (dev β†’ main) - [x] 11.2.1 Fill .github/pull_request_template.md for v0.38.0 release - [x] 11.2.2 `gh pr create --base main --head dev --title "Release v0.38.0: Advanced marketplace features (dev β†’ main)" --body-file ` - - [x] 11.2.3 PR #319 created: https://github.com/nold-ai/specfact-cli/pull/319 + - [x] 11.2.3 PR #319 created: - [x] 11.3 Merge release PR to main (when ready) - [ ] 11.3.1 Merge PR #319 to main diff --git a/openspec/changes/archive/2026-03-03-module-migration-01-categorize-and-group/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-03-module-migration-01-categorize-and-group/TDD_EVIDENCE.md index b487661d..233287d4 100644 --- a/openspec/changes/archive/2026-03-03-module-migration-01-categorize-and-group/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-03-module-migration-01-categorize-and-group/TDD_EVIDENCE.md @@ -41,13 +41,16 @@ Implementation: `src/specfact_cli/modules/init/src/first_run_selection.py` and ` ### Phase 3 follow-up (5.2.3, 5.2.7) **Interactive first-run UI (5.2.3):** + - `_interactive_first_run_bundle_selection()` in commands.py: welcome banner (Panel), questionary.select for profile or "Choose bundles manually", questionary.checkbox for manual bundle selection. When first run and interactive and no --profile/--install, init() calls it and installs selected bundles or shows tip if none. - `BUNDLE_DISPLAY` and `PROFILE_DISPLAY_ORDER` in first_run_selection.py for UI labels. **Graceful degradation (5.2.7):** + - In `install_bundles_for_init`, each `install_bundled_module` call wrapped in try/except; on exception log warning "Dependency resolver may be unavailable" and re-raise so errors are surfaced. **Additional tests:** + - `test_init_first_run_interactive_with_selection_calls_installer`: first run + interactive, mock selection returns ["specfact-codebase"], assert install called. - `test_init_first_run_interactive_no_selection_shows_tip`: first run + interactive, mock selection returns [], assert no install and "Tip" / "module install" in output. diff --git a/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/GAP_ANALYSIS.md b/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/GAP_ANALYSIS.md index 9d97b990..05cd04b0 100644 --- a/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/GAP_ANALYSIS.md +++ b/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/GAP_ANALYSIS.md @@ -43,6 +43,7 @@ Migration-03 will delete `src/specfact_cli/modules/{project,plan,backlog,...}/` directories. If any of the 85 uncategorized imports resolve to code that lives in those or adjacent directories (e.g., `specfact_cli.sync.*`, `specfact_cli.analyzers.*`, `specfact_cli.generators.*`, `specfact_cli.backlog.*`), the bundle code in specfact-cli-modules will raise `ImportError` at runtime after migration-03. The suggested initial categorization table labels these as likely MIGRATE candidates: + - `analyzers.*` β†’ codebase/project - `sync.*` β†’ codebase/project - `backlog.*` β†’ backlog @@ -54,6 +55,7 @@ If these are MIGRATE but have not yet been moved into specfact-cli-modules when ### Required action Section 19.1.1–19.1.4 (full import categorization) **must complete before gate 17.8** is run and accepted. Migration-03 may not begin implementation until all MIGRATE-tier items are either: + - (a) migrated to specfact-cli-modules (preferred), or - (b) confirmed as CORE with documented rationale (stays in specfact-cli) @@ -70,9 +72,11 @@ Section 19.1.1–19.1.4 (full import categorization) **must complete before gate ### Finding Migration-02 places `__getattr__` re-export shims at: + ``` src/specfact_cli/modules//src//__init__.py ``` + These shims delegate `from specfact_cli.modules.validate import X` to `from specfact_codebase.validate import X` and emit a `DeprecationWarning`. Migration-02's deprecation notice states: "removal in next major version." @@ -88,6 +92,7 @@ Additionally: migration-02 says "one major version cycle" for shim removal, but ### Required action Migration-03's proposal must be updated to: + 1. Explicitly state that the `specfact_cli.modules.*` Python import shims are removed as part of this change 2. Add a "Migration path for import consumers" section to its documentation update 3. Justify what "one major version cycle" means in this context (version series reference) @@ -110,6 +115,7 @@ Migration-03's proposal must be updated to: | migration-03 | Wave 4 (after 02) | "Backward-compat flat command shims registered by `bootstrap.py` in module-migration-01" | Both changes claim to remove the flat command shim layer. CHANGE_ORDER.md places migration-04 **before** migration-03 in the wave order. If migration-04 is implemented first and removes `FLAT_TO_GROUP` and `_make_shim_loader()` from `module_packages.py`, migration-03's flat shim removal claim will either: + - Fail (already deleted) - Silently do nothing (if the code is gone) - Create confusion about what migration-03 is actually removing from `bootstrap.py` @@ -136,11 +142,13 @@ The distinction between `module_packages.py` (migration-04 target) and `bootstra ### Finding Migration-02's "Handoff to migration-03 and migration-04" section defines migration-02 as complete when: + 1. specfact-cli PR merged to dev 2. specfact-cli-modules five bundles merged 3. Migration-complete gate passes (17.8) But tasks.md sections 18–23 (β‰ˆ50 sub-tasks) remain `[ ]` pending and live inside migration-02's task file. This creates two bad outcomes: + - **Option A**: Migration-02 stays open indefinitely while sections 18–23 are worked through β†’ blocks the "non-reversible gate" from being accepted β†’ blocks migration-03 - **Option B**: Migration-02 is closed at 17.8 with 18–23 silently abandoned β†’ specfact-cli-modules permanently lacks quality parity @@ -161,6 +169,7 @@ Create a dedicated follow-up change `module-migration-05-modules-repo-quality` t ### Finding After migration-03 closes, specfact-cli-modules is the canonical home for 17 modules. A developer fixing a bug in `specfact_backlog` after migration-03 will find: + - No `hatch run contract-test` (`@icontract` / CrossHair validation) - No coverage threshold enforcement - No `hatch run smart-test` (incremental test runner) @@ -189,6 +198,7 @@ At minimum, sections 21 (PR orchestrator workflow) and 22 (root config files β€” ### Finding `validate-modules-repo-sync.py --gate` verifies: + - All 74 files present in specfact-cli-modules: βœ… - Content differences accepted with `SPECFACT_MIGRATION_CONTENT_VERIFIED=1`: βœ… @@ -197,9 +207,11 @@ There is no automated step that exercises bundle code through the **installed bu ### Required action Add to gate checklist (task 17.8) a behavioral smoke test step: + ```bash hatch test -- tests/unit/bundles/ tests/integration/test_bundle_install.py -v ``` + This verifies the bundle lifecycle (install, official-tier verify, dep resolution) and bundle layout, which exercises the canonical bundle paths rather than shims. **Updated in tasks.md section 17.8.** @@ -233,6 +245,7 @@ Assign residual decoupling cleanup to a dedicated change: `module-migration-06-c ### Finding All five bundles are currently version-locked to core's minor version (e.g., 0.29.0). After migration-03 enables independent development in specfact-cli-modules, bundles and core will have independent release cycles. No policy exists for: + - Minimum/maximum acceptable divergence between a bundle version and core's `core_compatibility` range - What constitutes a patch vs minor vs major bump for a bundle (e.g., "adding a command = minor, fixing a bug = patch, changing a public API = major") - How a bundle consumer pins versions against `core_compatibility` diff --git a/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/proposal.md b/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/proposal.md index 9d9daf52..d5093277 100644 --- a/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/proposal.md +++ b/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/proposal.md @@ -121,9 +121,11 @@ See **`openspec/changes/module-migration-02-bundle-extraction/MIGRATION_GATE.md` Before marking migration-02 complete or merging the change: 1. **Run the gate script** from the specfact-cli worktree (with `SPECFACT_MODULES_REPO` pointing at the specfact-cli-modules clone, on the branch that will be merged): + ```bash SPECFACT_MODULES_REPO=/path/to/specfact-cli-modules python scripts/validate-modules-repo-sync.py --gate ``` + 2. **Gate criteria:** - All 17 migrated modules have every source file **present** in specfact-cli-modules at the correct bundle path (script fails if any file is missing). - **Content:** If any file’s content differs between worktree and modules repo, the script exits non-zero and lists differing files. Resolve by either (a) migrating missing logic into specfact-cli-modules and re-running, or (b) confirming that differences are only import/namespace and re-running with `SPECFACT_MIGRATION_CONTENT_VERIFIED=1`. diff --git a/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/tasks.md b/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/tasks.md index a712a47f..1abe4712 100644 --- a/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/tasks.md +++ b/openspec/changes/archive/2026-03-03-module-migration-02-bundle-extraction/tasks.md @@ -512,14 +512,18 @@ Migration-02 is not complete until the **specfact-cli-modules** repository conta - [x] 17.8.1 Confirm 17.8.0 (import categorization) is complete before proceeding - [x] 17.8.2 **Behavioral smoke test** β€” Run from specfact-cli worktree with `SPECFACT_MODULES_REPO` set: + ```bash hatch test -- tests/unit/bundles/ tests/integration/test_bundle_install.py -v ``` + Confirm: bundle layout tests pass, install lifecycle tests (official-tier verify, dependency resolution) pass via installed bundle paths β€” not shims. Record result. - [x] 17.8.3 **Presence gate** β€” Run: + ```bash SPECFACT_MODULES_REPO=/path/to/specfact-cli-modules python scripts/validate-modules-repo-sync.py --gate ``` + - If any file is missing in the modules repo, fix by migrating that content to specfact-cli-modules and re-run. - If content differs (e.g. import/namespace only), either migrate any missing logic to specfact-cli-modules, or after verification re-run with `SPECFACT_MIGRATION_CONTENT_VERIFIED=1`. Do not close the change until the gate passes. See proposal "Non-reversible gate" and `MIGRATION_GATE.md`. - [x] 17.8.4 Merge specfact-cli PR #332 to dev. βœ… Completed on `dev` (commit `039da8b`). Migration-02 is now non-reversibly closed: canonical source for the 17 modules is specfact-cli-modules only. @@ -622,7 +626,6 @@ Ensures bundle code in specfact-cli-modules does not hardcode imports from `spec **The following tasks (19.2–23.x) are deferred to `module-migration-05-modules-repo-quality`; do not check in migration-02. See `openspec/changes/module-migration-05-modules-repo-quality/tasks.md`.** - - [x] 19.2.1 Deferred handoff acknowledged: tracked in `module-migration-05` task 19.2.1. - [x] 19.2.2 Deferred handoff acknowledged: tracked in `module-migration-05` task 19.2.2. - [x] 19.2.3 Deferred handoff acknowledged: tracked in `module-migration-05` task 19.2.3. diff --git a/openspec/changes/archive/2026-03-04-module-migration-03-core-slimming/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-04-module-migration-03-core-slimming/CHANGE_VALIDATION.md index 2d6d3e19..ff5beb50 100644 --- a/openspec/changes/archive/2026-03-04-module-migration-03-core-slimming/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-04-module-migration-03-core-slimming/CHANGE_VALIDATION.md @@ -25,10 +25,12 @@ openspec validate module-migration-03-core-slimming --strict Result: **PASS** (`Change 'module-migration-03-core-slimming' is valid`). 2. Scope-consistency checks: + - Confirmed this change remains aligned to 0.40.0 release constraints and updated branch decision: **auth removal executed in migration-03 task 10.6** after backlog-auth-01 parity merged. - Updated spec deltas/tasks/design to reflect accepted 3-core/auth-moved scope. 3. Deferred-test baseline handoff: + - Added concrete `smart-test-full` baseline reference to migration-06 and migration-07 proposals: - `logs/tests/test_run_20260303_194459.log` - summary: `2738` collected, `359 failed`, `19 errors`, `22 skipped`. diff --git a/openspec/changes/archive/2026-03-04-module-migration-03-core-slimming/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-04-module-migration-03-core-slimming/TDD_EVIDENCE.md index 1a84a35c..8f117564 100644 --- a/openspec/changes/archive/2026-03-04-module-migration-03-core-slimming/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-04-module-migration-03-core-slimming/TDD_EVIDENCE.md @@ -64,7 +64,7 @@ - Command: `hatch test -- tests/unit/registry/test_core_only_bootstrap.py -v` - Timestamp: 2026-03-02 - Result: **7 passed** - - Notes: Removed _register_category_groups_and_shims (unconditional category/shim registration). CORE_MODULE_ORDER trimmed to 4 core (init, auth, module-registry, upgrade). _mount_installed_category_groups already used when category_grouping_enabled; added @beartype. Bootstrap registers only discovered packages; category groups and flat shims only for installed bundles. + - Notes: Removed _register_category_groups_and_shims (unconditional category/shim registration). CORE_MODULE_ORDER trimmed to 4 core (init, auth, module-registry, upgrade)._mount_installed_category_groups already used when category_grouping_enabled; added @beartype. Bootstrap registers only discovered packages; category groups and flat shims only for installed bundles. ### Phase: Task 12 β€” Phase 3 (cli.py) @@ -80,7 +80,7 @@ - Command: `hatch test -- tests/unit/modules/init/test_mandatory_bundle_selection.py -v` - Timestamp: 2026-03-02 - Result: **4 passed** - - Notes: VALID_PROFILES and PROFILE_BUNDLES in commands.py. init_command has @require(profile in VALID_PROFILES). _install_profile_bundles(profile) and _install_bundle_list(install_arg) implemented with @beartype; CI/CD gate and interactive first-run flow unchanged and passing. + - Notes: VALID_PROFILES and PROFILE_BUNDLES in commands.py. init_command has @require(profile in VALID_PROFILES). _install_profile_bundles(profile) and_install_bundle_list(install_arg) implemented with @beartype; CI/CD gate and interactive first-run flow unchanged and passing. ### Phase: Task 14 β€” Module signing gate diff --git a/openspec/changes/archive/2026-03-04-module-migration-04-remove-flat-shims/proposal.md b/openspec/changes/archive/2026-03-04-module-migration-04-remove-flat-shims/proposal.md index 51d3df99..dde6f46a 100644 --- a/openspec/changes/archive/2026-03-04-module-migration-04-remove-flat-shims/proposal.md +++ b/openspec/changes/archive/2026-03-04-module-migration-04-remove-flat-shims/proposal.md @@ -2,7 +2,6 @@ ## Why - Module-migration-01 introduced category group commands (`code`, `backlog`, `project`, `spec`, `govern`) and backward-compatibility shims so existing flat commands (e.g. `specfact validate`, `specfact analyze`) still worked while emitting a deprecation notice. The proposal stated: "Shims are removed after one major version cycle." The 0.40.x series completes that migration: the top-level CLI surface should show only core commands (`init`, `auth`, `module`, `upgrade`) and the five category groups. Scripts and muscle memory that still invoke flat commands must switch to the category form (e.g. `specfact code validate`). This reduces noise in `specfact --help`, clarifies the canonical command topology, and avoids maintaining two code paths. diff --git a/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/RESIDUAL_FAILURES.md b/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/RESIDUAL_FAILURES.md index ec9480c2..cc1ce0ec 100644 --- a/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/RESIDUAL_FAILURES.md +++ b/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/RESIDUAL_FAILURES.md @@ -5,6 +5,7 @@ Date: 2026-03-04 ## Scope of this residual list This list captures items that are either: + - not bundle-scope defects inside `specfact-cli-modules`, or - not executable from this environment (remote GitHub operations), after local bundle test migration parity was validated (`hatch run smart-test` passed). diff --git a/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/TEST_INVENTORY.md b/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/TEST_INVENTORY.md index 55ef75a2..01b25514 100644 --- a/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/TEST_INVENTORY.md +++ b/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/TEST_INVENTORY.md @@ -29,4 +29,3 @@ and the target locations in `specfact-cli-modules`. ## E2E tests (specfact-cli) - `tests/e2e/test_bundle_extraction_e2e.py` β†’ bundles: all β†’ target: `tests/e2e/test_bundle_extraction_e2e.py` - diff --git a/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/proposal.md b/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/proposal.md index 14ffa473..0bbeec1f 100644 --- a/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/proposal.md +++ b/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/proposal.md @@ -5,6 +5,7 @@ `module-migration-02-bundle-extraction` moved the source for 17 modules from specfact-cli into independently versioned bundle packages in specfact-cli-modules, but explicitly deferred the quality and operational parity work that makes specfact-cli-modules a viable canonical development environment. Sections 18–23 of migration-02's tasks.md capture this deferred scope. After migration-03 closes, specfact-cli-modules becomes the **canonical and only** home for those 17 modules. Without this change, developers working on bundles in specfact-cli-modules will lack: + - A test suite that runs against bundle code directly (not via specfact-cli shims) - Contract-first validation (`@icontract` / CrossHair) - Coverage thresholds diff --git a/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/tasks.md b/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/tasks.md index 22b40123..abe9f4ac 100644 --- a/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/tasks.md +++ b/openspec/changes/archive/2026-03-04-module-migration-05-modules-repo-quality/tasks.md @@ -67,7 +67,7 @@ Add PR orchestrator (or equivalent) and align CI so PRs to specfact-cli-modules - change detection + dev->main skip behavior - explicit `verify-module-signatures` job (`--require-signature --enforce-version-bump`) - matrix `quality (3.11/3.12/3.13)` jobs for format/type/lint/yaml/import-boundary/contract/smart-test/test - - Test PR opened: https://github.com/nold-ai/specfact-cli-modules/pull/4 + - Test PR opened: - Observed checks: - `quality (3.11)` pass (51s) - `quality (3.12)` pass (53s) @@ -282,7 +282,7 @@ Document and operationalize the versioning policy for independently released bun ## PR and closure - [x] PR.1 Create PR in specfact-cli-modules from feature branch to `dev`; reference migration-02 #316 and this change's GitHub issue - - Implemented as: https://github.com/nold-ai/specfact-cli-modules/pull/5 + - Implemented as: - [x] PR.2 Confirm CI passes all gates on the PR - PR #5 checks passed: - `detect-changes` @@ -301,5 +301,6 @@ Document and operationalize the versioning policy for independently released bun ## CHANGE_ORDER.md update required When archived, update `openspec/CHANGE_ORDER.md`: + - Move `module-migration-05-modules-repo-quality` from Pending to Implemented with archive date - If timing constraint met (sections 18-22 before migration-03): update migration-03's row to remove the quality-and-decoupling blocker note diff --git a/openspec/changes/archive/2026-03-05-backlog-core-07-ado-required-custom-fields-and-picklists/design.md b/openspec/changes/archive/2026-03-05-backlog-core-07-ado-required-custom-fields-and-picklists/design.md index 2d7702bc..58a74115 100644 --- a/openspec/changes/archive/2026-03-05-backlog-core-07-ado-required-custom-fields-and-picklists/design.md +++ b/openspec/changes/archive/2026-03-05-backlog-core-07-ado-required-custom-fields-and-picklists/design.md @@ -5,6 +5,7 @@ This change improves the ADO branch of the bridge adapter workflow while preserving provider-agnostic command structure. Current ownership after module migration: + - `specfact-cli-modules` (`specfact-backlog`) owns `specfact backlog map-fields`. - `specfact-cli` owns `backlog-core add` command orchestration and shared ADO adapter create path. @@ -13,12 +14,14 @@ Design must keep these boundaries explicit so each repo change is independently ## Goals / Non-Goals **Goals:** + - Persist ADO field constraint metadata during `map-fields` for add-time checks. - Provide interactive picklist selection for constrained ADO fields. - Enforce and explain constrained value validation in non-interactive mode. - Keep add-flow behavior deterministic when API discovery is unavailable. **Non-Goals:** + - Rework GitHub/Jira/Linear field mapping UX in this change. - Add new remote caching services or cloud dependencies. - Introduce breaking schema changes outside backlog config field metadata. @@ -26,25 +29,30 @@ Design must keep these boundaries explicit so each repo change is independently ## Decisions ### 1. Persist field constraints by work item type in backlog config + - Decision: extend persisted ADO mapping metadata to include `required` and `allowed_values` keyed by ADO field ref-name and work item type. - Rationale: keeps interactive and non-interactive behavior aligned and offline-capable for known mappings. - Alternative considered: live API checks only during add. Rejected due to offline/latency coupling and inconsistent non-interactive determinism. ### 2. Interactive picker uses constrained option lists from metadata API + - Decision: in interactive add mode, when a mapped field has constrained values, render a terminal picker (up/down, enter) for selection instead of free-form input. - Rationale: avoids invalid values and improves UX for long enterprise picklists. - Alternative considered: prompt free-form plus post-submit validation. Rejected due to repeated failure loops and poor discoverability. ### 3. Non-interactive validation is fail-fast with allowed-values hint + - Decision: validate provided values before create call and fail with explicit accepted values when invalid. - Rationale: script-friendly deterministic failure and actionable remediation. - Alternative considered: silent coercion/case-insensitive fuzzy matching. Rejected due to ambiguity and risk of wrong field data. ### 4. Contract enforcement remains on public command/service boundaries + - Decision: keep/extend `@icontract` and `@beartype` annotations on public validation/payload functions touched by the change. - Rationale: contract-first baseline for regression prevention. ### 5. Cross-repo schema compatibility for provider metadata + - Decision: persisted metadata keys for required/constrained fields are additive and backward-compatible so either repo can read safely during staged rollout. - Rationale: map-fields and add/create are split across repositories; temporary version skew must not crash commands. - Alternative considered: strict schema bump requiring lockstep release. Rejected due to operational friction and higher rollback risk. diff --git a/openspec/changes/archive/2026-03-05-backlog-core-07-ado-required-custom-fields-and-picklists/proposal.md b/openspec/changes/archive/2026-03-05-backlog-core-07-ado-required-custom-fields-and-picklists/proposal.md index 2e371fca..b2b34245 100644 --- a/openspec/changes/archive/2026-03-05-backlog-core-07-ado-required-custom-fields-and-picklists/proposal.md +++ b/openspec/changes/archive/2026-03-05-backlog-core-07-ado-required-custom-fields-and-picklists/proposal.md @@ -2,12 +2,10 @@ ## Why - `specfact backlog add --adapter ado` can fail when required custom fields exist and field constraints include allowed picklist values. Today, users can supply values that are not valid for the selected work item type, and the command does not consistently guide them to resolvable values in interactive or non-interactive mode. ## What Changes - - Extend `specfact backlog map-fields` to dynamically detect required custom fields per ADO work item type and persist requirement metadata for add-time validation. - Add a non-interactive `specfact backlog map-fields` mode that auto-discovers and applies deterministic mappings; fail with guidance to run interactive mapping only when auto-mapping cannot resolve required fields. - Extend `specfact backlog add --adapter ado` to accept repeatable `--custom-field key=value` input and merge mapped custom fields into create payload. @@ -18,6 +16,7 @@ - Update user-facing docs for `backlog map-fields` and `backlog add` custom field/picklist behavior. ## Capabilities + ### New Capabilities - `ado-field-value-selection`: Interactive selection workflow for ADO constrained field values during backlog add. diff --git a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/design.md b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/design.md index f85ee231..5fa5fdb6 100644 --- a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/design.md +++ b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/design.md @@ -3,6 +3,7 @@ The docs set spans the repository README, the published Jekyll site under `docs/`, architecture pages, command references, getting-started guides, adapter guides, and module marketplace guidance. The modularization wave changed the runtime model significantly: the CLI now has a lean core, most workflow commands are grouped under bundle categories, official bundles are installed from the marketplace, and the canonical implementation for extracted modules lives in `specfact-cli-modules`. The current documentation has three structural risks: + - drift from the former flat command topology, - duplicated or inconsistent marketplace/module guidance across README, guides, and reference pages, - no clearly documented ownership boundary for module-specific docs that still live in the core repo. @@ -12,6 +13,7 @@ This is a cross-cutting documentation change rather than a runtime feature. The ## Goals / Non-Goals **Goals:** + - Establish a documentation contract for the post-modularization architecture. - Audit and align all user-facing Markdown so command examples, installation flows, and architecture descriptions match current reality. - Separate core-owned documentation concerns from module/bundle-owned concerns without breaking current docs navigation. @@ -19,6 +21,7 @@ This is a cross-cutting documentation change rather than a runtime feature. The - Leave explicit migration notes so future docs relocation to `specfact-cli-modules` is expected and documented. **Non-Goals:** + - No runtime command or packaging behavior changes. - No immediate move of the docs publishing site from `specfact-cli` to `specfact-cli-modules`. - No attempt to archive or rewrite historical OpenSpec records. @@ -27,19 +30,23 @@ This is a cross-cutting documentation change rather than a runtime feature. The ## Decisions ### Decision: Treat the work as a full docs inventory plus ownership cleanup + A partial doc fix would leave stale pages behind because command and marketplace guidance is spread across many sections. The implementation will therefore inventory all first-party Markdown under `README.md` and `docs/` and classify each page as core-owned, module-owned-but-temporarily-hosted, shared, historical, or generated/vendor. Alternative considered: update only README, index, and command reference. Why not chosen: that would not satisfy the user-visible requirement to check every Markdown page and would preserve hidden drift in guides and adapters. ### Decision: Keep core docs as the publication host for now, but explicitly label temporary module-doc hosting + The docs site is still published from this repo, so the immediate change should not move hosting. Instead, module-focused pages will carry a consistent note that the content remains temporarily hosted in core and is intended to migrate to `specfact-cli-modules`. Alternative considered: move module docs immediately as part of this change. Why not chosen: that is a larger repo/process migration and would mix documentation alignment with publishing/platform changes. ### Decision: Reframe command docs around ownership and installation source + Command reference and related guides will describe: + - permanent core commands always available in `specfact-cli`, - grouped bundle commands that appear after marketplace installation, - per-category and per-package docs instead of a single legacy flat list. @@ -48,12 +55,14 @@ Alternative considered: keep one monolithic command reference and only patch exa Why not chosen: it would continue to blur the core-vs-bundle boundary and keep the old mental model alive. ### Decision: Update directory/dependency docs as architecture documentation, not just command help + `docs/reference/directory-structure.md`, `docs/reference/dependency-resolution.md`, module architecture docs, and marketplace docs will be aligned together so readers understand why bundles depend on each other, where code now lives, and which repo owns which artifacts. Alternative considered: keep dependency and directory docs unchanged because runtime behavior already works. Why not chosen: those pages are part of the architecture contract and will actively mislead contributors if left in pre-migration form. ### Decision: Add lightweight docs parity validation where practical + If existing tests can be extended cheaply, add or update targeted parity checks for command-surface and docs-ownership expectations. Alternative considered: rely only on manual review. diff --git a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/proposal.md b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/proposal.md index 7dc56f75..5d4d944f 100644 --- a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/proposal.md +++ b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/proposal.md @@ -18,9 +18,11 @@ The documentation set still carries drift from the pre-modularized CLI: some pag ## Capabilities ### New Capabilities + - `module-docs-ownership`: documentation defines the current and target ownership boundary between `specfact-cli` core docs and `specfact-cli-modules` bundle docs, including an explicit migration note for future relocation of module-specific documentation. ### Modified Capabilities + - `documentation-alignment`: documentation requirements are extended to cover the post-modularization command surface, lean-core architecture, marketplace-distributed bundles, and removal of stale flat-command guidance. - `implementation-status-docs`: implementation-status and architecture pages must clearly describe which functionality is owned by core, which is delivered by marketplace bundles, and which documentation remains temporarily hosted in core. - `module-development-guide`: module development and module architecture docs must reflect the dedicated modules repository, bundle/package boundaries, and the expected split between core lifecycle docs and bundle-specific command docs. diff --git a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/documentation-alignment/spec.md b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/documentation-alignment/spec.md index 5f9f2279..e2d3d1d7 100644 --- a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/documentation-alignment/spec.md +++ b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/documentation-alignment/spec.md @@ -1,35 +1,43 @@ ## ADDED Requirements ### Requirement: Live docs reflect lean-core and grouped bundle command topology + The live documentation set SHALL describe the current command surface as a lean core plus marketplace-installed grouped bundle commands, and SHALL NOT present the former flat all-commands topology as the primary current UX. #### Scenario: Reader checks command examples + - **WHEN** a reader follows command examples in README or published docs - **THEN** core commands are shown as always available from `specfact-cli` - **AND** bundle commands are shown through grouped command paths and marketplace installation context - **AND** stale flat-command examples are removed, corrected, or clearly marked as historical compatibility context ### Requirement: Marketplace guidance is discoverable and non-duplicative + Marketplace, bundle installation, dependency, trust, and publishing documentation SHALL be available through clear entry points and SHALL avoid contradictory or duplicate guidance across README, landing pages, guides, and reference pages. #### Scenario: Reader looks for marketplace workflow guidance + - **WHEN** a reader wants to install, trust, publish, or understand official bundles - **THEN** the docs provide a discoverable path from README or docs landing into marketplace-specific pages - **AND** terminology, command examples, and workflow descriptions are consistent across those pages ### Requirement: Command reference reflects ownership and package boundaries + The command reference documentation SHALL distinguish permanent core commands from marketplace-delivered bundle commands and SHALL organize module command coverage by package/category ownership instead of one legacy flat command inventory. #### Scenario: Reader checks command reference + - **WHEN** a reader opens command reference documentation - **THEN** the reference identifies which commands belong to core and which are provided by installed bundles - **AND** bundle command coverage is grouped by category or package boundary - **AND** readers can navigate from command docs to the relevant marketplace/module docs without ambiguity ### Requirement: Markdown quality workflow auto-fixes low-risk issues before enforcement + The documentation workflow SHALL automatically fix low-risk Markdown issues during pre-commit checks before enforcing markdown lint failures for non-fixable or higher-risk issues. #### Scenario: Contributor stages Markdown changes with trivial spacing issues + - **WHEN** a contributor stages Markdown files and runs the repository pre-commit checks - **THEN** the workflow attempts safe markdown auto-fixes first using the configured markdown lint tooling - **AND** any auto-fixed Markdown files are re-staged automatically diff --git a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/implementation-status-docs/spec.md b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/implementation-status-docs/spec.md index 089741e0..109be0bd 100644 --- a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/implementation-status-docs/spec.md +++ b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/implementation-status-docs/spec.md @@ -1,9 +1,11 @@ ## ADDED Requirements ### Requirement: Implementation-status docs describe core versus bundle ownership + Implementation-status and architecture status documentation SHALL explicitly describe which capabilities are owned by core runtime versus marketplace-installed bundles, and SHALL identify documentation that is still temporarily hosted in core despite belonging to bundle workflows. #### Scenario: Reader checks ownership in status docs + - **WHEN** a reader reviews implementation-status or architecture status pages - **THEN** the docs distinguish core lifecycle/runtime ownership from bundle workflow ownership - **AND** temporary docs-hosting exceptions are called out so documentation location does not imply incorrect runtime ownership diff --git a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/module-development-guide/spec.md b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/module-development-guide/spec.md index a8fe8c9d..3fbf0e22 100644 --- a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/module-development-guide/spec.md +++ b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/module-development-guide/spec.md @@ -1,17 +1,21 @@ ## ADDED Requirements ### Requirement: Module development docs reflect the dedicated modules repository model + The module development guide SHALL describe that official bundle implementation lives in `specfact-cli-modules`, while `specfact-cli` owns the lean runtime, registry, marketplace lifecycle, and shared contracts needed by installed bundles. #### Scenario: Developer reads module development docs after modularization + - **WHEN** a contributor reads the module development guide - **THEN** the guide explains the current two-repository model - **AND** it identifies which code and documentation concerns belong in `specfact-cli` versus `specfact-cli-modules` ### Requirement: Directory and dependency docs reflect bundle boundaries + Module development, directory-structure, and dependency documentation SHALL describe the current bundle/package layout, canonical repository ownership, and bundle dependency relationships introduced by marketplace-installed official bundles. #### Scenario: Contributor checks structure and dependency guidance + - **WHEN** a contributor reads directory or dependency documentation related to modules - **THEN** the docs show the current bundle/package boundaries and repository ownership - **AND** dependency explanations match the marketplace-installed bundle model rather than the former in-repo bundled module layout diff --git a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/module-docs-ownership/spec.md b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/module-docs-ownership/spec.md index 533f7507..a8ca20be 100644 --- a/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/module-docs-ownership/spec.md +++ b/openspec/changes/archive/2026-03-05-docs-01-core-modules-docs-alignment/specs/module-docs-ownership/spec.md @@ -1,17 +1,21 @@ ## ADDED Requirements ### Requirement: Core docs declare current and target docs ownership boundaries + The documentation SHALL state which documentation concerns remain owned by `specfact-cli` core, which concerns belong to marketplace-installed module bundles, and that module-specific docs are temporarily still hosted in the core docs set until they are migrated to `specfact-cli-modules`. #### Scenario: Reader checks docs ownership model + - **WHEN** a reader opens the README, docs landing page, or module architecture/development documentation - **THEN** the docs explain that core runtime, installation, lifecycle, registry, and marketplace concepts remain documented in `specfact-cli` - **AND** they explain that bundle-specific command and workflow docs are temporarily hosted there but are intended to migrate to `specfact-cli-modules` ### Requirement: Module-specific docs carry a migration note while hosted in core + Any live module-specific guide or reference page that remains in `specfact-cli` SHALL include a consistent note that the page is temporarily hosted in core and is planned to migrate to the modules repository. #### Scenario: Reader opens a bundle-focused page + - **WHEN** a reader opens a module- or bundle-focused guide in the core docs set - **THEN** the page includes a visible note about temporary hosting in `specfact-cli` - **AND** the note points to `specfact-cli-modules` as the future long-term home for module-specific documentation diff --git a/openspec/changes/archive/2026-03-05-module-migration-06-core-decoupling-cleanup/MIGRATION_REMOVAL_PLAN.md b/openspec/changes/archive/2026-03-05-module-migration-06-core-decoupling-cleanup/MIGRATION_REMOVAL_PLAN.md index c1c61811..87c460d9 100644 --- a/openspec/changes/archive/2026-03-05-module-migration-06-core-decoupling-cleanup/MIGRATION_REMOVAL_PLAN.md +++ b/openspec/changes/archive/2026-03-05-module-migration-06-core-decoupling-cleanup/MIGRATION_REMOVAL_PLAN.md @@ -36,6 +36,7 @@ Components with **no** imports from core (cli, init, module_registry, upgrade, r - **`sync`** (after bridge_templates): Only used by bridge_templates and tests. specfact-project has `sync_runtime`. β†’ Remove after bridge_templates; migrate sync tests. Phase 1 progress (2026-03-05): + - `templates.bridge_templates` removed from core (completed earlier). - Legacy unit tests under `specfact-cli/tests/unit/sync/` migrated to `specfact-cli-modules/tests/unit/specfact_project/sync_runtime/` (102 tests passing in modules worktree). - Core now enforces this via `test_core_repo_does_not_host_sync_runtime_unit_tests`. diff --git a/openspec/changes/archive/2026-03-05-module-migration-06-core-decoupling-cleanup/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-05-module-migration-06-core-decoupling-cleanup/TDD_EVIDENCE.md index b43b14e5..14cd69fd 100644 --- a/openspec/changes/archive/2026-03-05-module-migration-06-core-decoupling-cleanup/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-05-module-migration-06-core-decoupling-cleanup/TDD_EVIDENCE.md @@ -9,6 +9,7 @@ #### Pre-implementation (failing) evidence Temporary violation added to `src/specfact_cli/registry/bootstrap.py`: + ```python from backlog_core.main import backlog_app # noqa: F401 ``` @@ -16,6 +17,7 @@ from backlog_core.main import backlog_app # noqa: F401 **Command:** `hatch run pytest tests/unit/specfact_cli/test_module_boundary_imports.py::test_core_does_not_import_from_bundle_packages -v` **Result:** FAILED + ``` AssertionError: Core must not import from bundle packages (backlog_core, bundle_mapper). - src/specfact_cli/registry/bootstrap.py: from backlog_core.main import @@ -62,6 +64,7 @@ Added boundary test: `test_core_repo_does_not_host_sync_runtime_unit_tests`. **Command:** `hatch run pytest tests/unit/specfact_cli/test_module_boundary_imports.py::test_core_repo_does_not_host_sync_runtime_unit_tests -v` **Result:** FAILED + ``` AssertionError: Sync runtime unit tests must be migrated out of specfact-cli into specfact-cli-modules. - tests/unit/sync/test_bridge_probe.py @@ -77,9 +80,11 @@ AssertionError: Sync runtime unit tests must be migrated out of specfact-cli int #### Post-implementation (passing) evidence Migrated legacy core sync-runtime unit tests from: + - `specfact-cli/tests/unit/sync/test_*.py` To modules repo: + - `specfact-cli-modules/tests/unit/specfact_project/sync_runtime/test_*.py` Then removed migrated tests from `specfact-cli` core. diff --git a/openspec/changes/archive/2026-03-09-backlog-module-ownership-cleanup/proposal.md b/openspec/changes/archive/2026-03-09-backlog-module-ownership-cleanup/proposal.md index de5359ad..6ce7ea8f 100644 --- a/openspec/changes/archive/2026-03-09-backlog-module-ownership-cleanup/proposal.md +++ b/openspec/changes/archive/2026-03-09-backlog-module-ownership-cleanup/proposal.md @@ -2,12 +2,10 @@ ## Why - Backlog command ownership is still split across `specfact-cli` core shims, the built-in `backlog-core` package, and the marketplace bundle `nold-ai/specfact-backlog`. That violates the intended module boundary, causes duplicate command overlap, and leaves backlog prompts/templates coupled to core code even though they belong to backlog behavior. ## What Changes - - Make `nold-ai/specfact-backlog` the sole owner of user-facing backlog and policy commands. - Remove backlog command ownership and backlog-specific prompt/template assets from `specfact-cli` core. - Keep only truly shared runtime contracts, provider adapters, and generic data models in core. @@ -15,11 +13,13 @@ Backlog command ownership is still split across `specfact-cli` core shims, the b - Remove duplicate-registration fallback behavior once backlog is module-owned and no longer overlaps with core. ## Capabilities + ### New Capabilities - `backlog-module-ownership`: Backlog commands, prompts, and templates are owned by the backlog module instead of being split across core and module layers. ## Acceptance Criteria + - Running `specfact` with `nold-ai/specfact-backlog` installed does not produce duplicate backlog command overlap warnings during normal startup. - User-facing backlog commands are provided only by the installed backlog module, not by a parallel core backlog command surface. - Backlog-specific prompts and templates are shipped from the backlog module instead of core CLI resources. @@ -27,11 +27,11 @@ Backlog command ownership is still split across `specfact-cli` core shims, the b - Validation and command-audit coverage prove the backlog command tree works end-to-end with module-only ownership. ## Dependencies + - `module-migration-06-core-decoupling-cleanup` (#338) and `module-migration-07-test-migration-cleanup` (#339) establish the broader module-migration baseline. - `cli-val-07-command-package-runtime-validation` documents the duplicate-command/runtime-noise findings this cleanup resolves. - `init-ide-prompt-source-selection` depends on this cleanup to finalize prompt ownership boundaries. - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-03-09-backlog-module-ownership-cleanup/specs/backlog-module-ownership/spec.md b/openspec/changes/archive/2026-03-09-backlog-module-ownership-cleanup/specs/backlog-module-ownership/spec.md index 8134c9c9..9e58a699 100644 --- a/openspec/changes/archive/2026-03-09-backlog-module-ownership-cleanup/specs/backlog-module-ownership/spec.md +++ b/openspec/changes/archive/2026-03-09-backlog-module-ownership-cleanup/specs/backlog-module-ownership/spec.md @@ -5,11 +5,13 @@ The system SHALL treat `nold-ai/specfact-backlog` as the sole owner of user-facing backlog and policy command surfaces. #### Scenario: Core does not directly own backlog feature commands + - **WHEN** command registration is resolved in `specfact-cli` - **THEN** user-facing backlog feature commands are provided by the installed backlog module - **AND** core does not ship a parallel built-in backlog command surface for the same feature commands. #### Scenario: Core keeps only shared backlog framework contracts + - **WHEN** backlog ownership is resolved after migration - **THEN** core retains only shared provider integrations, generic data models, and minimal backlog contracts reused outside the backlog bundle - **AND** backlog-only command implementations, prompt resources, templates, and refinement helpers are not owned by core. @@ -19,6 +21,7 @@ The system SHALL treat `nold-ai/specfact-backlog` as the sole owner of user-faci Backlog-specific prompts, prompt templates, and backlog template semantics SHALL be owned by the backlog module, not by `specfact-cli` core. #### Scenario: Backlog refinement assets are not exported from core + - **WHEN** backlog-specific prompt/template resources are resolved - **THEN** they come from the backlog module resource set - **AND** core retains only generic framework/template infrastructure, if any. @@ -28,6 +31,7 @@ Backlog-specific prompts, prompt templates, and backlog template semantics SHALL The system SHALL not rely on duplicate backlog command overlap handling for normal runtime registration. #### Scenario: Backlog registration is single-owned + - **WHEN** the backlog module is installed and enabled - **THEN** normal registration does not require suppressing duplicate backlog command collisions between core and module code - **AND** users do not see duplicate backlog-extension warnings caused by split ownership. diff --git a/openspec/changes/archive/2026-03-09-cli-val-07-command-package-runtime-validation/design.md b/openspec/changes/archive/2026-03-09-cli-val-07-command-package-runtime-validation/design.md index 57fae5a2..03031e27 100644 --- a/openspec/changes/archive/2026-03-09-cli-val-07-command-package-runtime-validation/design.md +++ b/openspec/changes/archive/2026-03-09-cli-val-07-command-package-runtime-validation/design.md @@ -42,6 +42,7 @@ The validation matrix should be built from: This keeps the plan aligned with the shipped code surface and reduces drift when new commands are added. Alternative considered: + - A static markdown checklist only. Rejected because it will drift as soon as bundle commands change. ### 2. Validate commands in user-realistic phases @@ -62,6 +63,7 @@ The execution order should be: This order front-loads setup failures and ensures dependent groups are validated after their owning bundle is installed. Alternative considered: + - Alphabetical execution across all commands. Rejected because it hides setup dependencies and makes failures harder to triage. ### 3. Define a safe validation argv for every leaf command @@ -75,6 +77,7 @@ Each command must have a deterministic validation invocation recorded in the mat The matrix must explicitly mark which category each command uses so gaps are visible. Alternative considered: + - Execute only `--help` for all commands. Rejected because it would not catch runtime bootstrap/output regressions like the current one. ### 4. Separate forbidden diagnostic noise from actionable warnings @@ -92,6 +95,7 @@ Normal output must still preserve: - command-level validation errors the user can act on Alternative considered: + - Silence all startup warnings. Rejected because it would hide legitimate security or precedence problems. ### 5. Reuse existing acceptance test infrastructure where possible @@ -103,6 +107,7 @@ Implementation should extend the existing CLI validation approach rather than in - fixture workspaces for safe command execution categories Alternative considered: + - A standalone shell script outside pytest. Rejected because it weakens repeatability and CI integration. ### 6. Treat marketplace bundle code and core backlog code as one runtime surface for validation @@ -119,6 +124,7 @@ Validation and fixes therefore need to cover both repositories together. In prac - saved provider metadata from the bundle must be consumed by `backlog add` in core Alternative considered: + - Move all backlog behavior into one package as part of this change. Rejected because it is a larger architectural migration than the current release-stabilization scope. ### 7. Make long metadata fetches observable instead of silent @@ -126,6 +132,7 @@ Alternative considered: `backlog map-fields` now fetches required-field and picklist metadata after the user selects an ADO work item type. That can trigger many API calls before the next prompt appears. The command should therefore emit a progress/status message before this post-selection fetch so the CLI does not appear hung. Alternative considered: + - Keep the current behavior and rely on faster networks. Rejected because the current silent gap is already confusing users and obscures whether the command is still working. ## Risks / Trade-offs diff --git a/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/CHANGE_VALIDATION.md index ee2ef9d4..0a3ff554 100644 --- a/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/CHANGE_VALIDATION.md @@ -25,6 +25,7 @@ A patch release is required before PyPI users receive this fix, because `0.40.0` ## Install verification A clean temp venv install of `dist/specfact_cli-0.40.1-py3-none-any.whl` was verified with both: + - `specfact -v` - `specfact-cli -v` diff --git a/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/TDD_EVIDENCE.md index dfe72354..53fc7813 100644 --- a/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/TDD_EVIDENCE.md @@ -16,6 +16,7 @@ PY ``` Observed failure summary: + - installed `specfact_cli` package contained only `modules/` and `resources/` - `specfact_cli/cli.py` missing from installed `0.40.0` wheel payload - console script `specfact-cli` failed with `ModuleNotFoundError: No module named 'specfact_cli.cli'` @@ -29,6 +30,7 @@ HATCH_DATA_DIR=/tmp/hatch-data HATCH_CACHE_DIR=/tmp/hatch-cache VIRTUALENV_OVERR ``` Observed failure summary: + - `test_pyproject_wheel_explicitly_maps_src_package_root` failed because wheel config lacked explicit `only-include` / `sources` mapping for `src/specfact_cli` ## Passing Evidence @@ -42,6 +44,7 @@ HATCH_DATA_DIR=/tmp/hatch-data HATCH_CACHE_DIR=/tmp/hatch-cache VIRTUALENV_OVERR ``` Result: + - `6 passed` ### 2026-03-06 built wheel artifact verification @@ -69,6 +72,7 @@ PY ``` Result summary: + - built wheel contains `specfact_cli/__init__.py` - built wheel contains `specfact_cli/cli.py` - entry points include both `specfact` and `specfact-cli` targeting `specfact_cli.cli:cli_main` @@ -86,6 +90,7 @@ python -m venv /tmp/specfact-cli-wheel-verify --system-site-packages ``` Result summary: + - installed wheel exposes `specfact` - installed wheel exposes `specfact-cli` - both commands return `SpecFact CLI version 0.40.1` diff --git a/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/specs/package-distribution/spec.md b/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/specs/package-distribution/spec.md index b7be683d..b7b24b09 100644 --- a/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/specs/package-distribution/spec.md +++ b/openspec/changes/archive/2026-03-09-packaging-01-wheel-package-inclusion/specs/package-distribution/spec.md @@ -7,12 +7,14 @@ The published wheel MUST include the importable `specfact_cli` Python package required by declared console scripts. #### Scenario: Wheel contains core CLI module + - **GIVEN** a wheel is built from the repository release configuration - **WHEN** its contents are inspected - **THEN** it includes `specfact_cli/cli.py` - **AND** it includes `specfact_cli/__init__.py` #### Scenario: Console scripts target importable CLI entrypoint + - **GIVEN** the built distribution metadata - **WHEN** console script entrypoints are inspected - **THEN** both `specfact` and `specfact-cli` resolve to `specfact_cli.cli:cli_main` diff --git a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/TDD_EVIDENCE.md index 5b5e46b9..e5bb32c1 100644 --- a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/TDD_EVIDENCE.md @@ -20,10 +20,12 @@ ## Phase 1: Setup and Integration ### Task 1.1-1.6: Worktree Setup + **Status:** COMPLETE **Time:** 2026-03-10 22:06-22:07 Commands executed: + ```bash git worktree add ../specfact-cli-worktrees/feature/backlog-02-migrate-core-commands -b feature/backlog-02-migrate-core-commands origin/dev cd ../specfact-cli-worktrees/feature/backlog-02-migrate-core-commands @@ -34,6 +36,7 @@ cp -r ../specfact-cli-worktrees/feature/agile-01-feature-hierarchy/modules/backl ``` **Evidence:** + - Worktree created: `feature/backlog-02-migrate-core-commands` - Source files copied: add.py, analyze_deps.py, delta.py, diff.py, promote.py, sync.py, verify.py, release_notes.py @@ -42,10 +45,12 @@ cp -r ../specfact-cli-worktrees/feature/agile-01-feature-hierarchy/modules/backl ## Phase 2: Integration ### Task 2.1-2.4: Import Updates and Command Registration + **Status:** COMPLETE **Time:** 2026-03-10 22:07-22:10 Changes made: + 1. Updated imports in backlog_core files: `from backlog_core.` β†’ `from specfact_backlog.backlog.` 2. Added imports to commands.py: - `from specfact_backlog.backlog_core.commands.add import add` @@ -65,6 +70,7 @@ Changes made: - `app.add_typer(_delta_app, name="delta", ...)` **Syntax Check:** + ```bash python3 -m py_compile packages/specfact-backlog/src/specfact_backlog/backlog/commands.py # Result: Syntax OK @@ -75,10 +81,12 @@ python3 -m py_compile packages/specfact-backlog/src/specfact_backlog/backlog/com ## Phase 3: Quality Gates ### Task 5.1-5.8: Quality Gates + **Status:** COMPLETE (ALL TESTS PASSING) **Time:** 2026-03-10 22:10-22:40 **Commands executed:** + ```bash cd /home/dom/git/nold-ai/specfact-cli-modules hatch run format # Result: All checks passed! 272 files @@ -88,6 +96,7 @@ hatch run smart-test # Result: 204 passed, 0 failed, 16 skipped ``` **Test Results:** + - **204 tests PASSED** (was 196, fixed 8 failures) - **0 tests FAILED** - **16 tests SKIPPED** (legacy retired functionality) @@ -110,9 +119,11 @@ hatch run smart-test # Result: 204 passed, 0 failed, 16 skipped - Added `schema_extensions` section to `module-package.yaml` **Version Update:** + - Bumped specfact-backlog version: 0.40.20 β†’ 0.41.0 **Module Signing:** + - Pre-commit hooks require module signing (GPG private key needed) - User action required: Run `hatch run python scripts/sign-modules.py --key-file ...` - PR #32 created with note about signing requirement @@ -122,11 +133,13 @@ hatch run smart-test # Result: 204 passed, 0 failed, 16 skipped ## Compliance Declaration **Rulesets Applied:** + - `.cursorrules` (Git Worktree Policy, AGENTS.md Authority) - `AGENTS.md` (Git Worktree Policy section, Hard Gate TDD) - `openspec/config.yaml` (task format, module signing) **Git Worktree Policy Compliance:** CONFIRMED + - Worktree created: /home/dom/git/nold-ai/specfact-cli-worktrees/feature/backlog-02-migrate-core-commands - Implementation from worktree: YES - Pre-flight checks: DONE diff --git a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/design.md b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/design.md index 2f08ab68..3deebc9d 100644 --- a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/design.md +++ b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/design.md @@ -3,6 +3,7 @@ ## Context The `backlog-core` module was deleted in commit 978cc82, removing 9 command implementations: + - `add` (27KB - interactive/non-interactive item creation) - `analyze_deps` (dependency graph analysis) - `sync` (bidirectional backlog sync) @@ -14,6 +15,7 @@ The `backlog-core` module was deleted in commit 978cc82, removing 9 command impl - `generate_release_notes` (release notes generation) Source code exists in: + - Worktree: `specfact-cli-worktrees/feature/agile-01-feature-hierarchy/modules/backlog-core/` - Git history: `git show 978cc82^:modules/backlog-core/` @@ -22,6 +24,7 @@ Target location: `specfact-cli-modules/packages/specfact-backlog/src/specfact_ba ## Goals / Non-Goals **Goals:** + - Recover all deleted command implementations - Integrate commands into specfact-backlog bundle structure - Maintain backward-compatible CLI surface (`specfact backlog add`, etc.) @@ -29,6 +32,7 @@ Target location: `specfact-cli-modules/packages/specfact-backlog/src/specfact_ba - Add ceremony aliases for high-impact commands **Non-Goals:** + - Re-implement from scratch (use existing code) - Modify command behavior (migration only, no feature changes) - Support backlog-core as standalone module (bundle-only) @@ -44,6 +48,7 @@ Target location: `specfact-cli-modules/packages/specfact-backlog/src/specfact_ba ### 2. Code Organization **Decision**: Structure under `backlog/commands/` with submodules: + ``` specfact_backlog/backlog/commands/ add.py @@ -62,6 +67,7 @@ specfact_backlog/backlog/commands/ ### 3. Integration Pattern **Decision**: Register commands via existing `commands.py` app, following same pattern as `daily`/`refine`: + ```python @app.command() def add(...): ... @@ -72,6 +78,7 @@ def add(...): ... ### 4. Ceremony Aliases **Decision**: Add ceremony aliases for high-frequency commands: + - `backlog ceremony add` β†’ `backlog add` - `backlog ceremony sync` β†’ `backlog sync` diff --git a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/proposal.md b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/proposal.md index 7f8dc39f..e8b6596d 100644 --- a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/proposal.md +++ b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/proposal.md @@ -2,12 +2,10 @@ ## Why - Commit 978cc82 deleted the `backlog-core` module (containing `add`, `analyze-deps`, `trace-impact`, `verify-readiness`, `diff`, `sync`, `promote`, `generate-release-notes`, `delta` commands) as part of backlog ownership cleanup. However, these commands were never migrated to the `nold-ai/specfact-backlog` bundle. Result: documented commands are missing from the CLI, creating a product/runtime alignment gap where README and docs describe commands that fail with "No such command". ## What Changes - - **RECOVER** deleted backlog-core command implementations from worktree/git history - **MIGRATE** commands into `specfact-backlog` bundle under appropriate subcommand structure - **INTEGRATE** command registrations with specfact-backlog's Typer app structure @@ -16,6 +14,7 @@ Commit 978cc82 deleted the `backlog-core` module (containing `add`, `analyze-dep - **DEPRECATE** legacy backlog-core module references in favor of bundle-only ownership ## Capabilities + ### New Capabilities - `backlog-add`: Interactive and non-interactive backlog item creation with parent validation, DoR checks, and adapter-specific payload construction (GitHub, ADO). @@ -33,6 +32,7 @@ Commit 978cc82 deleted the `backlog-core` module (containing `add`, `analyze-dep - `backlog-daily-markdown-normalization`: Ensure migrated commands support Markdown normalization for consistency. ## Dependencies + - `backlog-module-ownership-cleanup` (archived): Established that specfact-backlog should own all backlog commands. - `module-migration-10-bundle-command-surface-alignment`: Validates documented vs runtime command surface; this change closes the backlog gap. diff --git a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-add/spec.md b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-add/spec.md index 4401d123..f2c4636e 100644 --- a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-add/spec.md +++ b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-add/spec.md @@ -7,26 +7,31 @@ The system SHALL provide `specfact backlog add` command that creates backlog items with the same functionality as the deleted backlog-core implementation. #### Scenario: Add command creates GitHub issue + - **WHEN** the user runs `specfact backlog add --adapter github --project-id --type story --title "Test" --body "Body"` - **THEN** a GitHub issue is created with the specified title, body, and type - **AND** the command outputs the created issue ID, key, and URL #### Scenario: Add command creates ADO work item + - **WHEN** the user runs `specfact backlog add --adapter ado --project-id --type story --title "Test"` - **THEN** an ADO work item is created with the specified title and type - **AND** required custom fields are validated and included in payload #### Scenario: Interactive mode prompts for missing fields + - **WHEN** the user runs `specfact backlog add` without required fields - **THEN** interactive prompts request title, body, type, and parent - **AND** validation ensures parent exists before create #### Scenario: DoR validation before create + - **WHEN** the user runs `specfact backlog add --check-dor` - **THEN** the item is validated against `.specfact/dor.yaml` rules - **AND** creation proceeds only if DoR criteria are met #### Scenario: Ceremony alias works + - **WHEN** the user runs `specfact backlog ceremony add` - **THEN** the command forwards to `specfact backlog add` - **AND** all add options are available diff --git a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-analyze-deps/spec.md b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-analyze-deps/spec.md index be5af43f..927f9719 100644 --- a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-analyze-deps/spec.md +++ b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-analyze-deps/spec.md @@ -7,15 +7,18 @@ The system SHALL provide `specfact backlog analyze-deps` for dependency graph analysis. #### Scenario: Analyze-deps shows item dependencies + - **WHEN** the user runs `specfact backlog analyze-deps --project-id ` - **THEN** the backlog dependency graph is built - **AND** parent/child and blocking relationships are displayed #### Scenario: Cycle detection highlights issues + - **WHEN** the dependency graph contains cycles - **THEN** cycles are detected and reported as warnings - **AND** affected items are listed for resolution #### Scenario: Impact surface for selected item + - **WHEN** the user analyzes deps for a specific item - **THEN** upstream and downstream dependencies are highlighted diff --git a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-delta/spec.md b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-delta/spec.md index 990f76b9..d33bb891 100644 --- a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-delta/spec.md +++ b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-delta/spec.md @@ -7,18 +7,22 @@ The system SHALL provide `specfact backlog delta` with subcommands for backlog change analysis. #### Scenario: Delta status shows backlog changes + - **WHEN** the user runs `specfact backlog delta status --project-id ` - **THEN** current backlog state is compared to baseline - **AND** added/updated/deleted items are listed #### Scenario: Delta impact analyzes item effects + - **WHEN** the user runs `specfact backlog delta impact ` - **THEN** dependent items and cascade effects are identified #### Scenario: Delta cost-estimate calculates effort + - **WHEN** the user runs `specfact backlog delta cost-estimate` - **THEN** story points and business value deltas are aggregated #### Scenario: Delta rollback-analysis shows revert options + - **WHEN** the user runs `specfact backlog delta rollback-analysis` - **THEN** safe rollback paths and risks are presented diff --git a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-sync/spec.md b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-sync/spec.md index 610d1bc0..f44a0ea2 100644 --- a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-sync/spec.md +++ b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-sync/spec.md @@ -7,19 +7,23 @@ The system SHALL provide `specfact backlog sync` command for bidirectional backlog synchronization. #### Scenario: Sync from OpenSpec to backlog + - **WHEN** the user runs `specfact backlog sync --adapter github --project-id ` - **THEN** OpenSpec changes are exported to GitHub issues/ADO work items - **AND** state mapping preserves status semantics #### Scenario: Bidirectional sync with cross-adapter + - **WHEN** the user runs sync with cross-adapter configuration - **THEN** state is mapped between adapters using canonical status - **AND** lossless round-trip preserves content #### Scenario: Sync with bundle integration + - **WHEN** sync is run within an OpenSpec bundle context - **THEN** synced items update bundle state and source tracking #### Scenario: Ceremony alias works + - **WHEN** the user runs `specfact backlog ceremony sync` - **THEN** the command forwards to `specfact backlog sync` diff --git a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-verify-readiness/spec.md b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-verify-readiness/spec.md index bf5b1568..748f1f2d 100644 --- a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-verify-readiness/spec.md +++ b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/specs/backlog-verify-readiness/spec.md @@ -7,11 +7,13 @@ The system SHALL provide `specfact backlog verify-readiness` for DoR validation. #### Scenario: Verify-readiness checks DoR criteria + - **WHEN** the user runs `specfact backlog verify-readiness --project-id ` - **THEN** each backlog item is validated against `.specfact/dor.yaml` - **AND** items passing/failing DoR are reported #### Scenario: DoR failures show actionable guidance + - **WHEN** an item fails DoR validation - **THEN** specific missing criteria are listed - **AND** remediation hints are provided diff --git a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/tasks.md b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/tasks.md index 74b509f1..d3c7a702 100644 --- a/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/tasks.md +++ b/openspec/changes/archive/2026-03-11-backlog-02-migrate-core-commands/tasks.md @@ -62,7 +62,7 @@ - [x] 7.3 Stage all changes: `git add -A` - [x] 7.4 Commit with GPG signing: `git commit -S -m "feat: migrate backlog-core commands to specfact-backlog bundle"` (with --no-verify for pre-commit hooks) - [x] 7.5 Push branch: `git push -u origin feature/backlog-02-migrate-core-commands` -- [x] 7.6 Create PR to `dev`: https://github.com/nold-ai/specfact-cli-modules/pull/32 +- [x] 7.6 Create PR to `dev`: ## 8. Cleanup (post-merge) diff --git a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/CHANGE_VALIDATION.md index 8ebe0b3b..0b32182e 100644 --- a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ ## Breaking Changes Detected None. This change is purely additive: + - New module package in specfact-cli-modules - No existing production code in specfact-cli is modified - `bundle_group_command: code` extends the existing group additively via `_merge_typer_apps` @@ -22,9 +23,11 @@ None. This change is purely additive: ## Dependencies Affected ### Critical Updates Required + None. ### Recommended Updates + None. ## Impact Assessment diff --git a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/design.md b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/design.md index 3f64df2a..9b113dd2 100644 --- a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/design.md +++ b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/design.md @@ -3,6 +3,7 @@ ## Architecture Overview The module follows the standard `specfact-cli-modules` package pattern: + - `module-package.yaml` defines the module metadata and `bundle_group_command: code` - `review/app.py` is the Typer extension entrypoint registered by the CLI registry - `run/findings.py` contains the Pydantic data models (`ReviewFinding`, `ReviewReport`) @@ -96,6 +97,7 @@ packages/specfact-code-review/ ## Contract Strategy All public APIs in this change receive: + - `@beartype` β€” runtime type enforcement - `@require` β€” preconditions on inputs - `@ensure` β€” postconditions on outputs diff --git a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/proposal.md b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/proposal.md index 9bf45a07..43a98002 100644 --- a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/proposal.md +++ b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/proposal.md @@ -20,6 +20,7 @@ This change establishes the foundation: the module package scaffold, the governa Violation categories: `clean_code`, `security`, `type_safety`, `contracts`, `testing`, `style`, `architecture`. Scoring algorithm: + ``` base_score = 100 Deductions: error/blocking=-15, error/fixable=-5, warning=-2, info=-1 @@ -53,6 +54,6 @@ reward_delta = score - 80 (range: -80..+20) - **GitHub Issue**: [#398](https://github.com/nold-ai/specfact-cli/issues/398) -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/398 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: in_progress diff --git a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/code-review-module/spec.md b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/code-review-module/spec.md index 0f751bbb..6b17936f 100644 --- a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/code-review-module/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/code-review-module/spec.md @@ -1,26 +1,31 @@ ## ADDED Requirements ### Requirement: Code Review Module Registration + The `nold-ai/specfact-code-review` module SHALL be installable and extend `specfact code` with a `review` subgroup exposing `run`, `ledger`, and `rules` subcommands. #### Scenario: Module install surfaces review subgroup + - **GIVEN** the module is installed via `specfact module install nold-ai/specfact-code-review` - **WHEN** the user runs `specfact code --help` - **THEN** a `review` subgroup appears in the command list - **AND** `specfact code review --help` shows `run`, `ledger`, and `rules` subcommands #### Scenario: module-package.yaml has required fields + - **GIVEN** `packages/specfact-code-review/module-package.yaml` exists - **WHEN** the module loader parses it - **THEN** `bundle_group_command` equals `code`, `tier` equals `official`, `name` equals `nold-ai/specfact-code-review` - **AND** `core_compatibility` matches `>=0.40.0,<1.0.0` #### Scenario: Module not installed produces no surface + - **GIVEN** the module is NOT installed - **WHEN** the user runs `specfact code --help` - **THEN** no `review` subgroup appears and no error is raised #### Scenario: Duplicate install is idempotent + - **GIVEN** the module is already installed - **WHEN** the user installs it again - **THEN** no duplicate `review` entries appear in `specfact code --help` diff --git a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-finding-model/spec.md b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-finding-model/spec.md index 44f9a80e..839be991 100644 --- a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-finding-model/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-finding-model/spec.md @@ -1,20 +1,24 @@ ## ADDED Requirements ### Requirement: ReviewFinding Pydantic Model + The system SHALL provide a `ReviewFinding` Pydantic BaseModel with validated `category`, `severity`, `tool`, `rule`, `file`, `line`, `message`, and `fixable` fields. #### Scenario: Valid ReviewFinding creates successfully + - **GIVEN** a dict with all required fields: category, severity, tool, rule, file, line, message - **WHEN** `ReviewFinding(**data)` is called - **THEN** a valid instance is returned with all fields populated - **AND** `fixable` defaults to `False` if not provided #### Scenario: Invalid severity is rejected + - **GIVEN** a dict with `severity="critical"` (not in the valid set) - **WHEN** `ReviewFinding(**data)` is called - **THEN** a `ValidationError` is raised #### Scenario: Valid severity and category values accepted + - **GIVEN** severity values `error`, `warning`, `info` and category values `clean_code`, `security`, `type_safety`, `contracts`, `testing`, `style`, `architecture`, `tool_error` - **WHEN** each is used in a `ReviewFinding` - **THEN** all are accepted without validation error diff --git a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-report-model/spec.md b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-report-model/spec.md index 8fd51d60..fc62b45c 100644 --- a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-report-model/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-report-model/spec.md @@ -1,30 +1,36 @@ ## ADDED Requirements ### Requirement: ReviewReport Governance-01 Compatible Envelope + The system SHALL provide a `ReviewReport` Pydantic model carrying standard governance-01 fields (`schema_version`, `run_id`, `timestamp`, `overall_verdict`, `ci_exit_code`) and review-specific extensions (`score`, `reward_delta`, `findings`, `summary`). #### Scenario: PASS verdict maps to governance-01 PASS + - **GIVEN** a run with `score=85` - **WHEN** `ReviewReport` is constructed - **THEN** `overall_verdict` equals `"PASS"` and `ci_exit_code` equals `0` - **AND** `reward_delta` equals `5` (85 - 80) #### Scenario: WARN verdict maps to PASS_WITH_ADVISORY + - **GIVEN** a run with `score=60` - **WHEN** `ReviewReport` is constructed - **THEN** `overall_verdict` equals `"PASS_WITH_ADVISORY"` and `ci_exit_code` equals `0` #### Scenario: BLOCK verdict maps to FAIL + - **GIVEN** a run with `score=45` - **WHEN** `ReviewReport` is constructed - **THEN** `overall_verdict` equals `"FAIL"` and `ci_exit_code` equals `1` #### Scenario: Blocking error forces FAIL regardless of score + - **GIVEN** a run with `score=75` but a finding with `severity=error` and `fixable=False` - **WHEN** `ReviewReport` is constructed - **THEN** `overall_verdict` equals `"FAIL"` and `ci_exit_code` equals `1` #### Scenario: Standard governance fields always present + - **GIVEN** any `ReviewReport` instance - **WHEN** its fields are inspected - **THEN** `schema_version`, `run_id`, `timestamp`, `overall_verdict`, `ci_exit_code` are all non-null diff --git a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-scorer/spec.md b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-scorer/spec.md index 5d8a0c46..0f30d0f7 100644 --- a/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-scorer/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-01-module-scaffold/specs/review-scorer/spec.md @@ -1,29 +1,35 @@ ## ADDED Requirements ### Requirement: Review Scoring Algorithm + The system SHALL compute `score` (0-120) and `reward_delta = score - 80` from findings and bonus conditions. Base score is 100; deductions: blocking error=-15, fixable error=-5, warning=-2, info=-1; bonuses: +5 each for five conditions. #### Scenario: Single blocking error deducts 15 + - **GIVEN** one finding with `severity=error`, `fixable=False` - **WHEN** score is computed with no bonuses - **THEN** `score` equals `85` and `reward_delta` equals `5` #### Scenario: Auto-fixable error deducts 5 + - **GIVEN** one finding with `severity=error`, `fixable=True` - **WHEN** score is computed - **THEN** `score` equals `95` and `reward_delta` equals `15` #### Scenario: Warnings deduct 2 each + - **GIVEN** three findings with `severity=warning` - **WHEN** score is computed - **THEN** `score` equals `94` #### Scenario: Verdict thresholds applied correctly + - **GIVEN** scores of 85, 60, and 45 - **WHEN** verdict is determined - **THEN** score 85 β†’ `"PASS"`, score 60 β†’ `"PASS_WITH_ADVISORY"`, score 45 β†’ `"FAIL"` #### Scenario: Score capped at 120 + - **GIVEN** conditions that would produce a score exceeding 120 - **WHEN** score is computed - **THEN** `score` equals `120` diff --git a/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/CHANGE_VALIDATION.md index 9a43a6df..07acb3e5 100644 --- a/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ ## Breaking Changes Detected None. This change is purely additive: + - New module package in specfact-cli-modules - No existing production code in specfact-cli is modified - `bundle_group_command: code` extends the existing group additively via `_merge_typer_apps` @@ -22,9 +23,11 @@ None. This change is purely additive: ## Dependencies Affected ### Critical Updates Required + None. ### Recommended Updates + None. ## Impact Assessment diff --git a/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/design.md b/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/design.md index 10ceeecd..8e4bf701 100644 --- a/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/design.md +++ b/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/design.md @@ -3,6 +3,7 @@ ## Runner Pattern Both runners follow the same pattern: + 1. Accept a `files: list[Path]` parameter (validated by `@require`) 2. Invoke the external tool via `subprocess.run` with JSON output 3. Parse the JSON output @@ -19,6 +20,7 @@ data = json.loads(result.stdout) ``` Rule prefix β†’ category mapping: + ```python RULE_CATEGORY_MAP = { "S": "security", # Bandit security rules @@ -41,6 +43,7 @@ data = json.loads(result.stdout) ``` For each function block with complexity > 12: + ```python severity = "warning" if complexity <= 15 else "error" ``` @@ -48,6 +51,7 @@ severity = "warning" if complexity <= 15 else "error" ## Test Strategy (TDD-first) Unit tests mock `subprocess.run` to return fixture JSON: + - `test_ruff_runner.py`: fixture outputs for S603, C901, E501; test category mapping, filter, tool_error - `test_radon_runner.py`: fixture outputs for complexity 13, 16, 10; test severity thresholds diff --git a/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/specs/radon-runner/spec.md b/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/specs/radon-runner/spec.md index 4736bd63..adf75e56 100644 --- a/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/specs/radon-runner/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/specs/radon-runner/spec.md @@ -1,24 +1,29 @@ ## ADDED Requirements ### Requirement: Radon Cyclomatic Complexity Extraction + The system SHALL invoke `radon cc -j` on provided files and produce severity-tiered findings for functions exceeding complexity thresholds (12-15 β†’ warning, >15 β†’ error). #### Scenario: Function with complexity 13 produces warning + - **GIVEN** radon output shows a function with cyclomatic complexity 13 - **WHEN** `run_radon(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `severity="warning"` and `category="clean_code"` #### Scenario: Function with complexity 16 produces error + - **GIVEN** radon output shows a function with cyclomatic complexity 16 - **WHEN** `run_radon(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `severity="error"` and `category="clean_code"` #### Scenario: Function with complexity 12 or below produces no finding + - **GIVEN** all functions have complexity <= 12 - **WHEN** `run_radon(files=[...])` is called - **THEN** no findings are returned #### Scenario: Radon parse error produces tool_error finding + - **GIVEN** radon returns unparseable output - **WHEN** `run_radon(files=[...])` is called - **THEN** one `ReviewFinding` with `category="tool_error"` is returned diff --git a/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/specs/ruff-runner/spec.md b/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/specs/ruff-runner/spec.md index 5e800956..0aad83f6 100644 --- a/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/specs/ruff-runner/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-02-ruff-radon-runners/specs/ruff-runner/spec.md @@ -1,24 +1,29 @@ ## ADDED Requirements ### Requirement: Ruff Finding Extraction Mapped to ReviewFinding + The system SHALL invoke `ruff check --output-format json` on provided files and map rule prefixes to categories: `S*` β†’ security, `C9*` β†’ clean_code, `E/F/I*` β†’ style. #### Scenario: Bandit S-rules map to security category + - **GIVEN** ruff output contains a finding with rule `S603` - **WHEN** `run_ruff(files=[...])` is called - **THEN** the returned `ReviewFinding` has `category="security"` and `tool="ruff"` #### Scenario: C90 complexity rules map to clean_code + - **GIVEN** ruff output contains a finding with rule `C901` - **WHEN** `run_ruff(files=[...])` is called - **THEN** the finding has `category="clean_code"` #### Scenario: Only provided files are scanned + - **GIVEN** a file list `[file_a.py]` - **WHEN** `run_ruff(files=[file_a.py])` is called - **THEN** ruff is invoked with only `file_a.py` and no other files appear in findings #### Scenario: Ruff parse error or unavailability produces tool_error finding + - **GIVEN** ruff returns unparseable output or is not installed - **WHEN** `run_ruff(files=[...])` is called - **THEN** one `ReviewFinding` with `category="tool_error"` is returned and no exception propagates diff --git a/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/CHANGE_VALIDATION.md index 9f00df8c..78e2388c 100644 --- a/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ ## Breaking Changes Detected None. This change is purely additive: + - New module package in specfact-cli-modules - No existing production code in specfact-cli is modified - `bundle_group_command: code` extends the existing group additively via `_merge_typer_apps` @@ -22,9 +23,11 @@ None. This change is purely additive: ## Dependencies Affected ### Critical Updates Required + None. ### Recommended Updates + None. ## Impact Assessment diff --git a/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/design.md b/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/design.md index 44587a19..d1bef8ff 100644 --- a/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/design.md +++ b/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/design.md @@ -9,6 +9,7 @@ data = json.loads(result.stdout) ``` basedpyright JSON output format: + ```json { "generalDiagnostics": [ @@ -31,6 +32,7 @@ data = json.loads(result.stdout) pylint JSON: list of dicts with `{"message-id": "W0702", "path": "...", "line": N, "message": "..."}`. Message ID β†’ category mapping: + ```python PYLINT_CATEGORY_MAP = { "W0702": "architecture", # bare-except @@ -49,5 +51,6 @@ Both runners run on the full project for accuracy but filter results to return o ## Test Strategy Tests mock `subprocess.run` return values. Key test cases: + - basedpyright: error diagnostic, warning diagnostic, non-provided file filtered out - pylint: W0702 β†’ architecture, W0703 β†’ architecture, unknown ID β†’ style diff --git a/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/specs/basedpyright-runner/spec.md b/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/specs/basedpyright-runner/spec.md index 038002ca..c189c8c0 100644 --- a/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/specs/basedpyright-runner/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/specs/basedpyright-runner/spec.md @@ -1,20 +1,24 @@ ## ADDED Requirements ### Requirement: basedpyright Type-Safety Finding Extraction + The system SHALL parse `basedpyright --outputjson` output and map all diagnostics to `category="type_safety"`, filtered to the provided changed files only. #### Scenario: Type error maps to type_safety finding + - **GIVEN** basedpyright JSON output with a type error in `file_a.py` - **WHEN** `run_basedpyright(files=[file_a.py])` is called - **THEN** a `ReviewFinding` is returned with `category="type_safety"`, `tool="basedpyright"`, `severity="error"` - **AND** `file` and `line` are correctly populated #### Scenario: Only changed files are reported + - **GIVEN** basedpyright errors in multiple files but only `file_a.py` is in the provided list - **WHEN** `run_basedpyright(files=[file_a.py])` is called - **THEN** only findings from `file_a.py` are returned #### Scenario: basedpyright unavailable produces tool_error + - **GIVEN** basedpyright binary is unavailable - **WHEN** `run_basedpyright(files=[...])` is called - **THEN** one `ReviewFinding` with `category="tool_error"` is returned diff --git a/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/specs/pylint-runner/spec.md b/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/specs/pylint-runner/spec.md index 1cb2067a..add12929 100644 --- a/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/specs/pylint-runner/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-03-type-governance-runners/specs/pylint-runner/spec.md @@ -1,24 +1,29 @@ ## ADDED Requirements ### Requirement: pylint Architecture Smell Extraction + The system SHALL invoke pylint on provided files and map message IDs to violation categories: `W0702`, `W0703` β†’ architecture (bare-except, broad-except). #### Scenario: Bare except maps to architecture category + - **GIVEN** pylint output with message id `W0702` - **WHEN** `run_pylint(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `category="architecture"` and `tool="pylint"` #### Scenario: W0703 broad-except maps to architecture + - **GIVEN** pylint output with message id `W0703` - **WHEN** `run_pylint(files=[...])` is called - **THEN** finding has `category="architecture"` #### Scenario: Only changed files are filtered + - **GIVEN** a file list `[file_a.py]` - **WHEN** `run_pylint(files=[file_a.py])` is called - **THEN** only findings for `file_a.py` are returned #### Scenario: pylint parse error produces tool_error + - **GIVEN** pylint returns unparseable output - **WHEN** `run_pylint(files=[...])` is called - **THEN** one `ReviewFinding` with `category="tool_error"` is returned diff --git a/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/CHANGE_VALIDATION.md index ba4e5fcf..9d1d1e6f 100644 --- a/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ ## Breaking Changes Detected None. This change is purely additive: + - New module package in specfact-cli-modules - No existing production code in specfact-cli is modified - `bundle_group_command: code` extends the existing group additively via `_merge_typer_apps` @@ -22,9 +23,11 @@ None. This change is purely additive: ## Dependencies Affected ### Critical Updates Required + None. ### Recommended Updates + None. ## Impact Assessment diff --git a/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/design.md b/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/design.md index 1ea01b29..e8288908 100644 --- a/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/design.md +++ b/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/design.md @@ -3,6 +3,7 @@ ## contract_runner.py β€” AST Scan Uses Python's `ast` module to scan each provided file. For each function definition at module/class level: + 1. Check if the function name starts with `_` β†’ skip (private) 2. Check decorator list for `@require` or `@ensure` (from `icontract`) β†’ skip if present 3. If no icontract decorator found β†’ emit `ReviewFinding(category="contracts", severity="warning", rule="MISSING_ICONTRACT")` @@ -55,6 +56,7 @@ def run_review(files: list[Path], options: ReviewOptions) -> ReviewReport: ## TDD Gate Test file discovery: + ```python def expected_test_path(src_file: Path) -> Path: # src/specfact_code_review/run/scorer.py diff --git a/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/specs/contract-runner/spec.md b/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/specs/contract-runner/spec.md index 880b1b99..7ca31b49 100644 --- a/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/specs/contract-runner/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/specs/contract-runner/spec.md @@ -1,29 +1,35 @@ ## ADDED Requirements ### Requirement: icontract Decorator AST Scan and CrossHair Fast Pass + The system SHALL AST-scan changed Python files for public functions missing `@require`/`@ensure` decorators, and run CrossHair (2s/path timeout) for counterexample discovery. #### Scenario: Public function without @require produces contracts finding + - **GIVEN** a Python file with `def process_data(x):` without icontract decorators - **WHEN** `run_contract_check(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `category="contracts"` and `severity="warning"` #### Scenario: Public function with decorators produces no finding + - **GIVEN** a file with a public function decorated with both `@require` and `@ensure` - **WHEN** `run_contract_check(files=[...])` is called - **THEN** no contract-related finding is returned for that function #### Scenario: Private functions excluded from scan + - **GIVEN** a file with `def _private_helper(x):` without decorators - **WHEN** `run_contract_check(files=[...])` is called - **THEN** no finding is produced for `_private_helper` #### Scenario: CrossHair counterexample maps to contracts warning + - **GIVEN** CrossHair finds a counterexample for a function - **WHEN** `run_contract_check(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `category="contracts"`, `severity="warning"`, `tool="crosshair"` #### Scenario: CrossHair timeout or unavailability degrades gracefully + - **GIVEN** CrossHair hits the 2s timeout or is not installed - **WHEN** `run_contract_check(files=[...])` is called - **THEN** the AST scan still runs and no exception propagates diff --git a/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/specs/test-tdd-gate/spec.md b/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/specs/test-tdd-gate/spec.md index 9921ab79..e52e5150 100644 --- a/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/specs/test-tdd-gate/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-04-contract-test-runners/specs/test-tdd-gate/spec.md @@ -1,30 +1,36 @@ ## ADDED Requirements ### Requirement: TDD Gate Enforcing Test File Existence and Coverage Threshold + The system SHALL block the review if any changed `src/` file has no corresponding test file, or if tests fail, or if coverage is below 80%. #### Scenario: Changed src file with no test file produces BLOCK + - **GIVEN** a changed file `src/specfact_code_review/run/scorer.py` with no corresponding test file - **WHEN** the TDD gate runs - **THEN** a `ReviewFinding` with `rule="TEST_FILE_MISSING"`, `severity="error"`, `category="testing"` is returned - **AND** the overall verdict is forced to BLOCK #### Scenario: Passing tests with coverage >= 80% produces no finding + - **GIVEN** a changed file with a corresponding test file and 85% coverage - **WHEN** the TDD gate runs - **THEN** no testing finding is returned #### Scenario: Test failure produces BLOCK finding + - **GIVEN** a changed file with failing tests - **WHEN** the TDD gate runs - **THEN** a `ReviewFinding` with `severity="error"` and `category="testing"` is returned #### Scenario: Coverage below 80% produces warning + - **GIVEN** passing tests with 65% coverage - **WHEN** the TDD gate runs - **THEN** a `ReviewFinding` with `severity="warning"` and `category="testing"` is returned #### Scenario: --no-tests flag skips TDD gate + - **GIVEN** `--no-tests` is passed to `specfact code review run` - **WHEN** the runner executes - **THEN** no TDD gate check is performed and no testing findings are returned diff --git a/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/CHANGE_VALIDATION.md index 4c67dc07..449c77c2 100644 --- a/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ ## Breaking Changes Detected None. This change is purely additive: + - New module package in specfact-cli-modules - No existing production code in specfact-cli is modified - `bundle_group_command: code` extends the existing group additively via `_merge_typer_apps` @@ -22,9 +23,11 @@ None. This change is purely additive: ## Dependencies Affected ### Critical Updates Required + None. ### Recommended Updates + None. ## Impact Assessment diff --git a/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/design.md b/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/design.md index a5d9986a..a7b7fd54 100644 --- a/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/design.md +++ b/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/design.md @@ -11,6 +11,7 @@ data = json.loads(result.stdout) Filter results to provided file list. Map each semgrep result to `ReviewFinding`. Rule ID β†’ category mapping: + ```python SEMGREP_RULE_CATEGORY = { "get-modify-same-method": "clean_code", @@ -44,6 +45,7 @@ rules: ## Fixture Files For each rule, two fixture files: + - `bad_.py` β€” contains the anti-pattern; semgrep MUST report a finding - `good_.py` β€” clean equivalent; semgrep MUST NOT report a finding diff --git a/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/proposal.md b/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/proposal.md index 33fc283b..c6348415 100644 --- a/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/proposal.md +++ b/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/proposal.md @@ -6,7 +6,7 @@ Ruff and pylint cover well-known generic patterns, but several project-specific - **get+modify in same method** β€” violates single-responsibility; not detectable by ruff - **Unguarded nested attribute access** (`a.b.c` without None guard) β€” silent NoneAttributeError risk -- **Cross-layer calls** (repository.* + http_client.* in same function) β€” architecture boundary violation +- **Cross-layer calls** (repository.*+ http_client.* in same function) β€” architecture boundary violation - **Module-level network instantiation** β€” side effects at import time - **print() in src/** β€” complement to ruff T201 for cases ruff misses with complex expressions diff --git a/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/specs/clean-code-semgrep-rules/spec.md b/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/specs/clean-code-semgrep-rules/spec.md index 3fc7bed3..b99b6b6b 100644 --- a/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/specs/clean-code-semgrep-rules/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/specs/clean-code-semgrep-rules/spec.md @@ -1,29 +1,35 @@ ## ADDED Requirements ### Requirement: Five Custom Semgrep Rules for Project-Specific Anti-Patterns + The system SHALL provide five semgrep rules covering get+modify in same method, unguarded nested attribute access, cross-layer calls, module-level network instantiation, and print() in src/. Each rule SHALL be validated with bad/good fixture pairs. #### Scenario: get+modify rule fires on combined read/write method + - **GIVEN** `bad_get_modify.py` contains a method that both reads and writes state - **WHEN** semgrep runs the `get-modify-same-method` rule on that file - **THEN** at least one match is reported #### Scenario: get+modify rule does not fire on separated methods + - **GIVEN** `good_get_modify.py` separates read and write into different methods - **WHEN** semgrep runs the rule - **THEN** no match is reported #### Scenario: Nested attribute access rule fires on unguarded a.b.c + - **GIVEN** `bad_nested_access.py` contains `result = obj.config.value` without None-check - **WHEN** semgrep runs the `unguarded-nested-access` rule - **THEN** a match is reported #### Scenario: Cross-layer rule fires on mixed repository and http_client calls + - **GIVEN** a function calls both `repository.find_by_id(...)` and `http_client.post(...)` - **WHEN** semgrep runs the `cross-layer-call` rule - **THEN** a match is reported #### Scenario: print-in-src rule fires on print() in src/ files + - **GIVEN** a file in `src/` contains `print("debug")` - **WHEN** semgrep runs the `print-in-src` rule - **THEN** a match is reported diff --git a/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/specs/semgrep-runner/spec.md b/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/specs/semgrep-runner/spec.md index 32c9b183..4a12f08a 100644 --- a/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/specs/semgrep-runner/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-05-semgrep-clean-code-rules/specs/semgrep-runner/spec.md @@ -1,24 +1,29 @@ ## ADDED Requirements ### Requirement: Project-Specific Semgrep Rule Execution and Finding Extraction + The system SHALL invoke semgrep with the project-specific clean_code ruleset and map findings to `List[ReviewFinding]`, filtered to the provided file list. #### Scenario: Semgrep finding maps to ReviewFinding + - **GIVEN** semgrep output with a match on the `get-modify-same-method` rule - **WHEN** `run_semgrep(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `tool="semgrep"` and `category="clean_code"` #### Scenario: Non-provided files filtered out + - **GIVEN** semgrep finds matches in `file_a.py` and `file_b.py`, only `file_a.py` provided - **WHEN** `run_semgrep(files=[file_a.py])` is called - **THEN** only findings from `file_a.py` are returned #### Scenario: Semgrep unavailable produces tool_error + - **GIVEN** semgrep binary is unavailable - **WHEN** `run_semgrep(files=[...])` is called - **THEN** one `ReviewFinding` with `category="tool_error"` is returned and no exception propagates #### Scenario: Clean file produces no findings + - **GIVEN** a file that matches none of the 5 custom rules - **WHEN** `run_semgrep(files=[file])` is called - **THEN** an empty list is returned diff --git a/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/CHANGE_VALIDATION.md index d01f12cd..ef4c4007 100644 --- a/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ ## Breaking Changes Detected None. This change is purely additive: + - New module package in specfact-cli-modules - No existing production code in specfact-cli is modified - `bundle_group_command: code` extends the existing group additively via `_merge_typer_apps` @@ -22,9 +23,11 @@ None. This change is purely additive: ## Dependencies Affected ### Critical Updates Required + None. ### Recommended Updates + None. ## Impact Assessment diff --git a/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/proposal.md b/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/proposal.md index 0f9a5710..34f6713e 100644 --- a/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/proposal.md +++ b/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/proposal.md @@ -39,6 +39,6 @@ A local JSON fallback (`~/.specfact/ledger.json`) ensures the feature works offl - **GitHub Issue**: #395 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/395 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: in-progress diff --git a/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/specs/ledger-commands/spec.md b/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/specs/ledger-commands/spec.md index c0eda9f2..0e5cf702 100644 --- a/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/specs/ledger-commands/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/specs/ledger-commands/spec.md @@ -1,29 +1,35 @@ ## ADDED Requirements ### Requirement: Ledger CLI Subcommands for Update, Status, and Reset + The system SHALL provide `specfact code review ledger update|status|reset` subcommands for managing the reward ledger from the terminal. #### Scenario: ledger update reads ReviewReport JSON from stdin + - **GIVEN** `specfact code review run --json` output piped to `specfact code review ledger update` - **WHEN** the command executes - **THEN** `LedgerClient.record_run` is called with the parsed `ReviewReport` and exit code is 0 #### Scenario: ledger update with invalid JSON exits with error + - **GIVEN** invalid JSON is provided on stdin - **WHEN** `specfact code review ledger update` runs - **THEN** an error message is printed to stderr and exit code is 1 #### Scenario: ledger status prints current state + - **GIVEN** the ledger has `coins=7.3`, `streak_pass=2`, `last_verdict="PASS"` - **WHEN** `specfact code review ledger status` runs - **THEN** output includes `7.30` coins, `2` pass streak, and `PASS` last verdict #### Scenario: ledger reset without --confirm refuses deletion + - **GIVEN** `specfact code review ledger reset` is run without `--confirm` - **WHEN** the command executes - **THEN** an error message asking for `--confirm` is printed and nothing is deleted #### Scenario: ledger reset with --confirm clears local ledger + - **GIVEN** `specfact code review ledger reset --confirm` is run - **WHEN** the command executes - **THEN** `~/.specfact/ledger.json` is cleared and exit code is 0 diff --git a/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/specs/reward-ledger/spec.md b/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/specs/reward-ledger/spec.md index c31d0064..67382071 100644 --- a/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/specs/reward-ledger/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-06-reward-ledger/specs/reward-ledger/spec.md @@ -1,29 +1,35 @@ ## ADDED Requirements ### Requirement: Supabase Reward Ledger with Offline JSON Fallback + The system SHALL persist review run results to Supabase `ai_sync.review_runs` and accumulate coins/streaks in `ai_sync.reward_ledger`, with a local `~/.specfact/ledger.json` fallback when Supabase is unavailable. #### Scenario: Record run stores data in Supabase when available + - **GIVEN** a `ReviewReport` with `score=85`, `reward_delta=5`, `verdict="PASS"` and Supabase is reachable - **WHEN** `LedgerClient.record_run(report)` is called - **THEN** a row is inserted into `ai_sync.review_runs` and `ai_sync.reward_ledger` is updated with `coins += 0.5` and `streak_pass` incremented #### Scenario: Record run writes to local JSON when Supabase unavailable + - **GIVEN** `SUPABASE_URL` is not set - **WHEN** `LedgerClient.record_run(report)` is called - **THEN** the run is appended to `~/.specfact/ledger.json` and no exception is raised #### Scenario: Streak pass bonus applied at streak >= 5 + - **GIVEN** the agent has `streak_pass=4` and a new PASS run occurs - **WHEN** the ledger updates - **THEN** `streak_pass` becomes `5` and an additional `+0.5` coins is added #### Scenario: Streak block penalty applied at streak >= 3 + - **GIVEN** the agent has `streak_block=2` and a new BLOCK run occurs - **WHEN** the ledger updates - **THEN** `streak_block` becomes `3` and `-1.0` coins is deducted #### Scenario: Ledger status returns correct state + - **GIVEN** `cumulative_coins=12.5`, `streak_pass=3`, `last_verdict="PASS"` - **WHEN** `LedgerClient.get_status()` is called - **THEN** the returned dict includes `coins=12.5`, `streak_pass=3`, `last_verdict="PASS"` diff --git a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/CHANGE_VALIDATION.md index 68dc9d35..a21c5064 100644 --- a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ ## Breaking Changes Detected None. This change is purely additive: + - New module package in specfact-cli-modules - No existing production code in specfact-cli is modified - `bundle_group_command: code` extends the existing group additively via `_merge_typer_apps` @@ -22,9 +23,11 @@ None. This change is purely additive: ## Dependencies Affected ### Critical Updates Required + None. ### Recommended Updates + None. ## Impact Assessment diff --git a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/design.md b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/design.md index 9a527451..024a200c 100644 --- a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/design.md +++ b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/design.md @@ -64,10 +64,12 @@ def update_house_rules(skill_path: Path, runs: list[RunRecord]) -> None: ## File Locations In `specfact-cli` repo: + - `skills/specfact-code-review/SKILL.md` β€” the installable skill file - `.cursor/rules/house_rules.mdc` β€” Cursor mirror (auto-maintained) In `specfact-code-review` module (`specfact-cli-modules`): + - `rules/updater.py` β€” update algorithm - `rules/commands.py` β€” show/update/init CLI subcommands diff --git a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/proposal.md b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/proposal.md index 5cac87dd..c20b4f25 100644 --- a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/proposal.md +++ b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/proposal.md @@ -7,6 +7,7 @@ The review findings and ledger data are only useful if they influence future cod This change follows the `ai-integration-01` open Agent Skills standard β€” skill file at `skills/specfact-code-review/SKILL.md` in specfact-cli, installable via `specfact ide skill install --type code-review`. **ALIGNMENT NOTES (from overlap analysis):** + - Skill file lives at `skills/specfact-code-review/SKILL.md` (not `.claude/skills/`) β€” follows ai-integration-01 standard - Does NOT modify `CLAUDE.md` directly β€” that is owned by `ai-integration-03` - house_rules content is the dynamic body; delivery mechanism follows ai-integration-01 diff --git a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/house-rules-skill/spec.md b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/house-rules-skill/spec.md index 9733c0b2..40b99ff2 100644 --- a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/house-rules-skill/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/house-rules-skill/spec.md @@ -1,29 +1,35 @@ ## ADDED Requirements ### Requirement: ai-integration-01 Compliant House Rules Skill File + The system SHALL provide `skills/specfact-code-review/SKILL.md` with valid ai-integration-01 YAML frontmatter, DO/DON'T rules, and an auto-managed TOP VIOLATIONS section, within a 35-line hard cap. #### Scenario: SKILL.md has valid ai-integration-01 YAML frontmatter + - **GIVEN** `skills/specfact-code-review/SKILL.md` exists - **WHEN** the frontmatter is parsed - **THEN** `name`, `description`, and `allowed-tools` fields are present and valid #### Scenario: Skill file is within 35 line cap + - **GIVEN** the SKILL.md in its default or updated state - **WHEN** line count is measured - **THEN** line count is at most 35 #### Scenario: TOP VIOLATIONS section is auto-managed and other sections preserved + - **GIVEN** SKILL.md contains the auto-managed TOP VIOLATIONS marker - **WHEN** `specfact code review rules update` runs - **THEN** only the TOP VIOLATIONS section is modified and DO/DON'T sections are unchanged #### Scenario: SKILL.md creation does not modify CLAUDE.md + - **GIVEN** `specfact code review rules init` is run - **WHEN** all files created or modified are inspected - **THEN** `CLAUDE.md` is not in the list of modified files #### Scenario: rules init creates default SKILL.md for new project + - **GIVEN** no `skills/specfact-code-review/SKILL.md` exists - **WHEN** `specfact code review rules init` is run - **THEN** SKILL.md is created with default DO/DON'T rules within 35 lines diff --git a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/house-rules-updater/spec.md b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/house-rules-updater/spec.md index eafd223c..2ab88060 100644 --- a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/house-rules-updater/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/house-rules-updater/spec.md @@ -1,34 +1,41 @@ ## ADDED Requirements ### Requirement: House Rules Auto-Update Algorithm with Frequency Thresholds and Line Cap + The system SHALL implement an update algorithm that surfaces rules with >= 3 hits in last 20 runs, prunes rules with 0 hits for 10+ consecutive runs, enforces a 35-line hard cap, and increments the version header. #### Scenario: Rule appearing 3+ times in last 20 runs is surfaced + - **GIVEN** ledger data showing rule `C901` appeared 5 times in the last 20 runs - **WHEN** `update_house_rules(skill_path, ledger_data)` is called - **THEN** `C901` is present in the TOP VIOLATIONS section of the updated SKILL.md #### Scenario: Rule appearing fewer than 3 times is not surfaced + - **GIVEN** rule `T201` appeared only twice in the last 20 runs - **WHEN** `update_house_rules(...)` is called - **THEN** `T201` is NOT added to TOP VIOLATIONS #### Scenario: Rule with 0 hits for 10 consecutive runs is pruned + - **GIVEN** SKILL.md lists `W0702` in TOP VIOLATIONS and `W0702` has 0 hits in last 10 runs - **WHEN** `update_house_rules(...)` is called - **THEN** `W0702` is removed from TOP VIOLATIONS #### Scenario: Version header increments on each update + - **GIVEN** SKILL.md has `# House Rules β€” AI Coding Context (v3)` - **WHEN** `update_house_rules(...)` is called - **THEN** the version becomes `v4` and the `Updated:` timestamp reflects the current date #### Scenario: 35 line cap enforced by pruning lowest-frequency entries + - **GIVEN** updating would exceed the 35-line budget - **WHEN** `update_house_rules(...)` is called - **THEN** lowest-frequency entries are removed first and the result is at most 35 lines #### Scenario: DO and DON'T sections unchanged after update + - **GIVEN** DO and DON'T sections have specific content - **WHEN** `update_house_rules(...)` is called - **THEN** DO and DON'T sections remain byte-identical to their pre-update state diff --git a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/rules-commands/spec.md b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/rules-commands/spec.md index 260fab0c..d53ca22f 100644 --- a/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/rules-commands/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-07-house-rules-skill/specs/rules-commands/spec.md @@ -1,24 +1,29 @@ ## ADDED Requirements ### Requirement: House Rules CLI Subcommands for Show, Update, and Init + The system SHALL provide `specfact code review rules show|update|init` subcommands for managing the house rules skill file. #### Scenario: rules show prints current SKILL.md content + - **GIVEN** `skills/specfact-code-review/SKILL.md` exists - **WHEN** `specfact code review rules show` is run - **THEN** the full content is printed to stdout and exit code is 0 #### Scenario: rules show with missing SKILL.md prints helpful error + - **GIVEN** no SKILL.md exists at the expected path - **WHEN** `specfact code review rules show` is run - **THEN** an error message suggesting `rules init` is printed and exit code is 1 #### Scenario: rules update re-derives TOP VIOLATIONS from ledger + - **GIVEN** the ledger has 20 runs with violation `C901` appearing 5 times - **WHEN** `specfact code review rules update` is run - **THEN** `C901` appears in TOP VIOLATIONS and the version header is incremented #### Scenario: rules init creates default skill for new project + - **GIVEN** no SKILL.md exists - **WHEN** `specfact code review rules init` is run - **THEN** SKILL.md is created with default content and exit code is 0 diff --git a/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/CHANGE_VALIDATION.md index f6caadff..6111573d 100644 --- a/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ ## Breaking Changes Detected None. This change is purely additive: + - New module package in specfact-cli-modules - No existing production code in specfact-cli is modified - `bundle_group_command: code` extends the existing group additively via `_merge_typer_apps` @@ -22,9 +23,11 @@ None. This change is purely additive: ## Dependencies Affected ### Critical Updates Required + None. ### Recommended Updates + None. ## Impact Assessment diff --git a/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/proposal.md b/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/proposal.md index d47254cc..79d8b55a 100644 --- a/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/proposal.md +++ b/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/proposal.md @@ -21,6 +21,7 @@ Without this change, the module has no working entry point β€” all runner SPs ar - **NEW**: `tests/cli-contracts/specfact-code-review-rules.scenarios.yaml` (cli-val-01 format) **Exit code semantics (cli-val-05 aligned):** + - `ci_exit_code=0` β†’ PASS or WARN (advisory) - `ci_exit_code=1` β†’ BLOCK (hard gate) diff --git a/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/specs/review-cli-contracts/spec.md b/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/specs/review-cli-contracts/spec.md index 943c31e9..80ffe7b0 100644 --- a/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/specs/review-cli-contracts/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/specs/review-cli-contracts/spec.md @@ -1,24 +1,29 @@ ## ADDED Requirements ### Requirement: cli-val-01 Scenario YAML Files for All Three Command Groups + The system SHALL provide cli-val-01 compliant scenario YAML files for `specfact code review run`, `ledger`, and `rules` command groups. #### Scenario: review-run scenarios cover happy path and anti-patterns + - **GIVEN** `tests/cli-contracts/specfact-code-review-run.scenarios.yaml` exists - **WHEN** parsed against the cli-val-01 schema - **THEN** it contains at least one happy-path scenario (exit 0 on clean file) and one anti-pattern scenario #### Scenario: ledger scenarios cover pipe flow, status, and reset guard + - **GIVEN** `tests/cli-contracts/specfact-code-review-ledger.scenarios.yaml` exists - **WHEN** parsed - **THEN** it covers `ledger update` happy path, `ledger update` invalid JSON anti-pattern, `ledger status` happy path, and `ledger reset` missing --confirm anti-pattern #### Scenario: rules scenarios cover all three subcommands + - **GIVEN** `tests/cli-contracts/specfact-code-review-rules.scenarios.yaml` exists - **WHEN** parsed - **THEN** it covers `rules show`, `rules update`, and `rules init` happy paths plus error cases #### Scenario: All scenario files conform to cli-val-01 schema + - **GIVEN** all three scenario YAML files - **WHEN** validated against the cli-val-01 behavior contract schema - **THEN** no validation errors are reported diff --git a/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/specs/review-run-command/spec.md b/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/specs/review-run-command/spec.md index 7d896f08..05eb0bc1 100644 --- a/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/specs/review-run-command/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-08-review-run-integration/specs/review-run-command/spec.md @@ -1,34 +1,41 @@ ## ADDED Requirements ### Requirement: End-to-End specfact code review run Command + The system SHALL provide a fully wired `specfact code review run` command that orchestrates all tool runners and returns a `ReviewReport` with correct exit codes (0=PASS/WARN, 1=BLOCK). #### Scenario: Run on clean fixture produces PASS and exit 0 + - **GIVEN** `tests/fixtures/review/clean_module.py` with no violations and passing tests - **WHEN** `specfact code review run tests/fixtures/review/clean_module.py` is called - **THEN** `overall_verdict` equals `"PASS"` and exit code is `0` #### Scenario: Run on dirty fixture produces BLOCK and exit 1 + - **GIVEN** `tests/fixtures/review/dirty_module.py` with violations and missing test file - **WHEN** `specfact code review run tests/fixtures/review/dirty_module.py` is called - **THEN** `overall_verdict` equals `"FAIL"` and exit code is `1` #### Scenario: --json outputs valid ReviewReport JSON + - **GIVEN** any set of files - **WHEN** `specfact code review run --json` is called - **THEN** stdout contains valid JSON parseable as `ReviewReport` with all governance fields present #### Scenario: --score-only prints only reward_delta integer + - **GIVEN** a run with `reward_delta=-5` - **WHEN** `specfact code review run --score-only` is called - **THEN** stdout contains exactly `-5` followed by a newline #### Scenario: --fix applies ruff autofix then re-runs + - **GIVEN** files with auto-fixable ruff violations - **WHEN** `specfact code review run --fix` is called - **THEN** `ruff --fix` is applied and the review runs again on the fixed files #### Scenario: No files provided uses git diff HEAD + - **GIVEN** no `FILES` argument is provided - **WHEN** `specfact code review run` is called - **THEN** changed files are determined from `git diff HEAD --name-only` and the run proceeds diff --git a/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/design.md b/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/design.md index 4b045d86..13aac74e 100644 --- a/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/design.md +++ b/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/design.md @@ -17,6 +17,7 @@ explicit for local/offline use. ## Goals / Non-Goals **Goals:** + - Gate commits in this repository on `specfact code review run` - Keep the gate scoped to relevant staged files so it is fast enough for local use @@ -27,6 +28,7 @@ explicit for local/offline use. while allowing optional configured backends **Non-Goals:** + - Implementing or validating external `n8n` workflows - Adding new review scoring semantics - Making Supabase mandatory for local review-gate adoption diff --git a/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/proposal.md b/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/proposal.md index 8affb59b..85b13c4c 100644 --- a/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/proposal.md +++ b/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/proposal.md @@ -58,6 +58,6 @@ default with optional backend persistence when configured. - **GitHub Issue**: #393 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/393 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: synced after rewrite diff --git a/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/portable-review-adoption/spec.md b/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/portable-review-adoption/spec.md index 16defcf7..5ee1fa43 100644 --- a/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/portable-review-adoption/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/portable-review-adoption/spec.md @@ -1,27 +1,32 @@ ## ADDED Requirements ### Requirement: Portable Review-Gate Adoption Guidance + The system SHALL document how to add the same `specfact code review run` pre-commit gate to other projects, including optional house-rules workflow usage and the default local JSON ledger behavior with optional configured backend support. #### Scenario: Documentation shows a reusable pre-commit configuration + - **GIVEN** a developer wants to add code review gating to another project - **WHEN** they read the code-review module documentation - **THEN** they can copy a concrete pre-commit configuration that runs `specfact code review run` before commit success #### Scenario: Documentation explains optional house-rules integration + - **GIVEN** a project maintains a `house_rules` skill file - **WHEN** the developer follows the adoption guidance - **THEN** the documentation explains how to use that guidance in the review workflow without making it mandatory #### Scenario: Documentation explains JSON-first ledger behavior + - **GIVEN** a developer uses the review gate on a local or offline project - **WHEN** they read the adoption guidance - **THEN** the documentation states that the ledger works with local JSON storage by default and may use Supabase or another backend only when configured #### Scenario: Documentation explains commit-blocking semantics + - **GIVEN** a developer adopts the review gate in another repository - **WHEN** they read the guidance - **THEN** they understand that only blocking review verdicts fail the commit while advisory verdicts remain commit-green diff --git a/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/pre-commit-review-gate/spec.md b/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/pre-commit-review-gate/spec.md index b83feaec..d1183dcb 100644 --- a/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/pre-commit-review-gate/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/pre-commit-review-gate/spec.md @@ -1,31 +1,37 @@ ## ADDED Requirements ### Requirement: Repository Pre-Commit Review Gate + The system SHALL integrate `specfact code review run` into this repository's pre-commit workflow so commits are blocked when the review verdict is `FAIL` and allowed to proceed when the verdict is `PASS` or `PASS_WITH_ADVISORY`. #### Scenario: Pre-commit passes when review verdict is PASS + - **GIVEN** staged repository files produce a `PASS` verdict - **WHEN** the repository pre-commit workflow runs the review gate - **THEN** the hook exits successfully and the commit may proceed #### Scenario: Pre-commit passes when review verdict is PASS_WITH_ADVISORY + - **GIVEN** staged repository files produce a `PASS_WITH_ADVISORY` verdict - **WHEN** the repository pre-commit workflow runs the review gate - **THEN** the hook exits successfully and the commit may proceed #### Scenario: Pre-commit blocks commit when review verdict is FAIL + - **GIVEN** staged repository files produce a `FAIL` verdict - **WHEN** the repository pre-commit workflow runs the review gate - **THEN** the hook exits non-zero and the commit is blocked #### Scenario: Review gate only targets relevant staged files + - **GIVEN** a commit contains staged files and non-code staged files - **WHEN** the repository pre-commit workflow runs the review gate - **THEN** the command reviews only the relevant staged source files instead of the full repository #### Scenario: Missing review command surfaces actionable setup guidance + - **GIVEN** the local environment cannot run `specfact code review run` - **WHEN** the repository pre-commit workflow runs the review gate - **THEN** the hook exits non-zero with setup guidance instead of failing silently diff --git a/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/reward-ledger/spec.md b/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/reward-ledger/spec.md index a6f15af2..3a653f20 100644 --- a/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/reward-ledger/spec.md +++ b/openspec/changes/archive/2026-03-17-code-review-09-f4-automation-upgrade/specs/reward-ledger/spec.md @@ -1,37 +1,44 @@ ## MODIFIED Requirements ### Requirement: Supabase Reward Ledger with Offline JSON Fallback + The system SHALL persist review run results to local `~/.specfact/ledger.json` by default for local and offline workflows, and MAY additionally write to Supabase `ai_sync.review_runs` plus `ai_sync.reward_ledger` when backend configuration is present. #### Scenario: Record run writes to local JSON by default + - **GIVEN** a `ReviewReport` with `score=85`, `reward_delta=5`, `verdict="PASS"` - **WHEN** `LedgerClient.record_run(report)` is called in a local default setup - **THEN** the run is appended to `~/.specfact/ledger.json` and no backend configuration is required #### Scenario: Record run stores data in Supabase when configured + - **GIVEN** a `ReviewReport` with `score=85`, `reward_delta=5`, `verdict="PASS"` and Supabase is configured and reachable - **WHEN** `LedgerClient.record_run(report)` is called - **THEN** a row is inserted into `ai_sync.review_runs` and `ai_sync.reward_ledger` is updated with `coins += 0.5` and `streak_pass` incremented #### Scenario: Record run falls back to local JSON when backend unavailable + - **GIVEN** Supabase configuration is missing or unreachable - **WHEN** `LedgerClient.record_run(report)` is called - **THEN** the run is appended to `~/.specfact/ledger.json` and no exception is raised #### Scenario: Streak pass bonus applied at streak >= 5 + - **GIVEN** the agent has `streak_pass=4` and a new PASS run occurs - **WHEN** the ledger updates - **THEN** `streak_pass` becomes `5` and an additional `+0.5` coins is added #### Scenario: Streak block penalty applied at streak >= 3 + - **GIVEN** the agent has `streak_block=2` and a new BLOCK run occurs - **WHEN** the ledger updates - **THEN** `streak_block` becomes `3` and `-1.0` coins is deducted #### Scenario: Ledger status returns correct state + - **GIVEN** `cumulative_coins=12.5`, `streak_pass=3`, `last_verdict="PASS"` - **WHEN** `LedgerClient.get_status()` is called - **THEN** the returned dict includes `coins=12.5`, `streak_pass=3`, `last_verdict="PASS"` diff --git a/openspec/changes/archive/2026-03-17-module-migration-11-project-codebase-ownership-realignment/proposal.md b/openspec/changes/archive/2026-03-17-module-migration-11-project-codebase-ownership-realignment/proposal.md index 4f13c2cc..f7fd148f 100644 --- a/openspec/changes/archive/2026-03-17-module-migration-11-project-codebase-ownership-realignment/proposal.md +++ b/openspec/changes/archive/2026-03-17-module-migration-11-project-codebase-ownership-realignment/proposal.md @@ -21,6 +21,7 @@ This is not just a docs problem. The product needs a durable ownership rule for - Add a transition plan and validation coverage so pending changes and release docs do not reintroduce contradictory command ownership assumptions. ## Capabilities + ### New Capabilities - `project-codebase-ownership`: Explicit, testable ownership rules for `project` versus `codebase` command families and internal subsystems. @@ -41,7 +42,6 @@ This is not just a docs problem. The product needs a durable ownership rule for - `module-migration-10-bundle-command-surface-alignment` must align with the ownership decision in this change before finalizing public import command paths. - `backlog-module-ownership-cleanup` is the architectural precedent for fixing post-migration ownership drift after the initial extraction wave. - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-03-17-module-migration-11-project-codebase-ownership-realignment/specs/project-codebase-ownership/spec.md b/openspec/changes/archive/2026-03-17-module-migration-11-project-codebase-ownership-realignment/specs/project-codebase-ownership/spec.md index bddabccc..5b315a40 100644 --- a/openspec/changes/archive/2026-03-17-module-migration-11-project-codebase-ownership-realignment/specs/project-codebase-ownership/spec.md +++ b/openspec/changes/archive/2026-03-17-module-migration-11-project-codebase-ownership-realignment/specs/project-codebase-ownership/spec.md @@ -5,11 +5,13 @@ The system SHALL treat commands whose primary input is a source codebase or runtime code evidence as `code` category commands. #### Scenario: Brownfield import is code-owned + - **WHEN** the user runs the canonical code-first brownfield import workflow - **THEN** the workflow resolves from the `specfact code ...` command surface - **AND** `specfact code import` is treated as the canonical codebase-owned entrypoint rather than a project-owned path in the target command model. #### Scenario: Compatibility alias is transitional only + - **GIVEN** a temporary compatibility alias exists for a pre-realignment path such as `specfact project import from-code` - **WHEN** the command is invoked during the migration window - **THEN** the system routes to the code-owned implementation @@ -20,6 +22,7 @@ The system SHALL treat commands whose primary input is a source codebase or runt The system SHALL reserve the `project` category for commands whose primary subject is the SpecFact project bundle/workspace and its editable artifacts. #### Scenario: Project surface manages bundle artifacts + - **WHEN** a command primarily selects, reviews, edits, imports, exports, migrates, or otherwise manages SpecFact project bundle artifacts - **THEN** that command belongs to the `specfact project ...` surface - **AND** the command is not classified as codebase-owned solely because the artifact may later be synchronized with source code. @@ -29,6 +32,7 @@ The system SHALL reserve the `project` category for commands whose primary subje Subsystems that implement code-first brownfield analysis SHALL have one documented canonical bundle owner. #### Scenario: Analysis subsystems align with codebase ownership + - **WHEN** bundle ownership is resolved for brownfield analysis internals - **THEN** `analyzers`, `comparators`, brownfield-oriented `parsers`, and related import-analysis helpers are assigned to the codebase owner unless an explicit documented exception exists - **AND** migration plans, runtime registration, and docs do not describe contradictory owners for the same subsystem family. @@ -38,6 +42,7 @@ Subsystems that implement code-first brownfield analysis SHALL have one document Pending OpenSpec changes that touch command surface, docs, prompts, or migration cleanup SHALL align with the canonical `project` versus `codebase` ownership model. #### Scenario: Active change does not finalize conflicting import ownership + - **GIVEN** an active pending change updates grouped command paths or release-facing docs - **WHEN** that change references brownfield import ownership - **THEN** it references the canonical owner defined by this change diff --git a/openspec/changes/archive/2026-03-18-docs-03-command-syntax-parity/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-18-docs-03-command-syntax-parity/TDD_EVIDENCE.md index 0c93aa55..923c12a5 100644 --- a/openspec/changes/archive/2026-03-18-docs-03-command-syntax-parity/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-18-docs-03-command-syntax-parity/TDD_EVIDENCE.md @@ -5,6 +5,7 @@ **Timestamp**: 2026-03-18 **Command**: + ``` cd /home/dom/git/nold-ai/specfact-cli-worktrees/feature/docs-03-command-syntax-parity hatch test -- tests/unit/docs/test_release_docs_parity.py -v -k "removed or current" @@ -29,6 +30,7 @@ hatch test -- tests/unit/docs/test_release_docs_parity.py -v -k "removed or curr **Timestamp**: 2026-03-18 **Command**: + ``` cd /home/dom/git/nold-ai/specfact-cli-worktrees/feature/docs-03-command-syntax-parity hatch test -- tests/unit/docs/test_release_docs_parity.py -v @@ -37,6 +39,7 @@ hatch test -- tests/unit/docs/test_release_docs_parity.py -v **Result**: 21 PASSED (all) All new parity tests pass: + - `test_removed_project_plan_syntax_absent_from_authored_docs` βœ“ - `test_removed_project_import_from_bridge_syntax_absent_from_authored_docs` βœ“ - `test_removed_backlog_policy_syntax_absent_from_authored_docs` βœ“ diff --git a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/TDD_EVIDENCE.md index a406b667..9be12875 100644 --- a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/TDD_EVIDENCE.md @@ -13,6 +13,7 @@ cd /home/dom/git/nold-ai/specfact-cli-worktrees/feature/doc-frontmatter-schema & **Result:** βœ… 11 tests failed as expected (TDD - no implementation yet) **Failed Tests:** + - `TestFrontmatterParsing::test_valid_frontmatter_parsing` - parse_frontmatter function not implemented yet - `TestFrontmatterParsing::test_missing_required_fields` - parse_frontmatter function not implemented yet - `TestFrontmatterParsing::test_no_frontmatter` - parse_frontmatter function not implemented yet @@ -56,6 +57,7 @@ cd /home/dom/git/nold-ai/specfact-cli-worktrees/feature/doc-frontmatter-schema & **Result:** βœ… 10 tests failed as expected (TDD - no implementation yet) **Failed Tests:** + - `TestFileDiscovery::test_discover_docs_directory_files` - get_all_md_files function not implemented yet - `TestFileDiscovery::test_exempt_files_exclusion` - get_all_md_files function not implemented yet - `TestMissingDocOwnerDetection::test_missing_doc_owner_detection` - rg_missing_doc_owner function not implemented yet @@ -82,6 +84,7 @@ cd /home/dom/git/nold-ai/specfact-cli-worktrees/feature/doc-frontmatter-schema & **Result:** βœ… 9 tests failed as expected, 1 error (benchmark fixture) - TDD confirmed **Failed Tests:** + - `TestEndToEndWorkflow::test_complete_validation_workflow` - validation_main function not implemented yet - `TestEndToEndWorkflow::test_validation_with_all_valid_files` - validation_main function not implemented yet - `TestMultipleFileScenarios::test_large_number_of_files` - validation_main function not implemented yet @@ -93,6 +96,7 @@ cd /home/dom/git/nold-ai/specfact-cli-worktrees/feature/doc-frontmatter-schema & - `TestRealWorldScenarios::test_complex_tracking_patterns` - validation_main function not implemented yet **Error:** + - `TestPerformance::test_execution_time_with_many_files` - fixture 'benchmark' not found (expected - benchmark fixture not available) **Status:** βœ… TDD workflow confirmed - integration tests fail before implementation @@ -151,6 +155,7 @@ cd /home/dom/git/nold-ai/specfact-cli-worktrees/feature/doc-frontmatter-schema **Result:** βœ… 5 tests failed as expected before workflow changes; 1 coverage-dependency guard test passed. **Failure summary:** + - `compat-py311` currently depends on `changes` and `tests` instead of `changes` and `verify-module-signatures` - `contract-first-ci` currently depends on `changes`, `tests`, and `compat-py311` - `type-checking` currently depends on `changes` and `tests` @@ -173,6 +178,7 @@ cd /home/dom/git/nold-ai/specfact-cli-worktrees/feature/doc-frontmatter-schema regression slice remained green (`34 passed`). **Passing summary:** + - `compat-py311`, `contract-first-ci`, `type-checking`, `linting`, and `cli-validation` now depend on `changes` plus `verify-module-signatures` - `quality-gates` still depends on `tests` because it consumes coverage artifacts diff --git a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/design.md b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/design.md index f3d7b0a7..10e1a56b 100644 --- a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/design.md +++ b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/design.md @@ -5,12 +5,14 @@ The current documentation system lacks ownership tracking and synchronization mechanisms. Documentation files can become outdated when source code changes, leading to maintenance challenges and potential misinformation. This design implements a frontmatter-based ownership and validation system to address these issues. ### Current State + - Documentation files in `docs/` and root-level markdown files - No ownership tracking mechanism - Manual review process for documentation updates - No automated validation of documentation metadata ### Constraints + - Must integrate with existing pre-commit infrastructure - Must support Python 3.12+ environment - Must use existing code quality tools and patterns @@ -18,6 +20,7 @@ The current documentation system lacks ownership tracking and synchronization me - Must be compatible with GitHub Actions workflows ### Stakeholders + - Developers: Need clear ownership and validation feedback - Technical Writers: Need documentation standards and templates - CI/CD Pipeline: Needs reliable validation mechanism @@ -26,6 +29,7 @@ The current documentation system lacks ownership tracking and synchronization me ## Goals / Non-Goals ### Goals + - Implement YAML frontmatter schema for documentation ownership - Create validation script with comprehensive error handling - Integrate with existing pre-commit workflow @@ -34,6 +38,7 @@ The current documentation system lacks ownership tracking and synchronization me - Ensure zero errors policy compliance ### Non-Goals + - Automated documentation updates (future enhancement) - Real-time synchronization (future enhancement) - Cross-repository documentation tracking (out of scope) @@ -42,85 +47,103 @@ The current documentation system lacks ownership tracking and synchronization me ## Decisions ### 1. YAML Frontmatter Schema + **Decision**: Use YAML frontmatter with specific fields: `title`, `doc_owner`, `tracks`, `last_reviewed`, `exempt`, `exempt_reason` -**Rationale**: +**Rationale**: + - YAML is already used in the codebase (Jekyll, OpenSpec) - Standard format that's familiar to developers - Supports structured data with validation - Human-readable and editable **Alternatives Considered**: + - JSON frontmatter: More rigid, less human-friendly - TOML frontmatter: Less common in documentation - Custom header format: Non-standard, harder to parse ### 2. Validation Script Implementation + **Decision**: Implement validation script in Python using PyYAML and standard library **Rationale**: + - Python is the primary language of the codebase - PyYAML provides robust YAML parsing - Standard library supports file operations and glob matching - Easy to integrate with existing hatch-based tooling **Alternatives Considered**: + - Shell script: Less maintainable, harder to test - Node.js: Would require additional runtime dependency - Rust: Overkill for this use case, would add complexity ### 3. Owner Resolution Strategy + **Decision**: Support both path-like identifiers and known tokens **Rationale**: + - Path-like identifiers provide direct mapping to code modules - Known tokens support repository-level and organizational ownership - Flexible approach accommodates different ownership patterns - Maintains compatibility with existing module structure **Alternatives Considered**: + - Path-only resolution: Too restrictive for some use cases - Token-only resolution: Loses direct code mapping - Complex ownership hierarchy: Overly complicated for current needs ### 4. Pre-commit Integration + **Decision**: Integrate as a separate pre-commit hook that runs after other validations **Rationale**: + - Separate hook allows independent execution and testing - Runs after other validations to catch issues early - Follows existing pre-commit patterns in the codebase - Easy to disable temporarily if needed **Alternatives Considered**: + - Integrated into existing code review gate: Would complicate existing validation - Post-commit hook: Too late to prevent bad commits - CI-only validation: Loses local developer feedback loop ### 5. Error Handling and User Experience + **Decision**: Provide detailed error messages with fix hints and `--fix-hint` flag **Rationale**: + - Reduces developer friction during adoption - Provides immediate guidance for common issues - Follows CLI best practices for user experience - Supports gradual adoption and learning curve **Alternatives Considered**: + - Minimal error messages: Poor user experience - Automatic fixes: Too risky, could make incorrect changes - Interactive prompts: Would complicate CI integration ### 6. Test-Driven Development Approach + **Decision**: Follow strict TDD-first approach with contract testing **Rationale**: + - Ensures robust implementation from the start - Provides living documentation through tests - Follows codebase conventions and quality standards - Enables safe refactoring and maintenance **Alternatives Considered**: + - Test-last approach: Higher risk of bugs and regressions - No formal testing: Unacceptable for quality standards - Property-based testing only: Would miss specific scenarios @@ -128,40 +151,50 @@ The current documentation system lacks ownership tracking and synchronization me ## Risks / Trade-offs ### [Developer Adoption Resistance] β†’ Mitigation + **Risk**: Developers may resist adding frontmatter to existing documentation -**Mitigation**: +**Mitigation**: + - Provide clear migration guide and templates - Offer `--fix-hint` flag for easy fixes - Phase rollout starting with new documentation - Provide comprehensive documentation and examples ### [Performance Impact on Large Repositories] β†’ Mitigation + **Risk**: Validation script may be slow on repositories with many documentation files **Mitigation**: + - Implement efficient file discovery using glob patterns - Use caching for owner resolution where appropriate - Optimize YAML parsing with streaming where possible - Set performance requirements in specs ### [False Positives in Owner Resolution] β†’ Mitigation + **Risk**: Owner resolution may incorrectly flag valid owners **Mitigation**: + - Make `VALID_OWNER_TOKENS` and `SOURCE_ROOTS` configurable - Provide clear error messages with suggestions - Allow easy configuration updates - Implement comprehensive test coverage ### [Complexity for New Contributors] β†’ Mitigation + **Risk**: Frontmatter requirements may be confusing for new contributors **Mitigation**: + - Provide detailed documentation with examples - Create templates for common documentation types - Offer clear error messages with guidance - Include in contributing guidelines and onboarding ### [Integration with Existing Workflows] β†’ Mitigation + **Risk**: May disrupt existing documentation workflows **Mitigation**: + - Phase rollout with opt-in period - Provide clear migration path - Maintain backward compatibility where possible @@ -170,24 +203,28 @@ The current documentation system lacks ownership tracking and synchronization me ## Migration Plan ### Phase 1: Infrastructure Setup + 1. Create validation script with comprehensive tests 2. Update pre-commit configuration 3. Add documentation for frontmatter schema 4. Test with sample documentation files ### Phase 2: Gradual Adoption + 1. Start with new documentation files 2. Add frontmatter to critical documentation first 3. Provide migration guide for existing docs 4. Offer team training and Q&A sessions ### Phase 3: Full Enforcement + 1. Enable pre-commit hook for all developers 2. Update CI configuration for enforcement 3. Monitor and address any issues 4. Celebrate successful adoption ### Rollback Strategy + 1. Disable pre-commit hook if critical issues arise 2. Revert to manual review process temporarily 3. Address issues in validation script @@ -199,4 +236,4 @@ The current documentation system lacks ownership tracking and synchronization me 2. **Performance Optimization**: What's the threshold for "too slow" on large repositories? Should we implement caching? 3. **Exemption Process**: Should we implement a formal review process for exempt documents? 4. **CI Integration Timing**: Should docs sync check run before or after other CI checks? -5. **Gradual Enforcement**: Should we implement warnings before hard failures during adoption phase? \ No newline at end of file +5. **Gradual Enforcement**: Should we implement warnings before hard failures during adoption phase? diff --git a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/proposal.md b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/proposal.md index edf6635e..f9afbf55 100644 --- a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/proposal.md +++ b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/proposal.md @@ -5,6 +5,7 @@ The repository has extensive documentation but lacks a mechanism to track ownership and ensure documentation stays aligned with source code changes. This leads to documentation drift where docs become outdated when source files change, creating maintenance burden and potential misinformation for developers and users. The goal is to implement a frontmatter-based ownership model that: + - Clearly defines which module owns each documentation file - Tracks which source files each doc should stay synchronized with - Provides validation to prevent metadata drift diff --git a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/doc-frontmatter-schema/spec.md b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/doc-frontmatter-schema/spec.md index 8c0ed89a..2b497df7 100644 --- a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/doc-frontmatter-schema/spec.md +++ b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/doc-frontmatter-schema/spec.md @@ -3,6 +3,7 @@ ## ADDED Requirements ### Requirement: YAML Frontmatter Format + The system SHALL support YAML frontmatter in Markdown documentation files with the following schema: ```yaml @@ -18,74 +19,91 @@ exempt_reason: "" # required if exempt: true ``` #### Scenario: Valid frontmatter structure + - **WHEN** a Markdown file contains properly formatted YAML frontmatter - **THEN** the frontmatter SHALL be parsed successfully - **AND** all required fields SHALL be present #### Scenario: Missing required fields + - **WHEN** a Markdown file has frontmatter missing required fields - **THEN** the validation SHALL fail with clear error message - **AND** the error SHALL specify which fields are missing ### Requirement: Owner Identifier Resolution + The system SHALL support two types of owner identifiers: + 1. Path-like identifiers (e.g., `src/specfact/parser`) 2. Known tokens (e.g., `specfact-cli`, `nold-ai`, `openspec`) #### Scenario: Path-like owner resolution + - **WHEN** `doc_owner` is a path-like identifier - **THEN** the system SHALL verify the path exists in the repository - **AND** validation SHALL pass if path exists #### Scenario: Known token resolution + - **WHEN** `doc_owner` is a known token - **THEN** the system SHALL verify token is in `VALID_OWNER_TOKENS` - **AND** validation SHALL pass if token is valid #### Scenario: Invalid owner identifier + - **WHEN** `doc_owner` cannot be resolved - **THEN** validation SHALL fail with resolution error - **AND** error SHALL suggest valid alternatives ### Requirement: Glob Pattern Tracking + The system SHALL support glob patterns in the `tracks` field to specify which files/directories the documentation should stay synchronized with. #### Scenario: Single glob pattern + - **WHEN** `tracks` contains a single valid glob pattern - **THEN** the pattern SHALL match appropriate files - **AND** validation SHALL pass #### Scenario: Multiple glob patterns + - **WHEN** `tracks` contains multiple glob patterns - **THEN** all patterns SHALL be validated - **AND** validation SHALL pass if all patterns are valid #### Scenario: Invalid glob pattern + - **WHEN** `tracks` contains an invalid glob pattern - **THEN** validation SHALL fail with pattern error ### Requirement: Exemption Handling + The system SHALL support document exemption for stable/legal documentation that doesn't need synchronization. #### Scenario: Valid exemption + - **WHEN** `exempt: true` with valid `exempt_reason` - **THEN** validation SHALL pass - **AND** document SHALL be excluded from sync checks #### Scenario: Exemption without reason + - **WHEN** `exempt: true` but `exempt_reason` is empty - **THEN** validation SHALL fail - **AND** error SHALL require exemption reason ### Requirement: Frontmatter Extraction + The system SHALL provide a function to extract frontmatter from Markdown files. #### Scenario: Extract from file with frontmatter + - **WHEN** `extract_frontmatter(path)` is called on file with valid frontmatter - **THEN** function SHALL return parsed frontmatter dictionary - **AND** original file content SHALL remain unchanged #### Scenario: Extract from file without frontmatter + - **WHEN** `extract_frontmatter(path)` is called on file without frontmatter - **THEN** function SHALL return empty dictionary - **AND** no error SHALL be raised @@ -93,17 +111,22 @@ The system SHALL provide a function to extract frontmatter from Markdown files. ## Contract Requirements ### Requirement: Input Validation Contracts + All public functions SHALL use `@icontract` decorators for input validation: + - `@require` for preconditions - `@ensure` for postconditions #### Scenario: Invalid input type + - **WHEN** function receives invalid input type - **THEN** `@require` contract SHALL raise appropriate exception ### Requirement: Type Safety Contracts + All public functions SHALL use `@beartype` decorators for runtime type checking. #### Scenario: Type mismatch + - **WHEN** function receives argument of wrong type -- **THEN** `@beartype` SHALL raise TypeError with clear message \ No newline at end of file +- **THEN** `@beartype` SHALL raise TypeError with clear message diff --git a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/doc-frontmatter-validation/spec.md b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/doc-frontmatter-validation/spec.md index 5d341b2d..e682672f 100644 --- a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/doc-frontmatter-validation/spec.md +++ b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/doc-frontmatter-validation/spec.md @@ -106,6 +106,7 @@ The system SHALL properly handle files marked as exempt. ### Requirement: Validation Contracts The validation script SHALL use `@icontract` decorators for validation logic: + - `@require` for input validation - `@ensure` for validation results diff --git a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/docs-contributing-updates/spec.md b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/docs-contributing-updates/spec.md index 72c2f482..62337403 100644 --- a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/docs-contributing-updates/spec.md +++ b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/specs/docs-contributing-updates/spec.md @@ -3,66 +3,81 @@ ## ADDED Requirements ### Requirement: Frontmatter Documentation + The system SHALL provide comprehensive documentation for the frontmatter schema and validation system. #### Scenario: Schema reference documentation + - **WHEN** user reads `docs/contributing/docs-sync.md` - **THEN** they SHALL find complete frontmatter schema reference - **AND** examples for all field types #### Scenario: Validation workflow documentation + - **WHEN** user reads contributing docs - **THEN** they SHALL understand validation workflow - **AND** know how to fix validation errors ### Requirement: Getting Started Guide + The system SHALL provide a getting started guide for new contributors. #### Scenario: New contributor setup + - **WHEN** new contributor reads docs - **THEN** they SHALL find setup instructions - **AND** example frontmatter for common cases #### Scenario: Common patterns documentation + - **WHEN** user reads documentation - **THEN** they SHALL find common frontmatter patterns - **AND** best practices for different doc types ### Requirement: Troubleshooting Guide + The system SHALL provide troubleshooting guidance for validation issues. #### Scenario: Error message reference + - **WHEN** user encounters validation error - **THEN** they SHALL find error reference in docs - **AND** step-by-step resolution guide #### Scenario: Fix hint examples + - **WHEN** user needs help with fix hints - **THEN** they SHALL find examples in documentation - **AND** explanations of fix hint format ### Requirement: Integration Documentation + The system SHALL document how the validation integrates with existing workflows. #### Scenario: Pre-commit integration docs + - **WHEN** user reads integration docs - **THEN** they SHALL understand pre-commit hook setup - **AND** configuration options #### Scenario: CI integration documentation + - **WHEN** user reads integration docs - **THEN** they SHALL find CI workflow documentation - **AND** branch protection setup guide ### Requirement: Examples and Templates + The system SHALL provide practical examples and templates. #### Scenario: Frontmatter template examples + - **WHEN** user needs frontmatter template - **THEN** they SHALL find examples for different doc types - **AND** copy-paste ready templates #### Scenario: Real-world examples + - **WHEN** user reads documentation - **THEN** they SHALL find real-world examples - **AND** explanations of design decisions @@ -70,17 +85,21 @@ The system SHALL provide practical examples and templates. ## Contract Requirements ### Requirement: Documentation Completeness + All documentation SHALL be complete and accurate. #### Scenario: Complete schema documentation + - **WHEN** user reads schema docs - **THEN** all frontmatter fields SHALL be documented - **AND** examples SHALL be provided ### Requirement: Documentation Accuracy + Documentation SHALL accurately reflect current implementation. #### Scenario: Accurate workflow description + - **WHEN** user follows documented workflow - **THEN** it SHALL work as described - **AND** produce expected results @@ -88,25 +107,31 @@ Documentation SHALL accurately reflect current implementation. ## Quality Requirements ### Requirement: Readability + Documentation SHALL be well-written and easy to understand. #### Scenario: Clear and concise writing + - **WHEN** user reads documentation - **THEN** content SHALL be clear and concise - **AND** free of jargon where possible ### Requirement: Organization + Documentation SHALL be well-organized. #### Scenario: Logical structure + - **WHEN** user navigates documentation - **THEN** structure SHALL be logical - **AND** easy to follow ### Requirement: Maintainability + Documentation SHALL be easy to maintain. #### Scenario: Easy updates + - **WHEN** maintainer updates documentation - **THEN** changes SHALL be straightforward -- **AND** structure SHALL support easy updates \ No newline at end of file +- **AND** structure SHALL support easy updates diff --git a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/tasks.md b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/tasks.md index 5233716a..03fcf98c 100644 --- a/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/tasks.md +++ b/openspec/changes/archive/2026-03-29-doc-frontmatter-schema/tasks.md @@ -3,6 +3,7 @@ ## TDD / SDD order (enforced) Per config.yaml and design.md, this change follows strict TDD-first ordering: + 1. Spec deltas first (already created in specs/) 2. Tests second (expect failure initially) 3. Code last (implement to pass tests) diff --git a/openspec/changes/archive/2026-03-31-ci-02-trustworthy-green-checks/proposal.md b/openspec/changes/archive/2026-03-31-ci-02-trustworthy-green-checks/proposal.md index e550a6fc..b1bb44ce 100644 --- a/openspec/changes/archive/2026-03-31-ci-02-trustworthy-green-checks/proposal.md +++ b/openspec/changes/archive/2026-03-31-ci-02-trustworthy-green-checks/proposal.md @@ -54,8 +54,8 @@ If maintainers cannot trust that "green" means the required checks really passed - **GitHub Issue**: #465 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/465 +- **Issue URL**: - **Parent Feature**: #406 -- **Parent Feature URL**: https://github.com/nold-ai/specfact-cli/issues/406 +- **Parent Feature URL**: - **Last Synced Status**: open - **Sanitized**: true diff --git a/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/TDD_EVIDENCE.md index 144f5f54..ba8c90be 100644 --- a/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/TDD_EVIDENCE.md @@ -5,6 +5,7 @@ **Timestamp**: 2026-03-31T~10:30 UTC (worktree session) **Command**: + ```bash hatch test -- tests/unit/specfact_cli/test_clean_code_principle_gates.py -v ``` @@ -54,6 +55,7 @@ Files changed: **Timestamp**: 2026-03-31T~10:35 UTC **Command**: + ```bash hatch test -- tests/unit/specfact_cli/test_clean_code_principle_gates.py -v ``` diff --git a/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/proposal.md b/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/proposal.md index 59ecbf6f..ea3cd318 100644 --- a/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/proposal.md +++ b/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/proposal.md @@ -37,6 +37,6 @@ SpecFact CLI already has a growing review toolchain, but the repository still la - **GitHub Issue**: #434 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/434 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: open diff --git a/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/agent-instruction-clean-code-charter/spec.md b/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/agent-instruction-clean-code-charter/spec.md index 9c0d95a9..cf01eb21 100644 --- a/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/agent-instruction-clean-code-charter/spec.md +++ b/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/agent-instruction-clean-code-charter/spec.md @@ -1,15 +1,18 @@ ## ADDED Requirements ### Requirement: Agent Instruction Clean-Code Charter + The repository SHALL expose the 7-principle clean-code charter consistently across its instruction surfaces without creating drift-prone duplicate sources of truth. #### Scenario: Core instruction surfaces reference the charter consistently + - **GIVEN** a contributor opens `AGENTS.md`, `CLAUDE.md`, `.cursor/rules/clean-code-principles.mdc`, or `.github/copilot-instructions.md` - **WHEN** they inspect clean-code guidance - **THEN** each surface points to the same clean-code charter semantics - **AND** any shorter alias surface references the canonical charter rather than redefining it independently #### Scenario: Generated IDE aliases stay lightweight + - **GIVEN** platform-specific instruction files are generated from `ai-integration-03-instruction-files` - **WHEN** clean-code guidance is included - **THEN** the generated file contains a short clean-code alias reference diff --git a/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/clean-code-compliance-gate/spec.md b/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/clean-code-compliance-gate/spec.md index 0438a768..9d793637 100644 --- a/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/clean-code-compliance-gate/spec.md +++ b/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/clean-code-compliance-gate/spec.md @@ -1,15 +1,18 @@ ## ADDED Requirements ### Requirement: Clean-Code Compliance Gate + The repository SHALL consume the expanded review-module clean-code categories and block clean-code regressions before merge. #### Scenario: Repo review includes expanded clean-code categories + - **GIVEN** the review module exposes clean-code categories `naming`, `kiss`, `yagni`, `dry`, and `solid` - **WHEN** `specfact review` runs against specfact-cli in CI or local gated mode - **THEN** those categories are included in the review result - **AND** regressions in blocking clean-code rules fail the gated run #### Scenario: Zero-finding dogfood baseline stays a prerequisite + - **GIVEN** `code-review-zero-findings` has not yet reached its zero-finding proof - **WHEN** implementation work for this change is evaluated - **THEN** clean-code gating cannot be considered complete diff --git a/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/clean-code-loc-nesting-check/spec.md b/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/clean-code-loc-nesting-check/spec.md index 0c5ab67f..50298683 100644 --- a/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/clean-code-loc-nesting-check/spec.md +++ b/openspec/changes/archive/2026-03-31-clean-code-01-principle-gates/specs/clean-code-loc-nesting-check/spec.md @@ -1,15 +1,18 @@ ## ADDED Requirements ### Requirement: Staged LOC, Nesting, and Parameter Checks + The repository SHALL adopt the expanded KISS metrics through a staged rollout that starts with the Phase A thresholds from the 2026-03-22 plan. #### Scenario: Phase A thresholds are enforced first + - **GIVEN** the clean-code review checks are enabled for specfact-cli - **WHEN** LOC-per-function findings are evaluated - **THEN** warning and error thresholds start at `>80` and `>120` - **AND** nesting-depth and parameter-count checks are active in the same review run #### Scenario: Phase B remains deferred until cleanup is complete + - **GIVEN** stricter LOC thresholds of `>40` and `>80` are planned - **WHEN** this change is implemented - **THEN** Phase B remains documented as a future tightening step diff --git a/openspec/changes/archive/2026-03-31-code-review-zero-findings/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-03-31-code-review-zero-findings/CHANGE_VALIDATION.md index 35ce5c32..953a0f79 100644 --- a/openspec/changes/archive/2026-03-31-code-review-zero-findings/CHANGE_VALIDATION.md +++ b/openspec/changes/archive/2026-03-31-code-review-zero-findings/CHANGE_VALIDATION.md @@ -15,6 +15,7 @@ - Required change artifacts are now present in the worktree. - Strict OpenSpec validation can be run in the worktree without losing in-progress task state. + # Change Validation Report: code-review-zero-findings **Validation Date**: 2026-03-18T19:15:00Z @@ -48,13 +49,16 @@ None. This change is a pure internal quality improvement: ## Dependencies Affected ### Critical Updates Required + None. ### Recommended Updates + - `sync/bridge_sync.py` (205 findings): Type annotation work may require callers that rely on implicit `Any` typing to be updated if they have type-checking enabled. All callers are internal (`src/specfact_cli/`). - `adapters/ado.py`, `adapters/github.py`: Same as above β€” internal callers only. ### Optional + - All other 258 files: No interface changes; only internal annotation/decorator additions. --- @@ -71,6 +75,7 @@ None. ## Format Validation ### proposal.md Format: **Pass** (after fixes applied) + - βœ… Title: `# Change: Zero-finding code review β€” dogfooding specfact review on specfact-cli` - βœ… `## Why` section present - βœ… `## What Changes` section with NEW/EXTEND/MODIFY markers @@ -81,6 +86,7 @@ None. **Issues fixed**: Added title header; added NEW/MODIFY markers to What Changes bullets; added Source Tracking section; added documentation impact to Impact section. ### tasks.md Format: **Pass** (after fixes applied) + - βœ… Hierarchical numbered sections (`## 0.` through `## 12.`) - βœ… All tasks use `- [ ] X.Y Description` format - βœ… Worktree creation first (task 1.1) @@ -97,6 +103,7 @@ None. **Issues fixed**: Added GitHub issue creation (Section 0); added documentation research section (9); added module signing quality gate (10); added version/changelog task (11); added worktree cleanup task (12.3); noted CHANGE_ORDER already done (12.1). ### specs Format: **Pass** + - βœ… New capability `dogfood-self-review` has spec file at `specs/dogfood-self-review/spec.md` - βœ… All modified capabilities (`code-review-module`, `debug-logging`, `review-cli-contracts`) have delta spec files - βœ… All scenarios use `####` (4 hashtags) as required @@ -106,6 +113,7 @@ None. - Note: `contract-runner` listed as modified capability in proposal but no delta spec created β€” justified because no spec-level behavior changes (coverage expansion only; existing spec already covers the behavior). ### config.yaml Compliance: **Pass** + - βœ… SDD+TDD order enforced in tasks - βœ… Contract decorator tasks included (Sections 5, 10) - βœ… Offline-first validation scenarios in specs (review runs locally, no cloud dependency) diff --git a/openspec/changes/archive/2026-03-31-code-review-zero-findings/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-31-code-review-zero-findings/TDD_EVIDENCE.md index 69d3fd54..f169179d 100644 --- a/openspec/changes/archive/2026-03-31-code-review-zero-findings/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-31-code-review-zero-findings/TDD_EVIDENCE.md @@ -45,17 +45,20 @@ result above β€” 2522 findings on the pre-fix codebase β€” which the tests would assert against if run outside TEST_MODE. **Proxy evidence command (pre-fix):** + ``` hatch run specfact code review run --scope full --json --out /tmp/baseline-review.json ``` + **Timestamp:** 2026-03-18 21:31:57 UTC **Result:** FAIL β€” 2522 findings, overall_verdict: FAIL - - test_review_overall_verdict_pass β†’ would FAIL (verdict=FAIL, not PASS) - - test_zero_basedpyright_unknown_member_type β†’ would FAIL (1515 findings) - - test_zero_semgrep_print_in_src β†’ would FAIL (352 findings) - - test_zero_missing_icontract β†’ would FAIL (291 findings) - - test_zero_radon_cc_error_band β†’ would FAIL (202 CC>=16 findings) - - test_zero_tool_errors β†’ PASS (tool_error fixed by .pylintrc in task 1.4) + +- test_review_overall_verdict_pass β†’ would FAIL (verdict=FAIL, not PASS) +- test_zero_basedpyright_unknown_member_type β†’ would FAIL (1515 findings) +- test_zero_semgrep_print_in_src β†’ would FAIL (352 findings) +- test_zero_missing_icontract β†’ would FAIL (291 findings) +- test_zero_radon_cc_error_band β†’ would FAIL (202 CC>=16 findings) +- test_zero_tool_errors β†’ PASS (tool_error fixed by .pylintrc in task 1.4) --- @@ -115,20 +118,23 @@ hatch run specfact code review run --scope full --json --out /tmp/baseline-revie **Command:** `python3 -m pytest tests/unit/specfact_cli/test_dogfood_self_review.py -q` **Timestamp:** 2026-03-18 22:58:00 UTC **Result:** PASS (expected skips under `TEST_MODE=true`) - - 6 tests collected - - 6 tests skipped by design because CI test mode suppresses the live review invocation + +- 6 tests collected +- 6 tests skipped by design because CI test mode suppresses the live review invocation **Command:** `basedpyright --outputjson modules/bundle-mapper/src/app.py scripts/verify-bundle-published.py` **Timestamp:** 2026-03-18 22:56:00 UTC **Result:** PASS with 0 errors, 2 warnings - - fixed `reportCallIssue` mismatches in `modules/bundle-mapper/src/app.py` - - fixed `reportOptionalMemberAccess` issues in `scripts/verify-bundle-published.py` + +- fixed `reportCallIssue` mismatches in `modules/bundle-mapper/src/app.py` +- fixed `reportOptionalMemberAccess` issues in `scripts/verify-bundle-published.py` **Command:** `basedpyright --outputjson ` **Timestamp:** 2026-03-18 22:59:00 UTC **Result:** PASS with 0 errors, 1103 warnings - - branch-local hard errors reduced from 5 to 0 in the touched light-file set - - largest remaining warning clusters are `module_registry/src/commands.py`, `adapters/ado.py`, `adapters/github.py`, and `cli.py` + +- branch-local hard errors reduced from 5 to 0 in the touched light-file set +- largest remaining warning clusters are `module_registry/src/commands.py`, `adapters/ado.py`, `adapters/github.py`, and `cli.py` --- @@ -145,7 +151,8 @@ hatch run specfact code review run --scope full --json --out /tmp/baseline-revie **Command:** `hatch run basedpyright src/specfact_cli/adapters/backlog_base.py src/specfact_cli/adapters/ado.py` **Timestamp:** 2026-03-23T00:45:37+01:00 **Result:** PASS β€” `0 errors, 0 warnings, 0 notes` - - cleared the remaining `reportUnknownMemberType` warnings in `src/specfact_cli/adapters/backlog_base.py` + +- cleared the remaining `reportUnknownMemberType` warnings in `src/specfact_cli/adapters/backlog_base.py` **Command:** `hatch run radon cc -s -n C src/specfact_cli/adapters/ado.py` **Timestamp:** 2026-03-23T00:45:37+01:00 @@ -154,10 +161,11 @@ hatch run specfact code review run --scope full --json --out /tmp/baseline-revie **Command:** `hatch run specfact code review run --scope full` **Timestamp:** 2026-03-23 00:45:44 +0100 start / 2026-03-23 00:46:20 +0100 finish **Result:** PASS β€” `Review completed with no findings.` - - Verdict: `PASS` - - CI exit: `0` - - Score: `115` - - Reward delta: `35` + +- Verdict: `PASS` +- CI exit: `0` +- Score: `115` +- Reward delta: `35` --- @@ -166,8 +174,9 @@ hatch run specfact code review run --scope full --json --out /tmp/baseline-revie **Command:** `hatch run python -c "from pathlib import Path; from specfact_cli.registry.module_installer import get_bundled_module_metadata, verify_module_artifact; meta=get_bundled_module_metadata()['bundle-mapper']; print(verify_module_artifact(Path('modules/bundle-mapper'), meta, allow_unsigned=True, require_integrity=True))"` **Timestamp:** 2026-03-23T00:59:25+01:00 **Result:** PASS β€” `True` - - aligned runtime artifact verification with the module signing payload by excluding `tests/` from hashed module directories - - confirmed the manually re-signed `modules/bundle-mapper/module-package.yaml` now passes bundled-module integrity checks + +- aligned runtime artifact verification with the module signing payload by excluding `tests/` from hashed module directories +- confirmed the manually re-signed `modules/bundle-mapper/module-package.yaml` now passes bundled-module integrity checks **Command:** `hatch run basedpyright src/specfact_cli/templates/specification_templates.py src/specfact_cli/registry/module_installer.py tests/integration/test_command_package_runtime_validation.py tests/unit/scripts/test_verify_bundle_published.py` **Timestamp:** 2026-03-23T00:59:25+01:00 @@ -176,14 +185,16 @@ hatch run specfact code review run --scope full --json --out /tmp/baseline-revie **Command:** `hatch run pytest tests/integration/test_command_package_runtime_validation.py::test_command_audit_help_cases_execute_cleanly_in_temp_home -q` **Timestamp:** 2026-03-23 ~01:00 CET **Result:** PASS β€” `1 passed in 23.57s` - - optimized the command-audit proof by seeding marketplace modules from local package fixtures and running `help-only` audit cases in-process while keeping fixture-backed cases subprocess-isolated + +- optimized the command-audit proof by seeding marketplace modules from local package fixtures and running `help-only` audit cases in-process while keeping fixture-backed cases subprocess-isolated **Command:** `hatch run pytest tests/unit/scripts/test_verify_bundle_published.py tests/unit/specfact_cli/test_module_boundary_imports.py tests/unit/templates/test_specification_templates.py tests/integration/test_command_package_runtime_validation.py -q` **Timestamp:** 2026-03-23 ~01:00 CET **Result:** PASS β€” `29 passed in 27.48s` - - `verify-bundle-published` tests updated to assert structured log output instead of stdout - - stale core-repo sync runtime unit tests removed to satisfy module-boundary migration gate - - implementation-plan template contract helper fixed so factory calls no longer fail with unset condition arguments + +- `verify-bundle-published` tests updated to assert structured log output instead of stdout +- stale core-repo sync runtime unit tests removed to satisfy module-boundary migration gate +- implementation-plan template contract helper fixed so factory calls no longer fail with unset condition arguments --- @@ -192,18 +203,21 @@ hatch run specfact code review run --scope full --json --out /tmp/baseline-revie **Command:** `hatch run pytest tests/unit/tools/test_smart_test_coverage.py -q -k popen_stream_to_log_streams_to_stdout_and_log_file` **Timestamp:** 2026-03-23T01:15:35+01:00 **Result:** FAIL β€” `1 failed, 75 deselected` - - failure reproduced the CI regression after switching the workflow to direct `python tools/smart_test_coverage.py run --level full` - - `_popen_stream_to_log()` wrote subprocess lines into the persistent log buffer, but `captured.out` stayed empty, so GitHub Actions no longer showed live pytest progress + +- failure reproduced the CI regression after switching the workflow to direct `python tools/smart_test_coverage.py run --level full` +- `_popen_stream_to_log()` wrote subprocess lines into the persistent log buffer, but `captured.out` stayed empty, so GitHub Actions no longer showed live pytest progress **Command:** `hatch run pytest tests/unit/tools/test_smart_test_coverage.py -q -k popen_stream_to_log_streams_to_stdout_and_log_file` **Timestamp:** 2026-03-23T01:16:48+01:00 **Result:** PASS β€” `1 passed, 75 deselected` - - `_popen_stream_to_log()` now tees each subprocess line to stdout while still appending it to the persistent log file + +- `_popen_stream_to_log()` now tees each subprocess line to stdout while still appending it to the persistent log file **Command:** `hatch run pytest tests/unit/tools/test_smart_test_coverage.py tests/unit/tools/test_smart_test_coverage_enhanced.py -q` **Timestamp:** 2026-03-23T01:16:48+01:00 **Result:** PASS β€” `107 passed in 1.70s` - - verified the stdout tee does not break the existing smart-test runner behaviors around full, unit, folder, integration, fallback, and threshold handling + +- verified the stdout tee does not break the existing smart-test runner behaviors around full, unit, folder, integration, fallback, and threshold handling **Command:** `hatch run basedpyright tools/smart_test_coverage.py` **Timestamp:** 2026-03-23T01:16:48+01:00 @@ -216,18 +230,21 @@ hatch run specfact code review run --scope full --json --out /tmp/baseline-revie **Command:** `HOME=/tmp/specfact-ci-empty-home SPECFACT_MODULES_REPO=/home/dom/git/nold-ai/specfact-cli-modules PYTHONPATH=/home/dom/git/nold-ai/specfact-cli-worktrees/bugfix/code-review-zero-findings/src:/home/dom/git/nold-ai/specfact-cli-worktrees/bugfix/code-review-zero-findings /home/dom/git/nold-ai/specfact-cli/.venv/bin/python -m pytest tests/integration/test_command_package_runtime_validation.py -q -k test_command_audit_help_cases_execute_cleanly_in_temp_home` **Timestamp:** 2026-03-23T01:26:45+01:00 **Result:** FAIL β€” `1 failed, 1 deselected in 13.31s` - - reproduced the GitHub Actions failure under a clean `HOME` - - the optimized in-process `help-only` path reused a root CLI app that had been imported against the original process home, so bundle commands like `project`, `spec`, `code`, `backlog`, and `govern` were missing even though the temp-home marketplace modules had been seeded correctly + +- reproduced the GitHub Actions failure under a clean `HOME` +- the optimized in-process `help-only` path reused a root CLI app that had been imported against the original process home, so bundle commands like `project`, `spec`, `code`, `backlog`, and `govern` were missing even though the temp-home marketplace modules had been seeded correctly **Command:** `hatch run pytest tests/integration/test_command_package_runtime_validation.py -q` **Timestamp:** 2026-03-23T01:26:45+01:00 **Result:** PASS β€” `2 passed in 24.87s` - - the help-only audit now rebuilds the existing root Typer app once per temp-home test run after resetting `CommandRegistry` and pointing discovery/installer roots at the temporary home + +- the help-only audit now rebuilds the existing root Typer app once per temp-home test run after resetting `CommandRegistry` and pointing discovery/installer roots at the temporary home **Command:** `HOME=/tmp/specfact-ci-empty-home SPECFACT_MODULES_REPO=/home/dom/git/nold-ai/specfact-cli-modules PYTHONPATH=/home/dom/git/nold-ai/specfact-cli-worktrees/bugfix/code-review-zero-findings/src:/home/dom/git/nold-ai/specfact-cli-worktrees/bugfix/code-review-zero-findings /home/dom/git/nold-ai/specfact-cli/.venv/bin/python -m pytest tests/integration/test_command_package_runtime_validation.py -q -k test_command_audit_help_cases_execute_cleanly_in_temp_home` **Timestamp:** 2026-03-23T01:26:45+01:00 **Result:** PASS β€” `1 passed, 1 deselected in 14.19s` - - confirms the CI-equivalent clean-home environment now sees the seeded workflow bundles during the fast in-process help audit path + +- confirms the CI-equivalent clean-home environment now sees the seeded workflow bundles during the fast in-process help audit path **Command:** `hatch run basedpyright tests/integration/test_command_package_runtime_validation.py` **Timestamp:** 2026-03-23T01:26:45+01:00 diff --git a/openspec/changes/archive/2026-03-31-code-review-zero-findings/design.md b/openspec/changes/archive/2026-03-31-code-review-zero-findings/design.md index 1c8e75db..8cb3dc4b 100644 --- a/openspec/changes/archive/2026-03-31-code-review-zero-findings/design.md +++ b/openspec/changes/archive/2026-03-31-code-review-zero-findings/design.md @@ -9,6 +9,7 @@ No new user-facing CLI commands or API surface changes are introduced. This is a ## Goals / Non-Goals **Goals:** + - Reduce basedpyright `reportUnknownMemberType` count from 1,531 to 0 by adding explicit type annotations to all untyped class members. - Eliminate all 352 `print-in-src` semgrep findings by replacing every `print()` call with `get_bridge_logger()` or `get_logger()`. - Add `@require` / `@ensure` / `@beartype` decorators to all 291 public functions flagged as `MISSING_ICONTRACT`. @@ -18,6 +19,7 @@ No new user-facing CLI commands or API surface changes are introduced. This is a - Establish a CI gate: `specfact review` must exit 0 on every PR targeting `dev` or `main`. **Non-Goals:** + - Changing any user-facing CLI command name, option, or output format. - Introducing new runtime dependencies. - Achieving CC=0 for orchestration scripts β€” only bring them below the error threshold (CC<16). diff --git a/openspec/changes/archive/2026-03-31-code-review-zero-findings/proposal.md b/openspec/changes/archive/2026-03-31-code-review-zero-findings/proposal.md index 8353cbd8..aa23d45e 100644 --- a/openspec/changes/archive/2026-03-31-code-review-zero-findings/proposal.md +++ b/openspec/changes/archive/2026-03-31-code-review-zero-findings/proposal.md @@ -17,9 +17,11 @@ SpecFact CLI's `specfact review` command is our flagship code-quality enforcemen ## Capabilities ### New Capabilities + - `dogfood-self-review`: Specification for running and passing `specfact review` against the specfact-cli repo itself β€” defines the self-review policy, acceptance criteria (0 findings, `overall_verdict: PASS`), and the CI gate that enforces it. ### Modified Capabilities + - `code-review-module`: The review tool must be able to scan itself; any self-referential edge cases (e.g., reviewing files that implement the reviewer) must be handled. - `debug-logging`: Logging migration extends the `get_bridge_logger()` contract to cover all `print()` replacement sites, including adapter and scripts layers. - `contract-runner`: The `MISSING_ICONTRACT` contract must produce actionable output for the 291 currently-uncovered public APIs. No rule changes β€” this is coverage expansion. @@ -37,6 +39,6 @@ SpecFact CLI's `specfact review` command is our flagship code-quality enforcemen ## Source Tracking - **GitHub Issue**: #423 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/423 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: open diff --git a/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/code-review-module/spec.md b/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/code-review-module/spec.md index 58875ed8..ce1e68fd 100644 --- a/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/code-review-module/spec.md +++ b/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/code-review-module/spec.md @@ -1,27 +1,33 @@ ## ADDED Requirements ### Requirement: Self-referential scan β€” review module can scan itself without errors + The `specfact-code-review` module SHALL be able to review its own source files (including the files that implement the reviewer) without infinite loops, false positives from meta-scanning, or unhandled exceptions. #### Scenario: Review run on specfact-cli repo completes without tool_error findings + - **WHEN** `specfact review` is run with the specfact-cli repo as the target - **THEN** no finding with `tool` equal to `code-review-module` or `category` equal to `tool_error` is produced - **AND** the run exits with code 0 (assuming all other findings are resolved) #### Scenario: Tool error finding is surfaced as error severity + - **WHEN** any configured tool fails to invoke (e.g., missing binary) - **THEN** a finding with `category="tool_error"` and `severity="error"` is produced - **AND** the finding message includes the tool name and failure reason ### Requirement: CI gate integration β€” review must be runnable non-interactively + The review module SHALL support a `--ci` or equivalent non-interactive flag that suppresses prompts, writes machine-readable output to `.specfact/code-review.json`, and exits with code 1 on any finding at severity `error` or higher. #### Scenario: Non-interactive CI run writes JSON report and exits non-zero on errors + - **WHEN** `specfact review run --ci` is executed and error-severity findings exist - **THEN** `.specfact/code-review.json` is written with `overall_verdict: "FAIL"` and `ci_exit_code: 1` - **AND** the process exits with code 1 #### Scenario: Non-interactive CI run exits zero on clean codebase + - **WHEN** `specfact review run --ci` is executed and no findings exist - **THEN** `.specfact/code-review.json` is written with `overall_verdict: "PASS"` and `ci_exit_code: 0` - **AND** the process exits with code 0 diff --git a/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/debug-logging/spec.md b/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/debug-logging/spec.md index 1579f0fc..774c0c1a 100644 --- a/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/debug-logging/spec.md +++ b/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/debug-logging/spec.md @@ -1,22 +1,27 @@ ## ADDED Requirements ### Requirement: Bridge logger used in all production source paths + Every production code path in `src/specfact_cli/` SHALL use `get_bridge_logger()` from `specfact_cli.common` for all diagnostic output, replacing any remaining `print()` builtin calls. #### Scenario: Adapter module writes diagnostic output via bridge logger + - **WHEN** an adapter module (e.g., `adapters/ado.py`, `adapters/github.py`) performs a network call or state change - **THEN** diagnostic messages are written via `logger = get_bridge_logger(__name__)` and `logger.debug(...)` / `logger.info(...)` - **AND** no `print()` call appears in the adapter module #### Scenario: Sync module writes diagnostic output via bridge logger + - **WHEN** `sync/bridge_sync.py` or `sync/spec_to_code.py` processes a file - **THEN** all progress and error messages are routed through `get_bridge_logger(__name__)` - **AND** no `print()` call appears in the sync module ### Requirement: Script-layer logging uses stdlib or Rich, not print() + Scripts in `scripts/` and `tools/` that run as standalone CLI programs SHALL use `logging.getLogger(__name__)` with a `StreamHandler` for progress output, or `rich.console.Console()` for formatted terminal output. The stdlib `print()` builtin SHALL NOT be used. #### Scenario: Standalone script writes progress via logging + - **WHEN** a script in `scripts/` needs to write a status message to stdout - **THEN** it calls `logging.getLogger(__name__).info(...)` or `console.print(...)` from a Rich Console instance - **AND** semgrep `print-in-src` reports zero findings for that script diff --git a/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/dogfood-self-review/spec.md b/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/dogfood-self-review/spec.md index f9d663b7..743e0751 100644 --- a/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/dogfood-self-review/spec.md +++ b/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/dogfood-self-review/spec.md @@ -1,93 +1,113 @@ ## ADDED Requirements ### Requirement: Self-review policy β€” specfact-cli runs specfact review on itself + The specfact-cli repository SHALL be subject to `specfact review` as a first-class CI gate, enforcing the same zero-finding standard we recommend to customers. #### Scenario: Review run on own repo produces zero findings + - **WHEN** `specfact review` is executed against the specfact-cli repository root - **THEN** `overall_verdict` is `PASS` - **AND** the findings array is empty - **AND** the process exits with code 0 #### Scenario: Review run failure blocks CI + - **WHEN** `specfact review` exits with code non-zero on a PR targeting `dev` or `main` - **THEN** the CI pipeline marks the PR check as failed - **AND** the PR cannot be merged until findings are resolved #### Scenario: Review result is machine-readable + - **WHEN** `specfact review --format json` is run in CI - **THEN** a JSON report is written to `.specfact/code-review.json` - **AND** the report schema_version is `1.0` - **AND** `overall_verdict`, `score`, `findings`, and `ci_exit_code` fields are present #### Scenario: Expanded clean-code categories stay at zero findings + - **GIVEN** the expanded clean-code pack is available from the review module - **WHEN** `specfact review` runs against the specfact-cli repository root with clean-code categories enabled - **THEN** categories `naming`, `kiss`, `yagni`, `dry`, and `solid` each report zero findings - **AND** the zero-finding proof is recorded in `TDD_EVIDENCE.md` ### Requirement: Type-safe codebase β€” zero basedpyright findings in strict mode + All public API class members and function signatures in `src/specfact_cli/` SHALL be explicitly typed so that `basedpyright` strict mode reports zero `reportUnknownMemberType`, `reportAttributeAccessIssue`, and `reportUnsupportedDunderAll` findings. #### Scenario: basedpyright strict mode passes on src/ + - **WHEN** `hatch run type-check` is executed - **THEN** basedpyright reports zero errors and zero warnings for files under `src/specfact_cli/` #### Scenario: Untyped class member introduced in PR fails CI + - **WHEN** a PR introduces a class member without a type annotation - **THEN** `hatch run type-check` exits non-zero - **AND** CI marks the type-check step as failed #### Scenario: TypedDict used for structured dict shapes + - **WHEN** a function accepts or returns a dict with a known schema - **THEN** a `TypedDict` or Pydantic model is used rather than `dict[str, Any]` - **AND** basedpyright infers the member types without `reportUnknownMemberType` ### Requirement: Print-free source β€” all production logging via bridge logger + No `print()` builtin calls SHALL appear in files under `src/specfact_cli/`, `scripts/`, or `tools/`, as detected by the semgrep `print-in-src` rule. #### Scenario: Logging call replaces print in adapter layer + - **WHEN** `get_bridge_logger()` is called in an adapter module (e.g., `adapters/ado.py`) - **THEN** structured log messages are routed to the debug log file when `--debug` is active - **AND** no `print()` call remains in the file - **AND** semgrep `print-in-src` reports zero findings for that file #### Scenario: Script-layer progress output uses Rich console or stdlib logging + - **WHEN** a script in `scripts/` or `tools/` needs to write progress to stdout - **THEN** it uses `rich.console.Console().print()` or `logging.getLogger(__name__)`, not the stdlib `print` builtin - **AND** semgrep `print-in-src` reports zero findings for that file ### Requirement: Full contract coverage β€” all public APIs carry icontract decorators + Every public function (non-underscore-prefixed) in `src/specfact_cli/` SHALL have at least one `@require` or `@ensure` decorator from icontract, and a `@beartype` decorator for runtime type enforcement. #### Scenario: Public function without @require fails contract_runner check + - **WHEN** `contract_runner` scans a file with a public function lacking `@require`/`@ensure` - **THEN** a `MISSING_ICONTRACT` finding is produced #### Scenario: Decorated public function produces no missing-contract finding + - **WHEN** a public function has both `@require` (or `@ensure`) and `@beartype` - **THEN** `contract_runner` produces zero `MISSING_ICONTRACT` findings for that function #### Scenario: Minimal meaningful contract per function + - **WHEN** a `@require` precondition is added to a public function - **THEN** the precondition checks a domain-meaningful invariant (e.g., path exists, non-empty string, valid enum) - **AND** the precondition is NOT a trivial `lambda x: x is not None` that merely restates the type #### Scenario: Utility contract exploration handles pathological strings gracefully + - **WHEN** CrossHair or unit tests exercise utility helpers with pathological string inputs such as control characters or malformed package names - **THEN** the helpers SHALL return a safe fallback value instead of raising unexpected exceptions - **AND** `hatch run contract-test` SHALL not report uncaught exceptions for those utility paths ### Requirement: Complexity budget β€” no function exceeds CC15 + No function in `src/specfact_cli/`, `scripts/`, or `tools/` SHALL have cyclomatic complexity >=16, as measured by radon. #### Scenario: High-complexity function split into helpers passes complexity check + - **WHEN** a function with CC>=16 is refactored into a top-level function and one or more private helpers - **THEN** `hatch run lint` (radon check) reports no CC>=16 findings for that function - **AND** each extracted helper has CC<10 #### Scenario: New code written during this change stays below threshold + - **WHEN** any new function is introduced during this change - **THEN** its cyclomatic complexity is <10 as measured by radon - **AND** no CC>=13 warning is raised for the new function diff --git a/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/review-cli-contracts/spec.md b/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/review-cli-contracts/spec.md index e1cc6430..ff4532e8 100644 --- a/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/review-cli-contracts/spec.md +++ b/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/review-cli-contracts/spec.md @@ -1,19 +1,23 @@ ## ADDED Requirements ### Requirement: Review CLI commands carry icontract and beartype decorators + All public command functions in the review module (`specfact code review run`, `ledger`, `rules`) SHALL have `@require` / `@ensure` decorators (icontract) and `@beartype` on their signatures, consistent with the project-wide contract-first standard. #### Scenario: review run command has precondition on repo_path + - **WHEN** `specfact code review run` is invoked with an invalid `repo_path` - **THEN** an icontract `ViolationError` is raised before any tool runner is invoked - **AND** the error message references the violated precondition #### Scenario: review CLI contracts are consistent with typed signatures + - **WHEN** `hatch run contract-test` is executed after type annotations are applied to the review CLI module - **THEN** `contract_runner` reports zero `MISSING_ICONTRACT` findings for review command functions - **AND** `basedpyright` reports zero type errors for the review CLI module #### Scenario: Contract validation scenarios cover review run with CI flag + - **GIVEN** `tests/cli-contracts/specfact-code-review-run.scenarios.yaml` exists - **WHEN** a scenario exercising `review run --ci` with a clean target is added - **THEN** the scenario validates exit code 0 and presence of `.specfact/code-review.json` diff --git a/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/review-run-command/spec.md b/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/review-run-command/spec.md index 5c964700..0e441f99 100644 --- a/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/review-run-command/spec.md +++ b/openspec/changes/archive/2026-03-31-code-review-zero-findings/specs/review-run-command/spec.md @@ -1,9 +1,11 @@ ## MODIFIED Requirements ### Requirement: End-to-End specfact code review run Command + The `specfact code review run` workflow SHALL support the dogfood self-review proof for the SpecFact CLI repository and emit a governed zero-finding report when remediation is complete. #### Scenario: Dogfood self-review on SpecFact CLI reaches zero tracked findings + - **GIVEN** the SpecFact CLI repository under the `code-review-zero-findings` remediation branch - **AND** the dogfood self-review tests in `tests/unit/specfact_cli/test_dogfood_self_review.py` - **WHEN** `specfact code review run --scope full --json --out ` is executed in an environment where the `code` bundle is installed diff --git a/openspec/changes/archive/2026-03-31-docs-05-core-site-ia-restructure/proposal.md b/openspec/changes/archive/2026-03-31-docs-05-core-site-ia-restructure/proposal.md index 50d2e1db..8c3c04a8 100644 --- a/openspec/changes/archive/2026-03-31-docs-05-core-site-ia-restructure/proposal.md +++ b/openspec/changes/archive/2026-03-31-docs-05-core-site-ia-restructure/proposal.md @@ -39,6 +39,6 @@ The core docs site at docs.specfact.io has a flat 5-section sidebar (Getting Sta - **GitHub Issue**: #438 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/438 +- **Issue URL**: - **Last Synced Status**: synced - **Sanitized**: true diff --git a/openspec/changes/archive/2026-03-31-docs-07-core-handoff-conversion/proposal.md b/openspec/changes/archive/2026-03-31-docs-07-core-handoff-conversion/proposal.md index 7768b813..e609698b 100644 --- a/openspec/changes/archive/2026-03-31-docs-07-core-handoff-conversion/proposal.md +++ b/openspec/changes/archive/2026-03-31-docs-07-core-handoff-conversion/proposal.md @@ -28,6 +28,6 @@ The core docs site currently has 20+ pages that contain full duplicate content o - **GitHub Issue**: #439 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/439 +- **Issue URL**: - **Last Synced Status**: synced - **Sanitized**: true diff --git a/openspec/changes/archive/2026-03-31-docs-12-docs-validation-ci/proposal.md b/openspec/changes/archive/2026-03-31-docs-12-docs-validation-ci/proposal.md index c1652121..6036b1f2 100644 --- a/openspec/changes/archive/2026-03-31-docs-12-docs-validation-ci/proposal.md +++ b/openspec/changes/archive/2026-03-31-docs-12-docs-validation-ci/proposal.md @@ -31,7 +31,7 @@ Documentation command examples can drift from actual CLI implementations as code - **GitHub Issue**: #440 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/440 +- **Issue URL**: - **Last Synced Status**: synced - **Sanitized**: true - **Cross-repo**: specfact-cli-modules/docs-12-docs-validation-ci diff --git a/openspec/changes/archive/2026-03-31-docs-13-core-nav-search-theme-roles/proposal.md b/openspec/changes/archive/2026-03-31-docs-13-core-nav-search-theme-roles/proposal.md index 8a964fdd..85039f77 100644 --- a/openspec/changes/archive/2026-03-31-docs-13-core-nav-search-theme-roles/proposal.md +++ b/openspec/changes/archive/2026-03-31-docs-13-core-nav-search-theme-roles/proposal.md @@ -41,7 +41,7 @@ The modules docs now have a richer navigation/search/theme model. The core site - **GitHub Issue**: #458 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/458 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: synced - **Sanitized**: true diff --git a/openspec/changes/archive/2026-03-31-docs-14-first-contact-story-and-onboarding/design.md b/openspec/changes/archive/2026-03-31-docs-14-first-contact-story-and-onboarding/design.md index 4db2b2d1..57e9330a 100644 --- a/openspec/changes/archive/2026-03-31-docs-14-first-contact-story-and-onboarding/design.md +++ b/openspec/changes/archive/2026-03-31-docs-14-first-contact-story-and-onboarding/design.md @@ -7,6 +7,7 @@ topology, modules, migration notes, and audience variants before a newcomer can SpecFact is relevant. The sharper product thesis coming out of the discovery work is: + - SpecFact is the validation and alignment layer for software delivery. - In greenfield and AI-assisted work, it adds the missing rigor layer that keeps fast generation from becoming unstable delivery. @@ -21,6 +22,7 @@ canonical top-level docs, while `specfact-cli-modules` owns module-deep workflow correct, but the handoff currently feels like internal structure rather than intentional onboarding. Stakeholders are: + - first-time visitors deciding whether to try SpecFact - returning users who need a fast path to the right docs surface - maintainers who need a stable messaging hierarchy that does not drift @@ -28,6 +30,7 @@ Stakeholders are: ## Goals / Non-Goals **Goals:** + - establish one canonical product story for all first-contact surfaces - make the validation-and-alignment USP explicit instead of implied - answer the core user questions consistently: @@ -43,6 +46,7 @@ Stakeholders are: - include GitHub repo metadata expectations so the product story starts before README scroll depth **Non-Goals:** + - redesign the entire information architecture of all docs - rewrite all deep reference pages or module-specific guides - change CLI runtime behavior or command ownership @@ -56,25 +60,30 @@ The repository landing, root README, `docs/index.md`, and modules homepage will coordinated onboarding surface rather than independent copy islands. Why: + - users evaluate the product across those touchpoints, not file-by-file - a split message creates hesitation even when each page is individually β€œgood” Alternative considered: + - improve each page independently without a shared message hierarchy - rejected because it tends to recreate drift and different answers to β€œwhat is SpecFact?” ### Decision: Lead with one primary identity sentence and one fast-start path Each first-contact surface will lead with: + - one identity statement - one primary value proposition - one short β€œstart here now” path Why: + - visitors need a fast go/no-go decision before they want product topology - a single path reduces overwhelm and increases trial intent Alternative considered: + - preserve multiple equal onboarding paths near the top - rejected because the current problem is over-choice and diluted focus @@ -84,11 +93,13 @@ The canonical story will define SpecFact first as the validation and alignment l delivery, with β€œkeep backlog, specs, tests, and code in sync” presented as the observable outcome. Why: + - β€œkeep in sync” is true but too generic on its own - validation plus alignment explains the value for AI-assisted coding, brownfield analysis, and enterprise governance in one frame Alternative considered: + - define the product primarily by the Swiss-knife metaphor or by enumerating command families - rejected because metaphor alone is not enough and capability lists obscure the USP @@ -100,9 +111,11 @@ handoff into spec-first tools, and module extensibility will remain as proof poi headline overload. Why: + - those details are strengths, but they are secondary to basic product comprehension Alternative considered: + - keep the current capability-dense hero - rejected because it communicates breadth before clarity @@ -113,9 +126,11 @@ Core docs will explain that `docs.specfact.io` is the default starting point and newcomers back to core docs if they are not yet oriented. Why: + - the repo split is an implementation detail until the user is ready for deeper workflows Alternative considered: + - merge all explanations into the README hero - rejected because it front-loads topology before value @@ -125,6 +140,7 @@ The change will encode the required first-contact questions and expected answers can be reviewed against a concrete standard. Why: + - without a framework, copy regresses toward β€œeverything SpecFact can do” - this creates an auditable quality bar for README/docs/repo metadata changes diff --git a/openspec/changes/archive/2026-03-31-docs-14-first-contact-story-and-onboarding/proposal.md b/openspec/changes/archive/2026-03-31-docs-14-first-contact-story-and-onboarding/proposal.md index 183846b6..82ea4de2 100644 --- a/openspec/changes/archive/2026-03-31-docs-14-first-contact-story-and-onboarding/proposal.md +++ b/openspec/changes/archive/2026-03-31-docs-14-first-contact-story-and-onboarding/proposal.md @@ -10,6 +10,7 @@ get, and how to start immediately. The sharper product truth is that SpecFact is the validation and alignment layer for software delivery in the age of AI-assisted coding: it reduces drift between backlog intent, specification, implementation, tests, and policy. That matters in four concrete situations: + - AI-assisted or β€œvibe-coded” greenfield work needs a validation layer so fast wins do not become fragile long-term liabilities. - Brownfield systems need reverse-engineered understanding and structured handoff into spec-first @@ -83,8 +84,8 @@ contact. - **GitHub Issue**: #466 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/466 +- **Issue URL**: - **Parent Feature**: #356 -- **Parent Feature URL**: https://github.com/nold-ai/specfact-cli/issues/356 +- **Parent Feature URL**: - **Last Synced Status**: open - **Sanitized**: true diff --git a/openspec/changes/archive/2026-03-31-init-ide-prompt-source-selection/proposal.md b/openspec/changes/archive/2026-03-31-init-ide-prompt-source-selection/proposal.md index f0ac4092..02a406e9 100644 --- a/openspec/changes/archive/2026-03-31-init-ide-prompt-source-selection/proposal.md +++ b/openspec/changes/archive/2026-03-31-init-ide-prompt-source-selection/proposal.md @@ -19,12 +19,14 @@ The original proposal treated this as only a simple prompt selector. That is no - ADD: When selected resources are missing, the command reports the missing owner/root and points users to the relevant core install/bootstrap flows such as `specfact module init --scope ` and `specfact module install --scope `. ## Capabilities + ### New Capabilities - `init-ide-prompt-selection`: `specfact init ide` can export prompts from core and selected installed modules with consistent interactive and non-interactive behavior. - `init-ide-installed-resource-orchestration`: `specfact init ide` can discover installed prompt/resource payloads from the effective module roots and direct users to the correct install/bootstrap command when those payloads are absent. ## Acceptance Criteria + - `specfact init ide` builds its prompt-source catalog from the effective installed module roots for the current repository context, including user scope, project scope, built-in core modules, and configured custom roots. - Default execution exports all discovered prompt sources by default rather than only the first matching root. - Interactive mode shows a source picker containing `core` plus installed module ids that actually contribute prompt resources. @@ -34,13 +36,13 @@ The original proposal treated this as only a simple prompt selector. That is no - The scope respects canonical command ownership from active migration changes and must not reintroduce obsolete command paths into prompt export or recommendations. ## Dependencies + - `backlog-module-ownership-cleanup` must land first so backlog prompt ownership is no longer split across core and module. - `packaging-02-cross-platform-runtime-and-module-resources` provides the installed-resource discovery foundation in `specfact-cli` and must stay the owner of payload discovery mechanics. - `specfact-cli-modules/packaging-01-bundle-resource-payloads` (`nold-ai/specfact-cli-modules#101`) must provide the bundle-owned prompt/template payloads that this change selects and exports. - `module-migration-11-project-codebase-ownership-realignment` must be treated as command-surface alignment context so exported prompts do not preserve obsolete grouped command ownership. - Existing `specfact module init` and `specfact module install` commands in `specfact-cli` remain the install/bootstrap path for user/project module roots; `init ide` extends only the post-install discovery/export path. - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-03-31-init-ide-prompt-source-selection/specs/init-ide-prompt-source-selection/spec.md b/openspec/changes/archive/2026-03-31-init-ide-prompt-source-selection/specs/init-ide-prompt-source-selection/spec.md index 535a42f4..c50b9b87 100644 --- a/openspec/changes/archive/2026-03-31-init-ide-prompt-source-selection/specs/init-ide-prompt-source-selection/spec.md +++ b/openspec/changes/archive/2026-03-31-init-ide-prompt-source-selection/specs/init-ide-prompt-source-selection/spec.md @@ -5,6 +5,7 @@ `specfact init ide` SHALL export all available prompt sources by default. #### Scenario: Default export includes core and installed modules across effective roots + - **WHEN** a user runs `specfact init ide` without restricting prompt sources - **THEN** prompt export includes core prompts - **AND** prompt export includes prompts from installed and enabled modules that provide prompt resources @@ -15,15 +16,18 @@ `specfact init ide` SHALL discover prompt and related module-owned resources from installed module roots and packaged resource directories. It SHALL not fetch module archives or treat the modules source repository as a runtime extraction source. #### Scenario: Installed project-scope bundle contributes prompt resources + - **WHEN** a repository has an installed module under `/.specfact/modules` - **THEN** `specfact init ide` can discover that module's packaged prompt resources for export in that repository. #### Scenario: Installed user-scope bundle contributes prompt resources + - **WHEN** a user has installed a module under `~/.specfact/modules` - **AND** no overriding project-scope copy shadows it - **THEN** `specfact init ide` can discover that module's packaged prompt resources for export. #### Scenario: Missing selected source does not trigger install work + - **WHEN** a selected prompt source is not installed or does not expose the required packaged resources - **THEN** `specfact init ide` fails or warns with actionable guidance - **AND** the guidance names the relevant scope and install/bootstrap command such as `specfact module init --scope project` or `specfact module install --scope user` @@ -34,6 +38,7 @@ Interactive `specfact init ide` SHALL allow users to choose prompt sources from installed options. #### Scenario: Interactive picker shows available sources + - **WHEN** `specfact init ide` runs in interactive mode - **THEN** it shows a multi-select source picker containing `core` and installed module ids with prompt resources - **AND** the selected sources determine which prompt resources are copied. @@ -43,11 +48,13 @@ Interactive `specfact init ide` SHALL allow users to choose prompt sources from Non-interactive `specfact init ide` SHALL accept a comma-separated prompt source selector. #### Scenario: Non-interactive selector accepts core and module ids + - **WHEN** a user runs `specfact init ide --prompts core,nold-ai/specfact-backlog` - **THEN** core prompts and the selected installed module prompts are copied - **AND** unrelated prompt sources are not copied. #### Scenario: Invalid or unavailable module source is rejected + - **WHEN** a user passes a prompt source token that is not `all`, not `core`, and not an installed module id with prompt resources - **THEN** the command fails with actionable guidance describing the invalid token and the available prompt sources. @@ -56,11 +63,13 @@ Non-interactive `specfact init ide` SHALL accept a comma-separated prompt source Exported prompts for VS Code / Copilot (under ``.github/prompts/``) and other multi-source IDE targets SHALL use a **flat** layout (no per-source subfolders) so editors and agents can discover ``specfact*.prompt.md`` (or equivalent) at the export root. #### Scenario: Core defers to modules on overlapping template basenames + - **WHEN** `core` and one or more installed modules expose the same source filename (e.g. ``specfact.01-import.md``) - **THEN** the prompt catalog SHALL list that basename only under the owning module source - **AND** `core` SHALL NOT duplicate that basename so exports are single-sourced. #### Scenario: Multiple module sources expose the same basename + - **WHEN** two installed modules expose the same template basename - **THEN** the merged export uses a deterministic last-wins rule by sorted source id (later id overwrites earlier) - **AND** the flat export contains exactly one file per output basename. diff --git a/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/design.md b/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/design.md index 52285d3b..4dade755 100644 --- a/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/design.md +++ b/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/design.md @@ -34,6 +34,7 @@ This is a cross-cutting change because it touches runtime behavior, module disco The runtime should derive a safe output mode from terminal interactivity and stream encoding, not just from TTY/color detection. When the active output encoding cannot represent configured Unicode glyphs, the CLI should either substitute ASCII-safe markers or run Rich in a mode that avoids unsupported glyph emission. Why this over "document UTF-8 only": + - the bug is user-facing on a supported platform - help/startup must be robust without bootstrap flags - graceful degradation is cheaper and more predictable than asking every caller to preconfigure environment variables @@ -48,6 +49,7 @@ Programmatic callers that land in the wrong interpreter or compiled dependency s - what supported invocation path the caller should use instead Why this over continuing with ad hoc `sys.path` fallback: + - path injection masks the real problem - compiled extensions such as `pydantic_core` cannot be made safe by string-based path hacks - explicit diagnostics give us a stable contract for Windows/Linux/macOS automation @@ -57,6 +59,7 @@ Why this over continuing with ad hoc `sys.path` fallback: `specfact init ide` should build its prompt export set from installed module packages and their packaged resource directories. Core init/install flows should use the same installed-package lookup model for other module-owned assets, beginning with backlog field mapping templates. The current hardcoded workflow prompt list is bundle-owned, so it should not live under `specfact_cli/resources/prompts`. Why this over keeping a core fallback: + - current prompt files represent workflow bundles, not core lifecycle commands - ownership should match installability and module provenance - dynamic discovery is the only way to stay correct when modules are optional or installed from different roots @@ -66,6 +69,7 @@ Why this over keeping a core fallback: This repo owns the runtime safety layer, installed-resource discovery contract, export/copy orchestration, and docs. The paired modules-repo change `packaging-01-bundle-resource-payloads` owns moving prompts and other bundle-owned resources into the released bundle packages. Why this split is now required: + - issue `#441` is rooted in the core CLI runtime and export flow - the audit verified the modules repo does not currently package the prompt payloads - the two repos now have distinct responsibilities that should be tracked separately diff --git a/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/proposal.md b/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/proposal.md index b1325d57..a4a9d722 100644 --- a/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/proposal.md +++ b/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/proposal.md @@ -14,6 +14,7 @@ Windows and mixed-environment automation currently fail in two places that shoul ## Capabilities ### New Capabilities + - `runtime-portability`: The CLI renders safely across terminal encodings and reports clear runtime-compatibility guidance for installation/interpreter mismatches. - `module-owned-ide-prompts`: `specfact init ide` discovers and exports prompt resources from installed modules and their packaged resource roots instead of from core-owned workflow prompt files. - `module-owned-runtime-resources`: core init/install flows resolve module-owned templates and similar assets from installed module packages rather than from core-owned fallback directories. diff --git a/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/specs/module-owned-ide-prompts/spec.md b/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/specs/module-owned-ide-prompts/spec.md index 54160efc..4dbe2262 100644 --- a/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/specs/module-owned-ide-prompts/spec.md +++ b/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/specs/module-owned-ide-prompts/spec.md @@ -1,40 +1,49 @@ ## ADDED Requirements ### Requirement: IDE prompt export SHALL use installed module resources + `specfact init ide` SHALL discover prompt templates from installed module packages and their packaged resource directories. The export flow SHALL not depend on workflow prompt files stored under the core CLI package for bundle-owned commands. #### Scenario: Installed bundle contributes prompt resources + - **WHEN** an installed module exposes packaged prompt resources for IDE export - **THEN** `specfact init ide` discovers that module's prompt directory from the installed module location - **AND** copies the prompt files from that module-owned resource path into the selected IDE folder #### Scenario: Core package does not masquerade as owner of bundle prompts + - **WHEN** workflow prompts exist only for bundle/module-owned commands - **THEN** the export catalog excludes equivalent core-owned fallback prompt files - **AND** prompt provenance remains attributable to the owning module ### Requirement: Missing prompt assets SHALL fail clearly + If a selected or installed module is expected to provide prompt resources but no packaged prompt directory is available, `specfact init ide` SHALL report an actionable error or warning that identifies the owning module and the missing resource path. #### Scenario: Selected module has no packaged prompt directory + - **WHEN** `specfact init ide` evaluates an installed module that should contribute prompts but its packaged prompt resource directory is absent - **THEN** the command reports which module is incomplete - **AND** the message explains that prompt resources must ship with the owning module package #### Scenario: Prompt discovery feeds later source selection + - **WHEN** the prompt export catalog is built for a repository with multiple installed modules - **THEN** the discovered prompt sources are available for later interactive or non-interactive source selection features - **AND** the catalog preserves module-level provenance for each exported prompt ### Requirement: Core init flows SHALL use installed module-owned template resources + When a setup or install flow needs a non-prompt resource that is owned by an extracted bundle, the core CLI SHALL resolve that asset from the installed bundle package instead of from a core-owned fallback directory. #### Scenario: Backlog field mapping templates resolve from installed backlog bundle + - **WHEN** a core init or setup flow needs backlog field mapping templates - **THEN** the CLI resolves those templates from the installed backlog bundle resource path - **AND** the flow does not require a canonical source copy under the core CLI repository #### Scenario: Missing module-owned template asset fails clearly + - **WHEN** a required installed bundle resource path for a module-owned template is absent - **THEN** the CLI reports which bundle-owned asset is missing - **AND** the message directs the user toward installing or updating the owning bundle diff --git a/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/specs/runtime-portability/spec.md b/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/specs/runtime-portability/spec.md index 25946c62..983444f4 100644 --- a/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/specs/runtime-portability/spec.md +++ b/openspec/changes/archive/2026-03-31-packaging-02-cross-platform-runtime-and-module-resources/specs/runtime-portability/spec.md @@ -1,27 +1,33 @@ ## ADDED Requirements ### Requirement: CLI output SHALL degrade safely on non-UTF-8 terminals + The SpecFact CLI SHALL render help, startup, and other common command output without raising encoding exceptions on supported Windows, Linux, and macOS terminals. When the active output stream cannot encode configured Unicode glyphs, the CLI SHALL switch to an ASCII-safe fallback for affected symbols instead of crashing. #### Scenario: Windows help rendering on a legacy code page + - **WHEN** a user runs a help or startup command in a terminal whose output encoding cannot represent the configured Unicode icons - **THEN** the CLI completes successfully - **AND** the rendered output uses encoding-safe fallback symbols for the unsupported glyphs #### Scenario: UTF-8 terminal preserves rich symbols + - **WHEN** a user runs the same help or startup command in a UTF-8-capable terminal - **THEN** the CLI completes successfully - **AND** the configured rich Unicode symbols remain enabled ### Requirement: Runtime mismatch diagnostics SHALL be actionable + When automation or programmatic invocation reaches a SpecFact installation whose runtime, module path, or compiled dependencies are incompatible with the calling environment, the CLI SHALL fail with a compatibility error that identifies the failing component and the resolved installation context. #### Scenario: External interpreter cannot load installed SpecFact module runtime + - **WHEN** a caller invokes backlog automation from a different interpreter environment than the one hosting the installed SpecFact stack - **THEN** the CLI fails with a compatibility error instead of a raw low-level import traceback - **AND** the error reports the unresolved module or compiled dependency - **AND** the error explains which interpreter or installation boundary must be used #### Scenario: Compatible installation resolves without manual path injection + - **WHEN** a caller invokes a supported SpecFact workflow from the interpreter that hosts the installed SpecFact runtime and module resources - **THEN** the CLI resolves the required runtime and module paths without requiring manual `.specfact/modules/...` injection diff --git a/openspec/changes/archive/2026-03-31-speckit-02-v04-adapter-alignment/TDD_EVIDENCE.md b/openspec/changes/archive/2026-03-31-speckit-02-v04-adapter-alignment/TDD_EVIDENCE.md index 359e5862..d87559bf 100644 --- a/openspec/changes/archive/2026-03-31-speckit-02-v04-adapter-alignment/TDD_EVIDENCE.md +++ b/openspec/changes/archive/2026-03-31-speckit-02-v04-adapter-alignment/TDD_EVIDENCE.md @@ -13,6 +13,7 @@ Production code for tasks 1.1–7.2 was written first (ToolCapabilities fields, **Result:** 110 passed in 4.90s New tests added: + - `TestToolCapabilitiesV04Fields` β€” 8 tests (backward compat, all new fields) - `TestScanExtensions` β€” 7 tests (catalog parsing, ignore, malformed JSON, merge) - `TestScanPresets` β€” 4 tests (JSON, directory, malformed fallback) diff --git a/openspec/changes/archive/2026-03-31-speckit-02-v04-adapter-alignment/design.md b/openspec/changes/archive/2026-03-31-speckit-02-v04-adapter-alignment/design.md index 61d85b9d..d87089ee 100644 --- a/openspec/changes/archive/2026-03-31-speckit-02-v04-adapter-alignment/design.md +++ b/openspec/changes/archive/2026-03-31-speckit-02-v04-adapter-alignment/design.md @@ -3,6 +3,7 @@ The `SpecKitAdapter` (in `src/specfact_cli/adapters/speckit.py`) was built when spec-kit had a simple layout: `specs/` or `.specify/specs/` directories containing `spec.md`, `plan.md`, `tasks.md`, and an optional `.specify/memory/constitution.md`. The adapter detects these directories, delegates parsing to `SpecKitScanner`, conversion to `SpecKitConverter`, and exposes `ToolCapabilities` with `version=None` and two sync modes. Spec-Kit v0.4.3 now has: + - 7+ slash commands (was 4) - 46 community extensions with their own commands, loaded from `extensions/catalog.community.json` - A pluggable preset system in `presets/` with catalog resolution (v0.3.0+) @@ -16,6 +17,7 @@ The adapter, scanner, capabilities model, and bridge config presets all need upd ## Goals / Non-Goals **Goals:** + - Detect and model spec-kit extensions installed in a target repository - Parse extension catalogs to expose extension-provided commands to SpecFact sync - Detect spec-kit version via CLI probe or directory heuristics @@ -25,6 +27,7 @@ The adapter, scanner, capabilities model, and bridge config presets all need upd - Maintain backward compatibility with repos using older spec-kit versions (pre-0.3.0) **Non-Goals:** + - Executing spec-kit extensions from SpecFact (we detect and model, not invoke) - Managing spec-kit presets (read-only detection) - Replacing spec-kit's own sync/reconcile extensions (we coordinate, not compete) @@ -43,6 +46,7 @@ Parse `extensions/catalog.community.json` and `extensions/catalog.core.json` as ### D2: Version detection with graceful degradation Three-tier version detection strategy: + 1. **CLI probe** (best): Run `specify --version` if CLI is on PATH β€” returns exact version 2. **Directory heuristics** (good): `presets/` dir β†’ `>=0.3.0`; `extensions/` dir β†’ `>=0.2.0`; `.specify/` dir only β†’ `>=0.1.0` 3. **Unknown** (fallback): `version=None` β€” same as today, no features gated @@ -104,6 +108,7 @@ Extension commands (e.g., `/speckit.reconcile.run`, `/speckit.sync.detect`) are ### D6: Scanner detects new directories without requiring spec-kit CLI `SpecKitScanner` adds detection for: + - `extensions/` directory β†’ extension catalog files - `presets/` directory β†’ preset catalog files - `.extensionignore` β†’ extension exclusion rules diff --git a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/design.md b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/design.md index aab8531f..d97d34c1 100644 --- a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/design.md +++ b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/design.md @@ -5,12 +5,14 @@ While the frontmatter schema provides ownership tracking, there's no enforcement mechanism to ensure documentation stays synchronized with source code changes. This design implements a CI-based enforcement system that automatically detects stale documentation and prevents merges until documentation is updated. ### Current State + - Frontmatter schema implemented for ownership tracking - No automated enforcement of documentation synchronization - Manual review process for documentation updates - No integration with CI/CD pipeline ### Constraints + - Must depend on `doc-frontmatter-schema` change - Must integrate with GitHub Actions workflows - Must support branch protection requirements @@ -19,6 +21,7 @@ While the frontmatter schema provides ownership tracking, there's no enforcement - Must follow existing CI/CD patterns in codebase ### Stakeholders + - Developers: Need clear feedback on documentation requirements - CI/CD Pipeline: Needs reliable enforcement mechanism - Code Reviewers: Need automated quality gates @@ -27,6 +30,7 @@ While the frontmatter schema provides ownership tracking, there's no enforcement ## Goals / Non-Goals ### Goals + - Implement CI algorithm to detect stale documentation - Create GitHub Actions workflow for enforcement - Integrate with branch protection @@ -35,6 +39,7 @@ While the frontmatter schema provides ownership tracking, there's no enforcement - Ensure zero errors policy compliance ### Non-Goals + - Automated documentation updates (future enhancement) - Real-time synchronization (future enhancement) - Cross-repository enforcement (out of scope) @@ -43,99 +48,120 @@ While the frontmatter schema provides ownership tracking, there's no enforcement ## Decisions ### 1. Sync Algorithm Implementation + **Decision**: Implement sync algorithm in Python using git diff and glob matching **Rationale**: + - Python is consistent with codebase language choice - Git diff provides reliable change detection - Glob matching supports flexible tracking patterns - Easy to integrate with GitHub Actions **Alternatives Considered**: + - Shell script: Less maintainable, harder to test - GitHub Actions built-in: Not flexible enough for complex logic - External service: Would add unnecessary complexity and dependencies ### 2. GitHub Actions Workflow + **Decision**: Create dedicated workflow file `.github/workflows/docs-sync.yml` **Rationale**: + - Dedicated workflow allows independent execution and testing - Follows existing GitHub Actions patterns in codebase - Easy to monitor and debug separately - Can be disabled temporarily if needed **Alternatives Considered**: + - Integrated into existing CI workflow: Would complicate existing validation - Reusable workflow: Overkill for current needs - Third-party action: Would add external dependency ### 3. Change Detection Strategy + **Decision**: Use `git diff --name-only ...` for change detection **Rationale**: + - Standard git command with reliable results - Provides exact list of changed files - Works with GitHub Actions PR context - Supports both merge and rebase workflows **Alternatives Considered**: + - GitHub API: More complex, rate limit concerns - File system comparison: Unreliable, doesn't work with PR context - Custom git implementation: Reinventing the wheel ### 4. Branch Protection Integration + **Decision**: Make docs sync check a required status check for main branch **Rationale**: + - Ensures documentation quality before merge - Follows existing branch protection patterns - Provides clear feedback to developers - Prevents documentation drift at source **Alternatives Considered**: + - Warning-only status: Wouldn't enforce quality standards - Optional check: Wouldn't be effective - Post-merge enforcement: Too late to prevent drift ### 5. Error Reporting Format + **Decision**: Provide clear, actionable error messages listing stale documents **Rationale**: + - Developers need to know exactly what to fix - Clear formatting improves user experience - Actionable guidance reduces resolution time - Follows CLI best practices **Alternatives Considered**: + - Generic error messages: Poor user experience - Interactive resolution: Would complicate CI integration - Automated fixes: Too risky for documentation content ### 6. Exemption Mechanism + **Decision**: Support `docs-exempt` label for intentional exemptions **Rationale**: + - Some changes legitimately don't require doc updates - Provides escape hatch for special cases - Follows GitHub label patterns - Easy to implement and understand **Alternatives Considered**: + - Configuration file: More complex to manage - Command-line flag: Doesn't work with CI - No exemption mechanism: Too rigid ### 7. Test-Driven Development Approach + **Decision**: Follow strict TDD-first approach with contract testing **Rationale**: + - Ensures robust implementation from the start - Provides living documentation through tests - Follows codebase conventions and quality standards - Enables safe refactoring and maintenance **Alternatives Considered**: + - Test-last approach: Higher risk of bugs and regressions - No formal testing: Unacceptable for quality standards - Integration testing only: Would miss edge cases @@ -143,48 +169,60 @@ While the frontmatter schema provides ownership tracking, there's no enforcement ## Risks / Trade-offs ### [False Positives in Stale Detection] β†’ Mitigation + **Risk**: Algorithm may incorrectly flag documents as stale **Mitigation**: + - Implement comprehensive test coverage - Provide clear error messages with context - Allow easy exemption process - Monitor and refine algorithm post-deployment ### [Performance Impact on CI] β†’ Mitigation + **Risk**: Sync check may slow down CI pipeline **Mitigation**: + - Optimize git diff and glob matching - Implement efficient file processing - Set performance requirements in specs - Monitor CI execution times ### [Developer Frustration] β†’ Mitigation + **Risk**: Developers may find enforcement frustrating **Mitigation**: + - Provide clear documentation and examples - Offer helpful error messages with guidance - Support exemption mechanism for special cases - Phase rollout with education and support ### [Complex Change Detection] β†’ Mitigation + **Risk**: Git diff may not handle all edge cases correctly **Mitigation**: + - Test with various git scenarios (merges, rebases, etc.) - Implement comprehensive error handling - Provide fallback mechanisms where needed - Monitor and address issues post-deployment ### [Branch Protection Complexity] β†’ Mitigation + **Risk**: Branch protection changes may cause CI disruptions **Mitigation**: + - Test branch protection changes in staging first - Provide clear documentation for maintainers - Offer rollback procedure - Monitor branch protection status ### [Exemption Abuse] β†’ Mitigation + **Risk**: Developers may overuse exemption mechanism **Mitigation**: + - Monitor exemption usage patterns - Provide guidelines for appropriate use - Review frequent exemptions @@ -193,30 +231,35 @@ While the frontmatter schema provides ownership tracking, there's no enforcement ## Migration Plan ### Phase 1: Algorithm Development + 1. Implement sync algorithm with comprehensive tests 2. Create GitHub Actions workflow file 3. Test with sample repositories and scenarios 4. Validate error reporting and formatting ### Phase 2: Integration Testing + 1. Test workflow in staging environment 2. Validate branch protection integration 3. Test exemption mechanism 4. Monitor performance and resource usage ### Phase 3: Gradual Rollout + 1. Enable workflow on select branches first 2. Monitor and address any issues 3. Provide team education and support 4. Gradually expand to all branches ### Phase 4: Full Enforcement + 1. Make docs sync check required for main branch 2. Update documentation with CI workflow details 3. Monitor compliance and effectiveness 4. Celebrate successful implementation ### Rollback Strategy + 1. Disable required status check if critical issues arise 2. Revert to warning-only mode temporarily 3. Address issues in algorithm or workflow @@ -229,4 +272,4 @@ While the frontmatter schema provides ownership tracking, there's no enforcement 3. **Error Severity**: Should we distinguish between critical and warning-level documentation issues? 4. **CI Integration Timing**: Should docs sync check run before or after other CI checks for optimal developer experience? 5. **Gradual Enforcement**: Should we implement a warning period before hard enforcement to allow team adaptation? -6. **Large PR Handling**: How should we handle very large PRs with many file changes to avoid performance issues? \ No newline at end of file +6. **Large PR Handling**: How should we handle very large PRs with many file changes to avoid performance issues? diff --git a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/proposal.md b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/proposal.md index b6150595..1b7ea3b9 100644 --- a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/proposal.md +++ b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/proposal.md @@ -5,6 +5,7 @@ While the frontmatter schema provides ownership tracking, there's no enforcement mechanism to ensure documentation stays synchronized with source code changes. Developers can modify tracked source files without updating corresponding documentation, leading to documentation drift. This change implements CI enforcement to: + - Automatically detect when source files change but tracked docs don't - Fail PRs with stale documentation - Provide clear guidance on what needs updating @@ -15,12 +16,14 @@ This change implements CI enforcement to: This change implements a CI-based documentation synchronization check: ### New Components + - **Sync Algorithm**: Detects stale documentation based on frontmatter tracking - **GitHub Workflow**: `.github/workflows/docs-sync.yml` for PR validation - **Sync Script**: `scripts/check-docs-sync.py` for algorithm implementation - **Branch Protection**: Required status check for main branch ### Modified Components + - **CI Configuration**: New workflow added to GitHub Actions - **Branch Protection**: Updated to require docs sync check - **Documentation**: Updated with CI workflow documentation @@ -28,37 +31,44 @@ This change implements a CI-based documentation synchronization check: ## Capabilities ### New Capabilities + - `docs-sync-algorithm`: CI sync algorithm specification and implementation - `github-workflow`: GitHub Actions workflow for docs sync checking - `ci-integration`: Branch protection setup and configuration ### Modified Capabilities + - `doc-frontmatter-schema`: Extended with CI integration requirements ## Impact ### Files to Create + - `scripts/check-docs-sync.py` - Sync algorithm implementation - `.github/workflows/docs-sync.yml` - GitHub Actions workflow - `docs/contributing/ci-docs-sync.md` - CI workflow documentation ### Files to Modify + - `.github/settings.yml` - Branch protection configuration - Existing documentation - Updated with CI workflow information ### Development Workflow + - PRs that modify tracked source files must update corresponding docs - CI provides clear error messages for stale documentation - Developers get immediate feedback on documentation requirements - Optional `docs-exempt` label for intentional exemptions ### Quality Gates + - Zero errors policy: CI workflow must pass before merge - TDD-first approach: Tests for sync algorithm created before implementation - Specfact code review: All changes go through review process - Git worktree patterns: Use git worktrees for isolated development ### GitHub Integration + - GitHub issue sync via specfact after openspec change creation - Proper labels: `documentation`, `quality`, `ci`, `automation` - Link to parent epic: `feature/docs-sync-epic` @@ -67,12 +77,14 @@ This change implements a CI-based documentation synchronization check: ## Success Criteria ### Technical Success + - βœ… CI docs sync check passes on all PRs with updated docs - βœ… CI fails appropriately when docs are stale - βœ… Sync algorithm correctly identifies affected documentation - βœ… Zero errors in all quality gates ### Process Success + - βœ… Openspec change follows spec-driven schema - βœ… Git worktree patterns used for isolation - βœ… Specfact code review completes with zero findings @@ -93,4 +105,4 @@ This change implements a CI-based documentation synchronization check: - Caching mechanism for glob pattern matching - Automated doc update suggestions - Impact analysis with change previews -- Interactive doc update assistance \ No newline at end of file +- Interactive doc update assistance diff --git a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/ci-integration/spec.md b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/ci-integration/spec.md index 6e9ecd15..d32e0e54 100644 --- a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/ci-integration/spec.md +++ b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/ci-integration/spec.md @@ -3,66 +3,81 @@ ## ADDED Requirements ### Requirement: Branch Protection Configuration + The system SHALL configure branch protection to require docs sync check. #### Scenario: Main branch protection + - **WHEN** branch protection is configured - **THEN** main branch SHALL require docs sync check - **AND** prevent merges with failing checks #### Scenario: Develop branch protection + - **WHEN** branch protection is configured - **THEN** develop branch SHALL require docs sync check - **AND** prevent merges with failing checks ### Requirement: Status Check Integration + The system SHALL integrate docs sync check as a required status check. #### Scenario: Required status check setup + - **WHEN** CI integration is complete - **THEN** docs sync check SHALL be required status check - **AND** appear in branch protection settings #### Scenario: Status check enforcement + - **WHEN** PR has failing docs sync check - **THEN** PR SHALL be blocked from merge - **AND** clear error SHALL be shown ### Requirement: Exemption Label Support + The system SHALL support exemption labels for intentional documentation exemptions. #### Scenario: Docs exempt label + - **WHEN** PR has `docs-exempt` label - **THEN** docs sync check SHALL be skipped - **AND** PR SHALL not be blocked #### Scenario: No exemption label + - **WHEN** PR doesn't have exemption label - **THEN** docs sync check SHALL run normally - **AND** enforce documentation requirements ### Requirement: Error Reporting Integration + The system SHALL integrate error reporting with GitHub UI. #### Scenario: Clear error messages + - **WHEN** docs sync check fails - **THEN** error messages SHALL appear in GitHub UI - **AND** be clearly formatted #### Scenario: Actionable guidance + - **WHEN** docs sync check fails - **THEN** output SHALL provide actionable guidance - **AND** list specific documents to update ### Requirement: Configuration Management + The system SHALL manage CI configuration appropriately. #### Scenario: Configuration file updates + - **WHEN** CI integration is implemented - **THEN** configuration files SHALL be updated - **AND** changes SHALL be version controlled #### Scenario: Backward compatibility + - **WHEN** CI integration is implemented - **THEN** existing workflows SHALL not be disrupted - **AND** backward compatibility SHALL be maintained @@ -70,17 +85,21 @@ The system SHALL manage CI configuration appropriately. ## Contract Requirements ### Requirement: Configuration Validation + CI configuration SHALL be validated before activation. #### Scenario: Configuration syntax validation + - **WHEN** configuration is updated - **THEN** it SHALL pass syntax validation - **AND** have no errors ### Requirement: Security Contracts + CI integration SHALL follow security best practices. #### Scenario: Secure workflow execution + - **WHEN** workflow runs - **THEN** it SHALL follow security best practices - **AND** not expose sensitive information @@ -88,17 +107,21 @@ CI integration SHALL follow security best practices. ## Integration Requirements ### Requirement: GitHub API Integration + The system SHALL integrate with GitHub API appropriately. #### Scenario: API rate limit handling + - **WHEN** workflow uses GitHub API - **THEN** it SHALL handle rate limits appropriately - **AND** retry when necessary ### Requirement: Existing CI Integration + The system SHALL integrate with existing CI infrastructure. #### Scenario: Compatibility with existing workflows + - **WHEN** docs sync check is added - **THEN** it SHALL be compatible with existing workflows - **AND** not disrupt current processes @@ -106,17 +129,21 @@ The system SHALL integrate with existing CI infrastructure. ## Performance Requirements ### Requirement: CI Performance Impact + The system SHALL minimize performance impact on CI. #### Scenario: Workflow execution time + - **WHEN** docs sync check runs - **THEN** it SHALL not significantly impact CI time - **AND** complete within reasonable limits ### Requirement: Resource Usage + The system SHALL use CI resources efficiently. #### Scenario: Resource efficient execution + - **WHEN** workflow runs - **THEN** resource usage SHALL be efficient - **AND** not exceed GitHub Actions limits @@ -124,27 +151,33 @@ The system SHALL use CI resources efficiently. ## Documentation Requirements ### Requirement: CI Documentation + The system SHALL provide documentation for CI integration. #### Scenario: Setup documentation + - **WHEN** user reads CI documentation - **THEN** they SHALL find clear setup instructions - **AND** configuration examples #### Scenario: Troubleshooting guide + - **WHEN** user encounters CI issues - **THEN** they SHALL find troubleshooting guide - **AND** common solutions ### Requirement: Developer Guidance + The system SHALL provide guidance for developers. #### Scenario: Workflow explanation + - **WHEN** developer reads documentation - **THEN** they SHALL understand CI workflow - **AND** how to work with it #### Scenario: Error resolution guide + - **WHEN** developer encounters CI errors - **THEN** they SHALL find error resolution guide -- **AND** step-by-step instructions \ No newline at end of file +- **AND** step-by-step instructions diff --git a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/docs-sync-algorithm/spec.md b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/docs-sync-algorithm/spec.md index c7eb2698..334eea28 100644 --- a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/docs-sync-algorithm/spec.md +++ b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/docs-sync-algorithm/spec.md @@ -3,74 +3,92 @@ ## ADDED Requirements ### Requirement: Change Detection Algorithm + The system SHALL implement an algorithm to detect when source files change but tracked documentation doesn't. #### Scenario: Source change without doc update + - **WHEN** source files matching `tracks` patterns change - **AND** corresponding docs are not updated - **THEN** algorithm SHALL detect stale documentation #### Scenario: Source and doc both updated + - **WHEN** source files change - **AND** corresponding docs are also updated - **THEN** algorithm SHALL not detect stale documentation ### Requirement: Git Diff Integration + The system SHALL integrate with git to detect changed files between commits. #### Scenario: Git diff between base and head + - **WHEN** algorithm runs on PR - **THEN** it SHALL use `git diff --name-only ...` - **AND** correctly identify changed files #### Scenario: Multiple changed files + - **WHEN** multiple files change in PR - **THEN** algorithm SHALL process all changed files - **AND** identify all affected documentation ### Requirement: Glob Pattern Matching + The system SHALL support glob pattern matching for tracking relationships. #### Scenario: Single glob pattern match + - **WHEN** changed file matches single glob pattern in `tracks` - **THEN** corresponding doc SHALL be marked for sync check #### Scenario: Multiple glob pattern matches + - **WHEN** changed file matches multiple glob patterns - **THEN** all corresponding docs SHALL be marked for sync check ### Requirement: Stale Documentation Identification + The system SHALL correctly identify stale documentation. #### Scenario: Doc not in changed files + - **WHEN** source files change matching doc's `tracks` - **AND** doc file itself is not in changed files - **THEN** doc SHALL be marked as stale #### Scenario: Doc in changed files + - **WHEN** source files change matching doc's `tracks` - **AND** doc file is in changed files - **THEN** doc SHALL not be marked as stale ### Requirement: Exempt Documentation Handling + The system SHALL properly handle exempt documentation. #### Scenario: Exempt doc with source changes + - **WHEN** source files change matching exempt doc's `tracks` - **THEN** doc SHALL not be marked as stale #### Scenario: Non-exempt doc processing + - **WHEN** source files change matching non-exempt doc's `tracks` - **THEN** doc SHALL undergo normal sync check ### Requirement: Error Reporting + The system SHALL provide clear error reporting for stale documentation. #### Scenario: Single stale document + - **WHEN** one document is stale - **THEN** error output SHALL list that document clearly #### Scenario: Multiple stale documents + - **WHEN** multiple documents are stale - **THEN** error output SHALL list all stale documents - **AND** format SHALL be clear and readable @@ -78,18 +96,23 @@ The system SHALL provide clear error reporting for stale documentation. ## Contract Requirements ### Requirement: Algorithm Contracts + The sync algorithm SHALL use `@icontract` decorators: + - `@require` for input validation - `@ensure` for result correctness #### Scenario: Invalid git references + - **WHEN** algorithm receives invalid git references - **THEN** `@require` contract SHALL raise appropriate exception ### Requirement: Performance Contracts + The algorithm SHALL have performance guarantees. #### Scenario: Large repository performance + - **WHEN** algorithm processes large repository - **THEN** execution SHALL complete in reasonable time - **AND** memory usage SHALL be efficient @@ -97,20 +120,25 @@ The algorithm SHALL have performance guarantees. ## Integration Requirements ### Requirement: GitHub Actions Integration + The algorithm SHALL integrate with GitHub Actions workflow. #### Scenario: Workflow execution + - **WHEN** workflow runs on PR - **THEN** algorithm SHALL receive correct base/head references - **AND** process changes appropriately ### Requirement: Exit Code Contract + The algorithm SHALL use appropriate exit codes. #### Scenario: No stale documents + - **WHEN** no stale documents found - **THEN** algorithm SHALL exit with code 0 #### Scenario: Stale documents found + - **WHEN** stale documents found -- **THEN** algorithm SHALL exit with code 1 \ No newline at end of file +- **THEN** algorithm SHALL exit with code 1 diff --git a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/github-workflow/spec.md b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/github-workflow/spec.md index af4bb8c1..972b9fe9 100644 --- a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/github-workflow/spec.md +++ b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/specs/github-workflow/spec.md @@ -3,71 +3,87 @@ ## ADDED Requirements ### Requirement: Workflow File Structure + The system SHALL provide a GitHub Actions workflow file `.github/workflows/docs-sync.yml` with proper structure. #### Scenario: Valid workflow file + - **WHEN** workflow file is created - **THEN** it SHALL have valid YAML structure - **AND** follow GitHub Actions best practices #### Scenario: Workflow triggers + - **WHEN** workflow file is examined - **THEN** it SHALL trigger on pull_request events - **AND** target main and develop branches ### Requirement: Workflow Steps + The workflow SHALL include all necessary steps for docs sync checking. #### Scenario: Checkout step + - **WHEN** workflow runs - **THEN** first step SHALL checkout repository - **AND** use fetch-depth: 0 for full history #### Scenario: Python setup step + - **WHEN** workflow runs - **THEN** it SHALL setup Python environment - **AND** install PyYAML dependency #### Scenario: Docs sync check step + - **WHEN** workflow runs - **THEN** it SHALL execute docs sync script - **AND** pass base/head references correctly ### Requirement: Environment Configuration + The workflow SHALL properly configure the execution environment. #### Scenario: Python version + - **WHEN** workflow runs - **THEN** it SHALL use Python 3.12 - **AND** environment SHALL be properly configured #### Scenario: Dependency installation + - **WHEN** workflow runs - **THEN** it SHALL install required dependencies - **AND** handle installation errors appropriately ### Requirement: Error Handling + The workflow SHALL handle errors appropriately. #### Scenario: Script execution failure + - **WHEN** docs sync script fails - **THEN** workflow SHALL fail - **AND** provide clear error output #### Scenario: Workflow timeout + - **WHEN** workflow exceeds timeout - **THEN** it SHALL fail gracefully - **AND** provide timeout information ### Requirement: Output Formatting + The workflow SHALL provide well-formatted output. #### Scenario: Success output + - **WHEN** docs sync check passes - **THEN** output SHALL show success message - **AND** be clearly formatted #### Scenario: Failure output + - **WHEN** docs sync check fails - **THEN** output SHALL show error details - **AND** list stale documents clearly @@ -75,17 +91,21 @@ The workflow SHALL provide well-formatted output. ## Contract Requirements ### Requirement: Workflow Validation + The workflow file SHALL be validated before use. #### Scenario: YAML validation + - **WHEN** workflow file is created - **THEN** it SHALL pass YAML validation - **AND** have no syntax errors ### Requirement: GitHub Actions Compatibility + The workflow SHALL be compatible with GitHub Actions. #### Scenario: GitHub Actions syntax + - **WHEN** workflow file is examined - **THEN** it SHALL use correct GitHub Actions syntax - **AND** supported features only @@ -93,17 +113,21 @@ The workflow SHALL be compatible with GitHub Actions. ## Integration Requirements ### Requirement: Branch Protection Integration + The workflow SHALL integrate with branch protection. #### Scenario: Required status check + - **WHEN** workflow is configured - **THEN** it SHALL be set as required status check - **AND** protect main branch ### Requirement: PR Integration + The workflow SHALL integrate with pull request workflow. #### Scenario: PR status updates + - **WHEN** workflow runs on PR - **THEN** it SHALL update PR status appropriately - **AND** provide clear status messages @@ -111,16 +135,20 @@ The workflow SHALL integrate with pull request workflow. ## Performance Requirements ### Requirement: Workflow Execution Time + The workflow SHALL execute within reasonable time limits. #### Scenario: Normal execution time + - **WHEN** workflow runs on typical PR - **THEN** execution SHALL complete in < 30 seconds ### Requirement: Resource Usage + The workflow SHALL use resources efficiently. #### Scenario: Memory usage + - **WHEN** workflow runs - **THEN** memory usage SHALL be reasonable -- **AND** not exceed GitHub Actions limits \ No newline at end of file +- **AND** not exceed GitHub Actions limits diff --git a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/tasks.md b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/tasks.md index 181fca21..3dfb4cf1 100644 --- a/openspec/changes/archive/2026-04-05-ci-docs-sync-check/tasks.md +++ b/openspec/changes/archive/2026-04-05-ci-docs-sync-check/tasks.md @@ -3,6 +3,7 @@ ## TDD / SDD order (enforced) Per config.yaml and design.md, this change follows strict TDD-first ordering: + 1. Spec deltas first (already created in specs/) 2. Tests second (expect failure initially) 3. Code last (implement to pass tests) @@ -33,6 +34,7 @@ Do not implement production code until tests exist and have been run (expecting ## 3. Test Implementation (TDD - Create Tests First) ### 3.1 Sync Algorithm Tests + - [ ] 3.1.1 Create `tests/unit/scripts/test_docs_sync/test_algorithm.py` - [ ] 3.1.1.1 Test change detection with git diff - [ ] 3.1.1.2 Test glob pattern matching @@ -45,6 +47,7 @@ Do not implement production code until tests exist and have been run (expecting - [ ] 3.1.2.2 Capture failure output in `openspec/changes/ci-docs-sync-check/TDD_EVIDENCE.md` ### 3.2 GitHub Workflow Tests + - [ ] 3.2.1 Create `tests/unit/scripts/test_docs_sync/test_workflow.py` - [ ] 3.2.1.1 Test workflow file validation - [ ] 3.2.1.2 Test workflow step execution @@ -55,6 +58,7 @@ Do not implement production code until tests exist and have been run (expecting - [ ] 3.2.2.2 Capture failure output ### 3.3 Integration Tests + - [ ] 3.3.1 Create `tests/integration/scripts/test_docs_sync/test_integration.py` - [ ] 3.3.1.1 Test end-to-end sync check workflow - [ ] 3.3.1.2 Test GitHub Actions integration @@ -67,6 +71,7 @@ Do not implement production code until tests exist and have been run (expecting ## 4. Implementation (Create Code to Pass Tests) ### 4.1 Sync Algorithm Implementation + - [ ] 4.1.1 Create `scripts/check-docs-sync.py` - [ ] 4.1.1.1 Implement `get_changed_files(base, head)` function with `@icontract` and `@beartype` - [ ] 4.1.1.2 Implement `parse_frontmatter(path)` function @@ -74,6 +79,7 @@ Do not implement production code until tests exist and have been run (expecting - [ ] 4.1.1.4 Implement main sync check algorithm ### 4.2 GitHub Workflow Implementation + - [ ] 4.2.1 Create `.github/workflows/docs-sync.yml` - [ ] 4.2.1.1 Implement checkout step with fetch-depth: 0 - [ ] 4.2.1.2 Implement Python setup step @@ -81,11 +87,13 @@ Do not implement production code until tests exist and have been run (expecting - [ ] 4.2.1.4 Add proper error handling and output formatting ### 4.3 Command-Line Interface + - [ ] 4.3.1 Add argument parsing to `check-docs-sync.py` - [ ] 4.3.1.1 Implement `--base` and `--head` arguments - [ ] 4.3.1.2 Add help text and usage examples ### 4.4 Test Execution and Evidence + - [ ] 4.4.1 Run all tests and verify they now pass - [ ] 4.4.1.1 `pytest tests/unit/scripts/test_docs_sync/ -v` - [ ] 4.4.1.2 `pytest tests/integration/scripts/test_docs_sync/ -v` @@ -94,45 +102,53 @@ Do not implement production code until tests exist and have been run (expecting ## 5. Quality Gates and Validation ### 5.1 Code Quality Checks + - [ ] 5.1.1 Run formatting: `hatch run format` - [ ] 5.1.2 Run type checking: `hatch run type-check` - [ ] 5.1.3 Run linting: `hatch run lint` - [ ] 5.1.4 Fix any issues found ### 5.2 Contract Validation + - [ ] 5.2.1 Run contract tests: `hatch run contract-test` - [ ] 5.2.2 Ensure all `@icontract` and `@beartype` decorators work correctly ### 5.3 OpenSpec Validation + - [ ] 5.3.1 Run `openspec validate ci-docs-sync-check --strict` - [ ] 5.3.2 Fix any validation issues ## 6. Documentation Updates ### 6.1 CI Workflow Documentation + - [ ] 6.1.1 Create `docs/contributing/ci-docs-sync.md` - [ ] 6.1.1.1 Document CI workflow structure - [ ] 6.1.1.2 Explain sync algorithm behavior - [ ] 6.1.1.3 Provide troubleshooting guide ### 6.2 Developer Guidance + - [ ] 6.2.1 Add CI integration section to contributing docs - [ ] 6.2.1.1 Explain how CI enforcement works - [ ] 6.2.1.2 Document exemption process - [ ] 6.2.1.3 Provide error resolution examples ### 6.3 Update README + - [ ] 6.3.1 Update README with CI workflow information - [ ] 6.3.2 Add badges for docs sync status ## 7. GitHub Integration ### 7.1 Branch Protection Configuration + - [ ] 7.1.1 Update branch protection settings - [ ] 7.1.1.1 Make docs sync check required status check - [ ] 7.1.1.2 Configure for main and develop branches ### 7.2 Test Workflow Execution + - [ ] 7.2.1 Test workflow on sample PR - [ ] 7.2.1.1 Create test PR with documentation changes - [ ] 7.2.1.2 Verify workflow executes correctly @@ -141,11 +157,13 @@ Do not implement production code until tests exist and have been run (expecting ## 8. Sample Implementation and Testing ### 8.1 Test with Sample Repository + - [ ] 8.1.1 Create test repository with sample docs - [ ] 8.1.2 Add frontmatter to sample files - [ ] 8.1.3 Test sync algorithm with various scenarios ### 8.2 Test Error Scenarios + - [ ] 8.2.1 Create scenarios with stale documentation - [ ] 8.2.2 Test workflow error detection - [ ] 8.2.3 Verify error messages are actionable @@ -153,6 +171,7 @@ Do not implement production code until tests exist and have been run (expecting ## 9. Create GitHub Issue and PR ### 9.1 Create GitHub Issue + - [ ] 9.1.1 Create issue in nold-ai/specfact-cli - [ ] 9.1.1.1 Title: `[Change] CI Docs Sync Check` - [ ] 9.1.1.2 Labels: `enhancement`, `change-proposal`, `documentation`, `quality`, `ci` @@ -161,6 +180,7 @@ Do not implement production code until tests exist and have been run (expecting - [ ] 9.1.1.5 Note dependency on `doc-frontmatter-schema` change ### 9.2 Create Pull Request + - [ ] 9.2.1 Prepare commit with all changes - [ ] 9.2.1.1 `git add .` - [ ] 9.2.1.2 `git commit -m "feat: add CI docs sync check"` @@ -181,4 +201,4 @@ Do not implement production code until tests exist and have been run (expecting - [ ] 10.3 `git worktree remove ../specfact-cli-worktrees/feature/ci-docs-sync-check` - [ ] 10.4 `git branch -d feature/ci-docs-sync-check` - [ ] 10.5 `git worktree prune` -- [ ] 10.6 (Optional) `git push origin --delete feature/ci-docs-sync-check` \ No newline at end of file +- [ ] 10.6 (Optional) `git push origin --delete feature/ci-docs-sync-check` diff --git a/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/design.md b/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/design.md index b0ef7b0a..2a08c91b 100644 --- a/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/design.md +++ b/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/design.md @@ -4,22 +4,26 @@ docs.specfact.io is a Jekyll static site (GitHub Pages). Source: `docs/`, front- on every page. Homepage is `docs/index.md`. Sidebar navigation in `docs/_layouts/default.html`. Two distinct user cohorts arrive at the docs: + - **Vibe coders**: non-Python-expert, heard "validate your vibe code", want results in seconds, will not read installation guides. Their mental model: run one command, see something useful. - **Experienced developers**: understand pip, virtual envs, module systems. Current docs already work for this group. They are NOT the audience being lost. Testing confirms the real vibe-coder "wow" sequence: + ```bash uvx specfact-cli init --profile solo-developer # ~5 seconds, user-level module install uvx specfact-cli code review run --path . --scope full # ~4 seconds, scored review output ``` + Total time to first result: ~10 seconds, zero pip install, zero virtual environment. This sequence works because `uvx specfact-cli init` installs modules at user level, and subsequent `uvx specfact-cli` invocations detect and use them. **Friction points identified through direct testing:** + 1. Running `uvx specfact-cli code review run --path .` without `--scope full` gives a confusing git-diff error ("Unable to determine changed tracked files"). Vibe coders will stop here. 2. Running `uvx specfact-cli code review run` without init gives "Command 'code' is not installed" @@ -33,6 +37,7 @@ This is a docs change plus a minor CLI UX improvement (error message and `--scop ## Goals / Non-Goals **Goals:** + - Homepage leads with the 2-command vibe-coder sequence and names `code review run` explicitly - uvx is the hero install method; pip is secondary for users who want a persistent installation - "Command 'code' is not installed" error tells the user the exact init command to run @@ -43,6 +48,7 @@ This is a docs change plus a minor CLI UX improvement (error message and `--scop - All current advanced content remains β€” just reordered **Non-Goals:** + - Redesigning the Jekyll theme or sidebar - Adding new CLI commands beyond a minor error-message improvement - Rewriting modules.specfact.io diff --git a/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/proposal.md b/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/proposal.md index 9bec3688..a868561b 100644 --- a/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/proposal.md +++ b/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/proposal.md @@ -19,10 +19,12 @@ immediate "Limitations" warning that actively discourages it, (4) the path cards persona and product dimension rather than by what they want to do right now. The intended vibe-coder entry sequence is: + ```bash uvx specfact-cli init --profile solo-developer # once β€” should install modules uvx specfact-cli code review run --path . --scope full # the "wow" command ``` + This sequence should work in ~10 seconds with no pip install and no virtual environment. However, direct testing reveals it is **completely broken** due to three bugs: @@ -48,6 +50,7 @@ can truthfully describe a vibe-coder entry sequence. ## What Changes **Bug fixes (blocking the "wow" path):** + - **Fix `init --profile` module installation**: `specfact init --profile solo-developer` must actually install the modules defined for that profile, not just bootstrap the runtime - **Fix `module install` under uvx**: module installation must work without pip in the uvx @@ -58,6 +61,7 @@ can truthfully describe a vibe-coder entry sequence. no git diff is available, or emit an error that includes the corrective command **Docs improvements (unlocked once bugs are fixed):** + - **Homepage hero completely rewritten**: opens with the vibe-coder outcome statement, immediately followed by the working 2-command uvx sequence - **`code review run` is explicitly named on the homepage** as the primary entry command @@ -69,6 +73,7 @@ can truthfully describe a vibe-coder entry sequence. ## Capabilities ### New Capabilities + - `dependency-resolution`: Version-aware bundle dependency resolution for `module install` and `module upgrade` β€” versioned specifiers in registry `index.json` and `module-package.yaml`, user prompts on missing/mismatched deps, `--yes` for auto-resolve, `--dry-run` for preview, @@ -80,6 +85,7 @@ can truthfully describe a vibe-coder entry sequence. with the scored output as the explicit "wow" proof point on the homepage ### Modified Capabilities + - `entrypoint-onboarding`: (1) Primary fast-start path must be inline on homepage; (2) path cards name user actions not personas; (3) `code review run` is the named primary command - `first-contact-story`: Hero pairs identity with a plain-language outcome; no architectural @@ -98,6 +104,7 @@ can truthfully describe a vibe-coder entry sequence. ## Impact **CLI changes:** + - `src/specfact_cli/modules/init/` β€” fix `--profile` to actually install profile modules - `src/specfact_cli/modules/module-registry/` or install path β€” fix pip-free install under uvx - Profile definition for `solo-developer` β€” add `specfact-code-review` to bundle list @@ -122,11 +129,13 @@ can truthfully describe a vibe-coder entry sequence. - `core_compatibility` error path β€” replace silent exception with actionable user-facing message **Docs changes:** + - `docs/index.md` β€” primary rewrite (hero + uvx block + 3 cards) - `docs/getting-started/installation.md` β€” promote uvx, restructure options - `docs/getting-started/quickstart.md` β€” reframe for vibe-coder audience **Spec changes:** + - `openspec/specs/entrypoint-onboarding/spec.md` β€” delta - `openspec/specs/first-contact-story/spec.md` β€” delta - `openspec/specs/first-run-selection/spec.md` β€” delta (profile install requirement) diff --git a/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/specs/dependency-resolution/spec.md b/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/specs/dependency-resolution/spec.md index 5a7c62fb..0ddfc130 100644 --- a/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/specs/dependency-resolution/spec.md +++ b/openspec/changes/archive/2026-04-05-docs-new-user-onboarding/specs/dependency-resolution/spec.md @@ -10,11 +10,13 @@ SHALL handle both forms. #### Scenario: Registry entry declares a versioned bundle dependency - **GIVEN** a registry entry with: + ```json "bundle_dependencies": [ {"id": "nold-ai/specfact-project", "version": ">=0.41.0"} ] ``` + - **WHEN** the installer processes this entry - **THEN** the installer SHALL treat `nold-ai/specfact-project` as a required dependency with the constraint `>=0.41.0` @@ -30,6 +32,7 @@ SHALL handle both forms. During `specfact module install`, the system SHALL resolve all `bundle_dependencies` from both the registry index and the module's `module-package.yaml` manifest. For each dependency: + - If the dependency is not installed, the CLI SHALL prompt the user to install it - If the dependency is installed but its version does not satisfy the declared specifier, the CLI SHALL prompt the user to upgrade it @@ -158,9 +161,11 @@ resolution plan without performing any installs or upgrades. - **WHEN** user runs `specfact module install A --dry-run` - **THEN** the CLI SHALL print a dependency plan: + ``` Would install: nold-ai/specfact-project 0.41.2 (required by A >=0.41.0) nold-ai/A 0.42.0 ``` + - **AND** SHALL exit 0 without modifying any installed modules diff --git a/openspec/changes/archive/2026-04-05-module-migration-04-remove-flat-shims/proposal.md b/openspec/changes/archive/2026-04-05-module-migration-04-remove-flat-shims/proposal.md index d201a754..f6545916 100644 --- a/openspec/changes/archive/2026-04-05-module-migration-04-remove-flat-shims/proposal.md +++ b/openspec/changes/archive/2026-04-05-module-migration-04-remove-flat-shims/proposal.md @@ -2,14 +2,12 @@ ## Why - Module-migration-01 introduced category group commands (`code`, `backlog`, `project`, `spec`, `govern`) and backward-compatibility shims so existing flat commands (e.g. `specfact validate`, `specfact analyze`) still worked while emitting a deprecation notice. The proposal stated: "Shims are removed after one major version cycle." The 0.40.x series completes that migration: the top-level CLI surface should show only core commands (`init`, `auth`, `module`, `upgrade`) and the five category groups. Scripts and muscle memory that still invoke flat commands must switch to the category form (e.g. `specfact code validate`). This reduces noise in `specfact --help`, clarifies the canonical command topology, and avoids maintaining two code paths. ## What Changes - - **REMOVE**: Registration of compat shims for all 17 non-core flat commands. No more top-level `analyze`, `drift`, `validate`, `repro`, `backlog`, `policy`, `project`, `plan`, `import`, `sync`, `migrate`, `contract`, `spec`, `sdd`, `generate`, `enforce`, `patch` at root. - **MODIFY**: `_register_category_groups_and_shims()` in `module_packages.py` becomes category-group-only registration (no `FLAT_TO_GROUP` shim loop). Optionally rename to `_register_category_groups()`. - **REMOVE**: `FLAT_TO_GROUP` and `_make_shim_loader()` (and any shim-specific tests that assert deprecation or shim delegation). @@ -17,6 +15,7 @@ The 0.40.x series completes that migration: the top-level CLI surface should sho - **MODIFY**: Docs and CHANGELOG to state the breaking change and migration path (flat β†’ category). ## Capabilities + ### Modified Capabilities - `category-command-groups`: Sole top-level surface for non-core module commands. No flat shims; users must use `specfact code analyze`, `specfact backlog ceremony`, etc. @@ -26,7 +25,6 @@ The 0.40.x series completes that migration: the top-level CLI surface should sho - Backward-compat shim layer (deprecation delegates) for the 17 flat command names. - --- ## Source Tracking @@ -36,4 +34,4 @@ The 0.40.x series completes that migration: the top-level CLI surface should sho - **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: proposed -- **Sanitized**: false \ No newline at end of file +- **Sanitized**: false diff --git a/openspec/changes/archive/2026-04-05-module-migration-10-bundle-command-surface-alignment/proposal.md b/openspec/changes/archive/2026-04-05-module-migration-10-bundle-command-surface-alignment/proposal.md index 78705711..9ece0411 100644 --- a/openspec/changes/archive/2026-04-05-module-migration-10-bundle-command-surface-alignment/proposal.md +++ b/openspec/changes/archive/2026-04-05-module-migration-10-bundle-command-surface-alignment/proposal.md @@ -2,14 +2,12 @@ ## Why - The released `v0.40.x` CLI command surface does not fully match the documented grouped command API. Several command implementations exist inside official bundles such as `specfact-project` and `specfact-spec`, but the installed bundle command trees do not expose the documented paths at runtime. This creates a release-quality gap: README/site/blog examples describe commands that fail with `No such command ...`, while users can only discover the subset currently mounted by the bundle root apps. This must be treated as a product/runtime alignment issue first, not just a docs refresh. Release documentation must match shipped behavior, and intended commands that are already implemented in bundle source should either be exposed correctly or removed from the documented surface explicitly. ## What Changes - - Audit the documented grouped command surface against the installed official bundle command trees for `project`, `spec`, and any other affected bundles. - Expose implemented-but-unreachable commands through the official bundle registration/runtime surface where they are intended to be public. - Mark any non-shipped or intentionally unsupported command forms as removed from the documented release surface instead of leaving them implied by source-only code. @@ -17,11 +15,13 @@ This must be treated as a product/runtime alignment issue first, not just a docs - Align README/docs/release-content references so `v0.40.x` documentation reflects the actual shipped command surface. ## Capabilities + ### New Capabilities - `bundle-command-surface-alignment`: Official bundle command trees match the documented grouped CLI surface for shipped releases. ## Acceptance Criteria + - Installed official bundles expose documented grouped commands such as `specfact code import`, `specfact project plan ...`, and `specfact spec generate ...` when those commands are intended to be public in `v0.40.x`. - If a command path is intentionally not part of the shipped runtime surface, README/docs/release content no longer describe it as available. - Runtime validation fails when a documented grouped command path is missing from the installed official bundle command tree. @@ -29,12 +29,12 @@ This must be treated as a product/runtime alignment issue first, not just a docs - Validation evidence distinguishes between runtime-surface fixes and docs-only removals. ## Dependencies + - `module-migration-02-bundle-extraction` established the official bundle packaging model. - `module-migration-06-core-decoupling-cleanup` and `module-migration-07-test-migration-cleanup` established the current post-migration command ownership baseline. - `cli-val-07-command-package-runtime-validation` provides the runtime audit harness that should be extended to catch this drift. - `backlog-module-ownership-cleanup` remains a separate cleanup scope and should not absorb unrelated `project`/`spec` bundle-surface fixes. - --- ## Source Tracking diff --git a/openspec/changes/archive/2026-04-05-readme-star-conversion-01/proposal.md b/openspec/changes/archive/2026-04-05-readme-star-conversion-01/proposal.md index c62c7e07..03f0c735 100644 --- a/openspec/changes/archive/2026-04-05-readme-star-conversion-01/proposal.md +++ b/openspec/changes/archive/2026-04-05-readme-star-conversion-01/proposal.md @@ -81,7 +81,6 @@ This change restructures the README so the first screen answers four questions i - Restore the previous README structure and delete the evidence bundle references if the new layout proves less clear or creates maintenance burden - --- ## Source Tracking @@ -90,4 +89,4 @@ This change restructures the README so the first screen answers four questions i - **GitHub Issue**: #481 - **Issue URL**: - **Last Synced Status**: proposed -- **Sanitized**: false \ No newline at end of file +- **Sanitized**: false diff --git a/openspec/changes/archive/2026-04-08-agile-01-feature-hierarchy/proposal.md b/openspec/changes/archive/2026-04-08-agile-01-feature-hierarchy/proposal.md index ecc81cb7..ab5e84be 100644 --- a/openspec/changes/archive/2026-04-08-agile-01-feature-hierarchy/proposal.md +++ b/openspec/changes/archive/2026-04-08-agile-01-feature-hierarchy/proposal.md @@ -81,5 +81,5 @@ section, leaving the change order document out of sync with GitHub. ## Source Tracking - source_id: "483" -- source_url: https://github.com/nold-ai/specfact-cli/issues/483 +- source_url: - adapter: github diff --git a/openspec/changes/archive/2026-04-08-agile-01-feature-hierarchy/specs/agile-feature-hierarchy/spec.md b/openspec/changes/archive/2026-04-08-agile-01-feature-hierarchy/specs/agile-feature-hierarchy/spec.md index c3d7ebc4..459c7dd0 100644 --- a/openspec/changes/archive/2026-04-08-agile-01-feature-hierarchy/specs/agile-feature-hierarchy/spec.md +++ b/openspec/changes/archive/2026-04-08-agile-01-feature-hierarchy/specs/agile-feature-hierarchy/spec.md @@ -1,15 +1,18 @@ ## ADDED Requirements ### Requirement: GitHub Agile Feature Hierarchy + The project governance workflow SHALL maintain a three-level GitHub planning hierarchy of Epic -> Feature -> User Story for the public SpecFact CLI backlog. #### Scenario: Feature issues group user stories under the correct epic + - **GIVEN** the public backlog contains Epic issues and change-proposal issues - **WHEN** the hierarchy setup work is completed - **THEN** each planned Feature issue is linked to its parent Epic - **AND** each grouped User Story issue is assigned to the correct Feature #### Scenario: CHANGE_ORDER stays aligned with the GitHub hierarchy + - **GIVEN** new Epic or Feature-level hierarchy items are introduced in GitHub - **WHEN** the change is updated - **THEN** `openspec/CHANGE_ORDER.md` reflects the current Epic and Feature sequencing metadata diff --git a/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/proposal.md b/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/proposal.md index e748a524..6f077dca 100644 --- a/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/proposal.md +++ b/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/proposal.md @@ -20,9 +20,11 @@ Active OpenSpec changes and linked GitHub issues still mix the old monolithic `s ## Capabilities ### New Capabilities + - `cross-repo-backlog-alignment`: Governance rules for assigning active changes and GitHub issues to the correct repository, reconciling issue migration or recreation, and maintaining aligned Epic -> Feature -> User Story planning hierarchies across `specfact-cli` and `specfact-cli-modules`. ### Modified Capabilities + - `backlog-module-ownership`: Active backlog and ceremony changes must align their repository ownership, issue tracking, and proposal scope with the canonical `specfact-backlog` bundle boundary. - `project-codebase-ownership`: Active changes that describe outdated module/package ownership must be reconciled to the canonical post-migration core-versus-bundle split before implementation proceeds. diff --git a/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/backlog-module-ownership/spec.md b/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/backlog-module-ownership/spec.md index adbbcf18..0ea9aed0 100644 --- a/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/backlog-module-ownership/spec.md +++ b/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/backlog-module-ownership/spec.md @@ -5,16 +5,19 @@ The system SHALL treat `nold-ai/specfact-backlog` as the sole owner of user-facing backlog and policy command surfaces, including the active proposal backlog and GitHub planning artifacts that track future backlog and ceremony feature work. #### Scenario: Core does not directly own backlog feature commands + - **WHEN** command registration is resolved in `specfact-cli` - **THEN** user-facing backlog feature commands are provided by the installed backlog module - **AND** core does not ship a parallel built-in backlog command surface for the same feature commands. #### Scenario: Core keeps only shared backlog framework contracts + - **WHEN** backlog ownership is resolved after migration - **THEN** core retains only shared provider integrations, generic data models, and minimal backlog contracts reused outside the backlog bundle - **AND** backlog-only command implementations, prompt resources, templates, and refinement helpers are not owned by core. #### Scenario: Active backlog proposals are not tracked as core-owned implementation work + - **WHEN** a pending OpenSpec change or linked GitHub issue describes backlog, scrum, kanban, safe, ceremony, or policy command behavior that belongs to `specfact-backlog` - **THEN** that work is assigned to the modules repo planning hierarchy rather than remaining a core-repo implementation story - **AND** the core repo retains only the shared contracts or bridge points, if any, that support the owning bundle. diff --git a/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/cross-repo-backlog-alignment/spec.md b/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/cross-repo-backlog-alignment/spec.md index 27e8de79..ce3186ae 100644 --- a/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/cross-repo-backlog-alignment/spec.md +++ b/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/cross-repo-backlog-alignment/spec.md @@ -1,47 +1,58 @@ ## ADDED Requirements ### Requirement: Active Change Ownership Must Be Classified Against The Canonical Repo Split + The system SHALL classify every active OpenSpec change and linked GitHub user story in `specfact-cli` against the canonical post-migration ownership model before further implementation work proceeds. #### Scenario: Active change is classified as core, modules, or split + - **WHEN** maintainers review active changes that still reference the pre-split monolithic structure - **THEN** each change is assigned one of: `core`, `modules`, or `split/rescope` - **AND** the decision is based on the canonical ownership specs rather than on stale proposal paths or legacy issue location. ### Requirement: Module-Owned Issues Must Use A Defined Reassignment Path + The system SHALL define a deterministic path for any GitHub issue that belongs in `specfact-cli-modules` instead of `specfact-cli`. #### Scenario: Native issue transfer is available and accepted + - **WHEN** a module-owned issue can be moved between the two repositories with acceptable metadata preservation - **THEN** the issue is transferred to `nold-ai/specfact-cli-modules` - **AND** the related OpenSpec artifacts and planning inventory are updated to reference the transferred issue in its new repository. #### Scenario: Native issue transfer is unavailable or unsuitable + - **WHEN** a module-owned issue cannot be moved cleanly between repositories - **THEN** the source issue in `specfact-cli` is closed with a comment pointing to the replacement issue in `specfact-cli-modules` - **AND** a replacement issue is created in `specfact-cli-modules` with updated scope aligned to the current architecture - **AND** the old and new issues cross-reference each other so planning history remains auditable. ### Requirement: Target Hierarchy Must Exist Before Module-Owned Stories Are Reassigned + The system SHALL establish the necessary Epic and Feature parents in `specfact-cli-modules` before module-owned user stories are transferred or recreated there. #### Scenario: Module-owned user story is prepared for reassignment + - **WHEN** a user story is classified as module-owned - **THEN** the target repository already has the Epic and Feature hierarchy needed to parent that story - **AND** the reassigned story is linked under the target Feature rather than being left as a flat issue. ### Requirement: Planning Inventories Must Be Updated In Both Repositories + The system SHALL keep planning metadata aligned across repositories when change ownership or GitHub issue ownership changes. #### Scenario: Change ownership is reassigned to modules repo + - **WHEN** a change or linked issue moves from `specfact-cli` planning to `specfact-cli-modules` - **THEN** `openspec/CHANGE_ORDER.md` in `specfact-cli` is updated to remove or annotate the old core-repo ownership - **AND** the corresponding planning inventory in `specfact-cli-modules` is updated with the new issue, Epic, Feature, and dependency references. ### Requirement: Rescoped Proposals Must State The Current Architecture And Repo Assignment + The system SHALL update affected active proposals before implementation resumes so they no longer describe obsolete monolithic paths or incorrect repository ownership. #### Scenario: Active proposal still references in-repo module implementation + - **WHEN** an active proposal describes implementation under `modules//` in `specfact-cli` for a module-owned capability - **THEN** the proposal is updated to either reflect its true core-only scope or to point to the owning implementation work in `specfact-cli-modules` - **AND** the proposal no longer implies that bundle-owned behavior will be implemented in the core repo. diff --git a/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/project-codebase-ownership/spec.md b/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/project-codebase-ownership/spec.md index f621f666..48f45a5f 100644 --- a/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/project-codebase-ownership/spec.md +++ b/openspec/changes/archive/2026-04-08-cross-repo-issue-realignment/specs/project-codebase-ownership/spec.md @@ -5,12 +5,14 @@ Pending OpenSpec changes that touch command surface, docs, prompts, migration cleanup, or issue planning SHALL align with the canonical post-migration ownership model instead of reintroducing monolithic `specfact-cli` module ownership by implication. #### Scenario: Active change does not finalize conflicting import ownership + - **GIVEN** an active pending change updates grouped command paths or release-facing docs - **WHEN** that change references brownfield import ownership - **THEN** it references the canonical owner defined by this change - **AND** it does not re-establish a conflicting public command path or subsystem owner by implication. #### Scenario: Active proposal does not preserve obsolete in-repo module paths + - **GIVEN** an active proposal still describes implementation under `modules//` in `specfact-cli` for a capability now owned by a bundle in `specfact-cli-modules` - **WHEN** maintainers reconcile the proposal against the current architecture - **THEN** the proposal is updated to the correct repository and bundle ownership model diff --git a/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/.openspec.yaml b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/.openspec.yaml new file mode 100644 index 00000000..98d7681c --- /dev/null +++ b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-04-09 diff --git a/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/CHANGE_VALIDATION.md b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/CHANGE_VALIDATION.md new file mode 100644 index 00000000..6c5c097f --- /dev/null +++ b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/CHANGE_VALIDATION.md @@ -0,0 +1,12 @@ +# CHANGE VALIDATION + +- Change: `governance-02-github-hierarchy-cache` +- Date: 2026-04-09 +- Command: `openspec validate governance-02-github-hierarchy-cache --strict` +- Result: PASS + +## Notes + +- The new capability `github-hierarchy-cache` validates as a net-new spec delta. +- The modified capability `agile-feature-hierarchy` remains aligned with the existing spec folder name. +- The change is apply-ready from an OpenSpec artifact perspective. diff --git a/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/TDD_EVIDENCE.md b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/TDD_EVIDENCE.md new file mode 100644 index 00000000..9d86eaef --- /dev/null +++ b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/TDD_EVIDENCE.md @@ -0,0 +1,36 @@ +# TDD Evidence + +## Failing-before implementation + +- Timestamp: `2026-04-09T21:03:37+02:00` +- Command: `python3 -m pytest tests/unit/scripts/test_sync_github_hierarchy_cache.py -q` +- Result: FAIL +- Summary: All three tests failed with `FileNotFoundError` because `scripts/sync_github_hierarchy_cache.py` did not exist yet. + +## Failing-before path relocation refinement + +- Timestamp: `2026-04-09T21:17:04+02:00` +- Command: `python3 -m pytest tests/unit/scripts/test_sync_github_hierarchy_cache.py -q` +- Result: FAIL +- Summary: The new default-path test failed because the script still targeted `openspec/GITHUB_HIERARCHY_CACHE.md` instead of ignored `.specfact/backlog/` storage. + +## Passing-after implementation + +- Timestamp: `2026-04-09T21:17:35+02:00` +- Command: `python3 -m pytest tests/unit/scripts/test_sync_github_hierarchy_cache.py -q` +- Result: PASS +- Summary: All five script tests passed after moving the cache into ignored `.specfact/backlog/` storage and keeping the no-op fingerprint path intact. + +## Additional verification + +- `python3 -m py_compile scripts/sync_github_hierarchy_cache.py` β†’ PASS +- `python3 scripts/sync_github_hierarchy_cache.py --force` β†’ generated `.specfact/backlog/github_hierarchy_cache.md` +- Second `python3 scripts/sync_github_hierarchy_cache.py` run β†’ `GitHub hierarchy cache unchanged (46 issues).` + +## Final scoped quality gates + +- Timestamp: `2026-04-09T22:04:18+02:00` +- Ruff: `hatch run ruff check scripts/sync_github_hierarchy_cache.py tests/unit/scripts/test_sync_github_hierarchy_cache.py` β†’ PASS +- basedpyright: `hatch run python -m basedpyright scripts/sync_github_hierarchy_cache.py tests/unit/scripts/test_sync_github_hierarchy_cache.py` β†’ PASS (`0 errors, 0 warnings`) +- pytest: `hatch test -- tests/unit/scripts/test_sync_github_hierarchy_cache.py -q` β†’ PASS (`15 passed`, after parity with modules-side script/tests) +- SpecFact code review: `hatch run specfact code review run --json --out .specfact/code-review.json scripts/sync_github_hierarchy_cache.py tests/unit/scripts/test_sync_github_hierarchy_cache.py` β†’ PASS (`overall_verdict` PASS, `ci_exit_code` 0; low-severity DRY hints on icontract preconditions documented in `proposal.md`) diff --git a/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/design.md b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/design.md new file mode 100644 index 00000000..b490b9e5 --- /dev/null +++ b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/design.md @@ -0,0 +1,84 @@ +# Context + +`specfact-cli` already maintains GitHub planning hierarchy through issue labels, parent-child links, and `openspec/CHANGE_ORDER.md`, but contributors still discover that structure by hitting the GitHub API manually. The new requirement is to make hierarchy lookup deterministic, cheap, and local: a generated markdown file under ignored `.specfact/backlog/` becomes the first source for parent Feature and Epic resolution, and the sync command is rerun only when the hierarchy changed. + +This is a cross-cutting governance change because it affects GitHub automation, OpenSpec operating rules, and agent instructions. The same pattern is needed in `specfact-cli-modules`, but each repo should own its own script, state file, and cache output so the result remains self-contained. + +## Goals / Non-Goals + +**Goals:** + +- Generate a deterministic markdown cache of Epic and Feature issues for this repository. +- Include enough metadata for issue-parenting work without another GitHub lookup: issue number, title, short summary, labels, parent/child relationships, and issue URLs. +- Make the sync fast on no-op runs by using a small fingerprint/state check before regenerating markdown. +- Update repo guidance so contributors use the cache first and only rerun sync when needed. + +**Non-Goals:** + +- Replacing GitHub as the authoritative source of issue hierarchy. +- Caching every issue type or full issue bodies. +- Synchronizing User Story issues into the cache in this first version. +- Introducing a new external dependency beyond the existing `gh` CLI. + +## Decisions + +### Use `gh api graphql` as the sole upstream source + +The script will query GitHub through `gh api graphql` so it can access issue type, labels, relationships, and brief body content in one supported path. This avoids scraping markdown or depending on REST endpoints that do not expose hierarchy fields consistently. + +Alternative considered: + +- `gh issue list/view` JSON loops: simpler, but requires many calls and awkward relationship reconstruction. + +### Split the sync into a lightweight fingerprint pass and a full render pass + +The script will first fetch only the Epic and Feature issue identity set plus timestamps/relationship fingerprints, hash that data, and compare it with a local state file. If the fingerprint matches, the script exits successfully without rewriting markdown. If it differs, the script performs a fuller metadata query and regenerates the cache. + +Alternative considered: + +- Always regenerate markdown: deterministic but wastes GitHub calls and makes local workflows slower. + +### Store human-readable cache plus machine-readable state under ignored `.specfact/backlog` + +The canonical human-facing output will be `.specfact/backlog/github_hierarchy_cache.md`. A companion state file, `.specfact/backlog/github_hierarchy_cache_state.json`, will hold the last fingerprint and generator metadata. Both files stay local and ignored by Git so the cache can be recreated freely without creating repository drift. + +Alternative considered: + +- State embedded in markdown comments: workable, but couples machine state to user-facing output and complicates deterministic rendering. + +### Render by deterministic section and sort order + +The markdown will use fixed sections for Epics and Features, with issues sorted stably by type, then issue number. Relationship lists and labels will also be sorted deterministically so reruns only change the file when source metadata actually changes. + +Alternative considered: + +- Preserve GitHub API order: easier, but can drift between runs and create noisy diffs. + +### Keep instruction updates in repo-local governance files + +The change will update `openspec/config.yaml` and `AGENTS.md` in this repo so the workflow explicitly says: consult the cache first, regenerate it when fresh planning metadata is needed, and avoid ad hoc GitHub lookups unless the cache is stale or missing. + +Alternative considered: + +- Document the behavior only in the script help text: insufficient because agents and OpenSpec flows read governance files first. + +## Risks / Trade-offs + +- [GitHub schema drift] β†’ Keep GraphQL fields minimal and cover parsing/rendering with tests that pin expected shapes. +- [Cache becomes stale if users forget to rerun sync] β†’ Update `AGENTS.md` and `openspec/config.yaml` to make rerun conditions explicit and keep the script fast enough to run routinely. +- [Relationship data differs between repos or issue states] β†’ Normalize missing parents/children to explicit empty values and show unresolved relationships clearly in markdown. +- [No-op fingerprint misses relevant content changes] β†’ Include type, number, title, updated timestamp, labels, and parent identity in the fingerprint rather than only issue count. + +## Migration Plan + +1. Add the sync script, state handling, markdown renderer, and tests. +2. Generate the initial cache file under ignored `.specfact/backlog/`. +3. Update `openspec/config.yaml` and `AGENTS.md` to use the cache-first workflow. +4. Run validation and repository tests, then sync the paired change issue metadata. + +Rollback is straightforward: remove the script, state file, cache file, and governance references if the workflow proves noisy or unreliable. + +## Open Questions + +- Whether a later follow-up should also cache User Story issues once the Feature/Epic workflow is stable. +- Whether the fingerprint pass should use a dedicated smaller GraphQL query or reuse one richer query and short-circuit before rendering if unchanged. diff --git a/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/proposal.md b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/proposal.md new file mode 100644 index 00000000..6738003a --- /dev/null +++ b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/proposal.md @@ -0,0 +1,38 @@ +# Governance: GitHub hierarchy cache (specfact-cli) + +## Why + +OpenSpec and agent workflows still have to query GitHub ad hoc to rediscover Epics, Features, and their parent links before creating or syncing change issues. That is slow, expensive, and error-prone, especially now that planning hierarchy matters in both `specfact-cli` and `specfact-cli-modules`. + +## What Changes + +- Add a deterministic repo-local hierarchy cache generator for GitHub Epic and Feature issues. +- Persist a repo-local markdown hierarchy cache at `.specfact/backlog/github_hierarchy_cache.md` (ignored; not committed) with issue number, title, brief summary, labels, and hierarchy relationships, plus a companion fingerprint/state file `.specfact/backlog/github_hierarchy_cache_state.json` so the sync can exit quickly when Epic and Feature metadata has not changed. +- Update governance instructions in `openspec/config.yaml` and `AGENTS.md` to consult the cached hierarchy first and rerun `python scripts/sync_github_hierarchy_cache.py` when fresh data is needed. +- Cover the script with tests so cache output and no-change behavior remain stable. + +## Capabilities + +### New Capabilities + +- `github-hierarchy-cache`: Deterministic synchronization of GitHub Epic and Feature hierarchy metadata into a repo-local OpenSpec markdown cache for low-cost parent and planning lookups. + +### Modified Capabilities + +- `agile-feature-hierarchy`: Local governance workflows must be able to resolve current Epic and Feature planning metadata from the repo-local cache before performing manual GitHub lookups. + +## Impact + +- Affected code: new script and tests under `scripts/` and `tests/`, plus OpenSpec governance guidance in `openspec/config.yaml` and `AGENTS.md`. +- Affected workflow: OpenSpec change creation, GitHub issue creation/sync, and `CHANGE_ORDER.md` maintenance become cache-first instead of lookup-first. +- Cross-repo impact: a sibling change in `specfact-cli-modules` must implement the same pattern so both repos expose equivalent planning metadata locally. + +## Source Tracking + +- GitHub Issue: [#491](https://github.com/nold-ai/specfact-cli/issues/491) +- Parent Feature: [#486](https://github.com/nold-ai/specfact-cli/issues/486) +- Related Modules Change: `specfact-cli-modules/governance-03-github-hierarchy-cache` + +## Code review note (SpecFact dogfood) + +Icontract `@require` preconditions on `fetch_hierarchy_issues`, `render_cache_markdown`, and `sync_cache` intentionally use small, similarly shaped predicates. The code-review module may emit low-severity DRY / duplicate-shape hints for those helpers; that is accepted here because collapsing them would break icontract’s per-parameter argument binding (e.g. `**kwargs` predicates are not supported the same way). diff --git a/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/specs/agile-feature-hierarchy/spec.md b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/specs/agile-feature-hierarchy/spec.md new file mode 100644 index 00000000..c56d3636 --- /dev/null +++ b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/specs/agile-feature-hierarchy/spec.md @@ -0,0 +1,28 @@ +## MODIFIED Requirements + +### Requirement: GitHub Agile Feature Hierarchy + +The project governance workflow SHALL maintain a three-level GitHub planning hierarchy of Epic -> Feature -> User Story +for the public SpecFact CLI backlog, and SHALL expose the current Epic and Feature metadata through a repo-local +hierarchy cache before manual GitHub lookups are used. + +#### Scenario: Feature issues group user stories under the correct epic + +- **GIVEN** the public backlog contains Epic issues and change-proposal issues +- **WHEN** the hierarchy setup work is completed +- **THEN** each planned Feature issue is linked to its parent Epic +- **AND** each grouped User Story issue is assigned to the correct Feature + +#### Scenario: CHANGE_ORDER stays aligned with the GitHub hierarchy + +- **GIVEN** new Epic or Feature-level hierarchy items are introduced in GitHub +- **WHEN** the change is updated +- **THEN** `openspec/CHANGE_ORDER.md` reflects the current Epic and Feature sequencing metadata +- **AND** stale issue state such as archived-but-open items is reconciled during validation + +#### Scenario: Local cache is consulted before manual hierarchy lookup + +- **GIVEN** a contributor needs a parent Feature or Epic while creating or syncing a change issue +- **WHEN** the local hierarchy cache is present and current +- **THEN** the contributor can resolve the parent relationship from the cache without an additional GitHub lookup +- **AND** the sync script is rerun only when the cache is stale or missing diff --git a/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/specs/github-hierarchy-cache/spec.md b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/specs/github-hierarchy-cache/spec.md new file mode 100644 index 00000000..e057c4a2 --- /dev/null +++ b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/specs/github-hierarchy-cache/spec.md @@ -0,0 +1,35 @@ +## ADDED Requirements + +### Requirement: Repository hierarchy cache sync + +The repository SHALL provide a deterministic sync mechanism that retrieves GitHub Epic and Feature issues for the current repository and writes a local hierarchy cache under ignored `.specfact/backlog/`. + +#### Scenario: Generate hierarchy cache from GitHub metadata + +- **WHEN** the user runs the hierarchy cache sync script for the repository +- **THEN** the script retrieves GitHub issues whose Type is `Epic` or `Feature` +- **AND** writes a markdown cache under ignored `.specfact/backlog/` with each issue's number, title, URL, short summary, labels, and hierarchy relationships +- **AND** the output ordering is deterministic across repeated runs with unchanged source data + +#### Scenario: Represent hierarchy relationships in cache output + +- **WHEN** a synced Epic or Feature has parent or child hierarchy links +- **THEN** the markdown cache includes those relationships in normalized form +- **AND** missing relationships are rendered as explicit empty or none values rather than omitted ambiguously + +#### Scenario: Fast exit on unchanged hierarchy state + +- **WHEN** the script detects that the current Epic and Feature hierarchy fingerprint matches the last synced fingerprint +- **THEN** it exits successfully without regenerating the markdown cache +- **AND** it reports that no hierarchy update was required + +### Requirement: Repository governance must use cache-first hierarchy lookup + +Repository governance instructions SHALL direct contributors and agents to consult the local hierarchy cache before performing manual GitHub lookups for Epic or Feature parenting. + +#### Scenario: Cache-first governance guidance + +- **WHEN** a contributor reads `AGENTS.md` or `openspec/config.yaml` for GitHub issue setup guidance +- **THEN** the instructions tell them to consult the local hierarchy cache first +- **AND** the instructions define when the sync script must be rerun to refresh stale hierarchy metadata +- **AND** the instructions state that the cache is local ephemeral state and must not be committed diff --git a/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/tasks.md b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/tasks.md new file mode 100644 index 00000000..fd7dd823 --- /dev/null +++ b/openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache/tasks.md @@ -0,0 +1,27 @@ +## 1. Change setup and governance sync + +- [x] 1.0 Create a dedicated git worktree from `origin/dev`, run `hatch env create` in the worktree, and run pre-flight + `hatch run smart-test-status` and `hatch run contract-test-status` before implementation work. +- [x] 1.1 Create and sync the GitHub issue for `governance-02-github-hierarchy-cache`, link it to the correct parent Feature, and update `openspec/CHANGE_ORDER.md` plus proposal source tracking. +- [x] 1.2 Validate the change artifacts and capture the validation report in `openspec/changes/governance-02-github-hierarchy-cache/CHANGE_VALIDATION.md`. + +## 2. Spec-first test setup + +- [x] 2.1 Add or update tests for hierarchy fingerprinting, deterministic markdown rendering, and fast no-change exit behavior. +- [x] 2.2 Run the targeted test command, confirm it fails before implementation, and record the failing run in `openspec/changes/governance-02-github-hierarchy-cache/TDD_EVIDENCE.md`. + +## 3. Implementation + +- [x] 3.1 Implement the repository-local GitHub hierarchy cache sync script and state file handling under `scripts/`. +- [x] 3.2 Generate the initial `.specfact/backlog/github_hierarchy_cache.md` output and ensure reruns remain deterministic without committing it. +- [x] 3.3 Update `openspec/config.yaml` and `AGENTS.md` so GitHub issue setup and parent lookup use the cache-first workflow. + +## 4. Verification + +- [x] 4.1 Re-run the targeted tests and record the passing run in `openspec/changes/governance-02-github-hierarchy-cache/TDD_EVIDENCE.md`. +- [x] 4.2 Run the required repo quality gates for the touched scope, including code review JSON refresh if stale. + +## 5. Post-merge cleanup + +- [ ] 5.1 Remove the feature worktree: `git worktree remove` (path used for this change) and `git worktree prune`. +- [ ] 5.2 Delete the local branch after merge: `git branch -d` (branch name used for this change). diff --git a/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/.openspec.yaml b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/.openspec.yaml new file mode 100644 index 00000000..e49efd11 --- /dev/null +++ b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-04-10 diff --git a/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/TDD_EVIDENCE.md b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/TDD_EVIDENCE.md new file mode 100644 index 00000000..838ea65a --- /dev/null +++ b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/TDD_EVIDENCE.md @@ -0,0 +1,39 @@ +# TDD Evidence + +## Failing-before evidence + +- Timestamp: `2026-04-10T22:22:22+02:00` +- Command: + +```bash +python3 -m pytest tests/unit/scripts/test_doc_frontmatter/test_agent_rule_frontmatter.py tests/unit/docs/test_agent_rules_governance.py -q +``` + +- Result: failed as expected before implementation. +- Failure summary: + - `scripts/check_doc_frontmatter.py` did not expose `AgentRuleFrontmatter`. + - `docs/agent-rules/INDEX.md` and `docs/agent-rules/05-non-negotiable-checklist.md` did not exist yet. + - `AGENTS.md` did not yet reference the canonical rule docs. + - The doc frontmatter validator still accepted `docs/agent-rules/INDEX.md` without required governance fields such as `applies_when`. + +## Passing-after evidence + +- Timestamp: `2026-04-10T22:39:02+02:00` +- Commands: + +```bash +python3 -m pytest tests/unit/scripts/test_doc_frontmatter/test_agent_rule_frontmatter.py tests/unit/docs/test_agent_rules_governance.py -q +python3 -m pytest tests/unit/scripts/test_doc_frontmatter/test_schema.py tests/unit/scripts/test_doc_frontmatter/test_validation.py tests/integration/scripts/test_doc_frontmatter/test_integration.py tests/unit/docs/test_docs_validation_scripts.py -q +python3 -m pytest tests/unit/specfact_cli/test_clean_code_principle_gates.py -q +hatch run format +hatch run yaml-lint +hatch run lint +hatch run contract-test +hatch run smart-test +openspec validate governance-03-deterministic-agent-governance-loading --strict +``` + +- Result: passing after implementation. +- Notes: + - `hatch run type-check` still reports the repository's existing warning baseline, so changed-file validation was verified separately with `basedpyright scripts/check_doc_frontmatter.py tests/helpers/doc_frontmatter_types.py tests/unit/scripts/test_doc_frontmatter/test_agent_rule_frontmatter.py tests/unit/docs/test_agent_rules_governance.py`, which passed with `0 errors, 0 warnings, 0 notes`. + - `hatch run specfact code review run --json --out .specfact/code-review.json` is currently blocked in this worktree because the `nold-ai/specfact-codebase` module that provides the `specfact code review` command is not installed. The command fails immediately with a missing-module message rather than change-related findings. diff --git a/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/design.md b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/design.md new file mode 100644 index 00000000..5622e21f --- /dev/null +++ b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/design.md @@ -0,0 +1,130 @@ +## Context + +The repository currently relies on a large `AGENTS.md` file to carry bootstrap rules, workflow gates, and long-form operational detail. That creates two problems: + +1. New sessions spend unnecessary context budget loading governance that is not always relevant. +2. Different AI models may compress or forget parts of the long document, causing inconsistent behavior around worktrees, change validation, cache refresh, TDD order, and finalization gates. + +The proposed rule system keeps `AGENTS.md` as the mandatory bootstrap surface, but reduces it to a small governance contract that points to a canonical `docs/agent-rules/INDEX.md` plus a mandatory non-negotiable checklist. Detailed rules move into focused Markdown artifacts with frontmatter so rule loading can be deterministic and selective. + +The deterministic bootstrap also needs to carry forward the GitHub issue-handling improvements already being introduced in repository governance: agents must not rely on stale plan text alone when linked change issues have live GitHub metadata that can invalidate readiness. Parent resolution, labels, project assignment, blockers, blocked-by links, and issue status must be handled as explicit governance checks rather than soft reminders. + +## Goals / Non-Goals + +**Goals:** + +- Define a deterministic bootstrap path that every session follows before implementation work. +- Keep `AGENTS.md` small while preserving it as the mandatory governance layer. +- Introduce a frontmatter schema that makes rule applicability, priority, stop conditions, and user-interaction requirements machine-readable. +- Make rule loading selective so only relevant governance detail is loaded for a given task. +- Preserve existing hard gates such as worktree requirements, OpenSpec validation, cache-first GitHub lookup, TDD evidence, quality gates, and module signature enforcement. + +**Non-Goals:** + +- Replacing OpenSpec lifecycle rules with a new workflow engine. +- Creating a generic policy engine for arbitrary repositories. +- Automatically resolving stale, ambiguous, or concurrent work situations without user input. +- Removing platform-specific alias files such as `CLAUDE.md` or `.github/copilot-instructions.md`. + +## Decisions + +### Decision: Keep `AGENTS.md` as a compact bootstrap contract + +`AGENTS.md` remains the first required instruction surface, but its role changes from comprehensive handbook to compact governance contract. It will: + +- define the mandatory startup sequence +- point to the canonical rule index +- point to the always-load non-negotiable checklist +- define precedence for explicit user override versus repository governance + +This preserves compatibility with tools and models that already look for `AGENTS.md` while preventing governance sprawl inside that file. + +Alternative considered: + +- Move everything to `docs/agent-rules/` and leave only a pointer in `AGENTS.md`. +- Rejected because many agents and IDE integrations are biased toward reading `AGENTS.md` first and may miss a too-thin pointer file. + +### Decision: Introduce a canonical rule index with deterministic loading semantics + +`docs/agent-rules/INDEX.md` becomes the dispatcher for governance rules. The index defines: + +- the mandatory always-load rule set +- applicability signals for domain-specific files +- load order and precedence +- stop/continue semantics + +This keeps task routing deterministic instead of relying on the model to infer which long documents to read. + +Alternative considered: + +- Let agents discover rule files heuristically by file names. +- Rejected because that is not deterministic across models and creates drift over time. + +### Decision: Use YAML frontmatter for every rule artifact + +Each `docs/agent-rules/*.md` file will include frontmatter fields such as: + +- `id` +- `title` +- `always_load` +- `applies_when` +- `priority` +- `blocking` +- `user_interaction_required` +- `stop_conditions` +- `depends_on` + +This makes rule selection and enforcement durable across compacted sessions and across different AI models. + +Alternative considered: + +- Encode the metadata inside Markdown prose only. +- Rejected because prose-only rule routing is easier to forget and harder to validate. + +### Decision: Make non-negotiable gates a standalone always-load artifact + +`docs/agent-rules/05-non-negotiable-checklist.md` will hold the invariant SHALL rules. It is always loaded after the index and before any domain-specific rules. This creates a small, stable enforcement nucleus that survives context compaction better than a large handbook. + +### Decision: Extend cache-first governance to session bootstrap + +The existing `github-hierarchy-cache` capability already requires cache-first guidance. This change extends it so the compact bootstrap path explicitly checks whether the local cache is missing or stale and refreshes it before governance work that depends on hierarchy metadata. + +### Decision: Make GitHub metadata completeness and issue-state ambiguity explicit readiness gates + +The compact governance system will treat GitHub issue metadata as part of deterministic implementation readiness for public work. Applicable rule files and the always-load checklist must explicitly require: + +- parent resolution from cache-backed or refreshed GitHub reality +- labels and project assignment completeness +- blockers and blocked-by completeness +- a live issue-state check for `in progress` ambiguity + +If an issue is already marked `in progress`, the governance system must force a clarification stop before implementation continues. This keeps concurrent work from being silently duplicated across sessions. + +Alternative considered: + +- Preserve these checks only as optional planning guidance in long-form prose. +- Rejected because those checks are exactly the kind of detail that gets dropped when contexts are compacted or when different models summarize instructions differently. + +## Risks / Trade-offs + +- [Rule sprawl moves from one file to many files] β†’ Mitigate with a strict index, a bounded always-load subset, and mandatory frontmatter validation. +- [Agents may read `AGENTS.md` but skip the index] β†’ Mitigate by making the bootstrap contract explicit and repetitive in the small `AGENTS.md`. +- [Applicability rules become ambiguous] β†’ Mitigate by defining exact task signals and precedence in the index. +- [Cross-model behavior still diverges if metadata is incomplete] β†’ Mitigate by requiring frontmatter fields and validation tests for every rule file. +- [Agents proceed from stale local change context while GitHub reality changed] β†’ Mitigate by making cache refresh, metadata completeness, and `in progress` state checks explicit blocking gates. +- [Documentation drift between `AGENTS.md`, rule files, and alias instructions] β†’ Mitigate by making alias surfaces reference canonical rule artifacts instead of embedding duplicated policy text. + +## Migration Plan + +1. Define the governance bootstrap contract and frontmatter schema in specs. +2. Replace the large `AGENTS.md` body with a compact bootstrap/governance layer. +3. Create `docs/agent-rules/INDEX.md`, `05-non-negotiable-checklist.md`, and the first domain rule files. +4. Update related alias or workflow instruction surfaces to reference the canonical rules rather than duplicating long guidance. +5. Add validation coverage for frontmatter schema, required always-load files, deterministic loading/precedence behavior, and GitHub readiness gates. +6. Verify the GitHub hierarchy cache guidance and issue-metadata readiness checks still work under the new bootstrap flow. + +## Open Questions + +- Whether rule-file validation should live in an existing docs/frontmatter validator or in a dedicated governance validator. +- Whether some high-frequency domain rules should also be always-load, or only index-selected. +- Whether platform-specific instruction generators should emit direct rule-file references or a stable alias that resolves through the same index. diff --git a/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/proposal.md b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/proposal.md new file mode 100644 index 00000000..f0ebba76 --- /dev/null +++ b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/proposal.md @@ -0,0 +1,35 @@ +## Why + +`AGENTS.md` has become a heavy mixed-purpose instruction surface that front-loads too much repository policy into every new session. That increases token cost, makes cross-model behavior less deterministic, and raises the risk that important workflow gates are forgotten once context is compacted. + +## What Changes + +- Introduce a deterministic agent-governance rule system with a small bootstrap `AGENTS.md`, a canonical rule index, and focused rule artifacts loaded by applicability. +- Define a machine-readable frontmatter contract for governance rule files so multiple AI models can follow the same loading and stop-condition semantics. +- Require an always-load non-negotiable checklist plus explicit precedence and stop/continue behavior for worktree, change validation, TDD, verification, and finalization gates. +- Make GitHub governance completeness explicit in the deterministic readiness flow, including parent resolution, labels, project assignment, blockers / blocked-by relationships, and live issue-state ambiguity checks. +- Tighten cache-first bootstrap guidance so session startup refreshes the local GitHub hierarchy cache when it is missing or stale. +- Move long-form governance detail out of `AGENTS.md` into dedicated markdown artifacts while preserving `AGENTS.md` as the mandatory small governance layer. + +## Capabilities + +### New Capabilities + +- `agent-governance-loading`: Deterministic bootstrap, rule discovery, rule frontmatter, precedence, and stop-condition behavior for AI instruction surfaces. + +### Modified Capabilities + +- `github-hierarchy-cache`: Require cache freshness checks as part of the compact governance bootstrap flow. + +## Impact + +- Affected governance docs and instruction surfaces: `AGENTS.md`, new `docs/agent-rules/` artifacts, and possibly other lightweight instruction aliases that must reference the canonical rule system. +- OpenSpec/runtime guidance: `openspec/config.yaml`, `openspec/CHANGE_ORDER.md`, and related workflow guidance for agents. +- GitHub workflow guidance: cache-backed parent lookup, metadata completeness checks, and concurrency-ambiguity handling for linked change issues. +- Validation scope: documentation consistency, frontmatter schema enforcement, and deterministic session-bootstrap behavior across AI models. + +## Tracking + +- GitHub Issue: [#494](https://github.com/nold-ai/specfact-cli/issues/494) +- Parent Feature: [#486](https://github.com/nold-ai/specfact-cli/issues/486) +- Blocked by: [#491](https://github.com/nold-ai/specfact-cli/issues/491) diff --git a/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/specs/agent-governance-loading/spec.md b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/specs/agent-governance-loading/spec.md new file mode 100644 index 00000000..f8711fe7 --- /dev/null +++ b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/specs/agent-governance-loading/spec.md @@ -0,0 +1,113 @@ +## ADDED Requirements + +### Requirement: Compact AGENTS bootstrap contract + +The repository SHALL keep `AGENTS.md` as the mandatory bootstrap governance surface, but it SHALL remain compact and SHALL delegate long-form operational detail to canonical rule artifacts. + +#### Scenario: Session bootstrap reads compact governance contract + +- **WHEN** an agent starts work in the repository +- **THEN** it reads `AGENTS.md` first +- **AND** `AGENTS.md` defines a mandatory bootstrap sequence rather than embedding the full long-form governance corpus +- **AND** the bootstrap sequence requires loading `docs/agent-rules/INDEX.md` +- **AND** the bootstrap sequence requires loading the canonical non-negotiable checklist before code implementation work + +#### Scenario: AGENTS stays compact while preserving enforcement + +- **WHEN** repository governance is updated +- **THEN** `AGENTS.md` SHALL retain only the bootstrap contract, invariant governance rules, and canonical references needed for startup +- **AND** detailed workflow, validation, or finalization guidance SHALL live in dedicated rule artifacts rather than being duplicated inline + +### Requirement: Deterministic rule index and loading semantics + +The repository SHALL provide a canonical rule index that deterministically decides which governance rule files must be loaded for a given task. + +#### Scenario: Always-load rule subset + +- **WHEN** an agent loads the governance rule index +- **THEN** the index SHALL identify a mandatory always-load subset +- **AND** that subset SHALL include the non-negotiable checklist +- **AND** the index SHALL define the order in which always-load rules are processed + +#### Scenario: Applicability-based rule loading + +- **WHEN** a task involves worktree management, OpenSpec change selection, GitHub issue coordination, TDD gating, quality verification, or finalization +- **THEN** the index SHALL map those task signals to specific `docs/agent-rules/*.md` files +- **AND** the index SHALL define which rule files are required versus optional for that task class +- **AND** the loading decision SHALL not depend on heuristic file-name guessing alone + +#### Scenario: Deterministic precedence + +- **WHEN** governance instructions overlap across `AGENTS.md`, the rule index, rule files, and change-local artifacts +- **THEN** the repository SHALL define a single precedence order for which instruction wins +- **AND** the precedence order SHALL include explicit handling for direct user override where repository governance permits it + +### Requirement: Governance rule files use machine-readable frontmatter + +Every dedicated governance rule artifact SHALL include machine-readable frontmatter that defines how and when the rule applies. + +#### Scenario: Required frontmatter fields are present + +- **WHEN** a file under `docs/agent-rules/` is intended to govern agent behavior +- **THEN** it SHALL include frontmatter fields for rule identity, applicability, priority, blocking semantics, and stop conditions +- **AND** it SHALL declare whether the file is always loaded +- **AND** it SHALL declare whether user interaction is required when the rule blocks progress + +#### Scenario: Frontmatter drives deterministic behavior + +- **WHEN** an agent evaluates a rule file with frontmatter +- **THEN** it can determine from metadata whether the rule is mandatory for the current task +- **AND** it can determine whether the rule requires a hard stop, read-only continuation, or interactive clarification +- **AND** it does not need to infer those semantics solely from unstructured prose + +### Requirement: Governance must define explicit stop and continue behavior + +The governance system SHALL define explicit blocking behavior for stale changes, concurrency ambiguity, missing metadata, and TDD gate violations. + +#### Scenario: Blocking ambiguity requires user clarification + +- **WHEN** a selected change is stale, superseded, ambiguous, or linked to GitHub work already in progress +- **THEN** the applicable rule SHALL require the agent to stop implementation work +- **AND** the rule SHALL state that the ambiguity must be surfaced to the user for clarification +- **AND** the rule SHALL define whether read-only investigation may continue while implementation remains blocked + +#### Scenario: TDD gate remains non-bypassable in compact governance + +- **WHEN** a task changes behavior in code +- **THEN** the applicable rule SHALL still require spec updates, test creation, failing-test evidence, implementation, and passing evidence in that order +- **AND** compact governance SHALL not weaken or omit that sequence + +### Requirement: Public GitHub work must pass metadata completeness checks + +The governance system SHALL define explicit readiness checks for linked GitHub change issues before implementation proceeds for public repository work. + +#### Scenario: Parent and dependency metadata must be complete + +- **WHEN** an agent prepares to implement a publicly tracked change with a linked GitHub issue +- **THEN** the applicable governance rules SHALL require verifying the issue's parent relationship, blockers, and blocked-by relationships against current repository GitHub reality +- **AND** the parent lookup SHALL use the local hierarchy cache first and refresh the cache when repository-defined freshness rules require it +- **AND** implementation SHALL not proceed if the required parent or dependency metadata is missing or ambiguous + +#### Scenario: Labels and project assignment must be complete + +- **WHEN** an agent prepares to implement a publicly tracked change with a linked GitHub issue +- **THEN** the applicable governance rules SHALL require verifying that the issue has the required labels and project assignment for repository workflow completeness +- **AND** implementation SHALL not proceed until that metadata is complete or the user explicitly directs an allowed exception path + +#### Scenario: Live GitHub issue state can block implementation + +- **WHEN** an agent prepares to implement a publicly tracked change with a linked GitHub issue +- **AND** the issue is already marked `in progress` +- **THEN** the governance rules SHALL treat that state as a concurrency ambiguity +- **AND** the agent SHALL stop implementation work and ask the user to clarify whether the change is already being worked in another session +- **AND** the rules SHALL define whether only read-only investigation may continue while implementation remains blocked + +### Requirement: Canonical aliases prevent instruction drift + +Repository instruction surfaces other than `AGENTS.md` SHALL reference the canonical governance rule system instead of embedding duplicate long-form policy text. + +#### Scenario: Alias instruction surfaces stay synchronized + +- **WHEN** a contributor reads another repository instruction surface such as `CLAUDE.md` or generated IDE guidance +- **THEN** the surface SHALL reference the canonical rule system for governance semantics +- **AND** it SHALL avoid copying long-form governance content that could drift from the canonical source diff --git a/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/specs/github-hierarchy-cache/spec.md b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/specs/github-hierarchy-cache/spec.md new file mode 100644 index 00000000..1e440ad5 --- /dev/null +++ b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/specs/github-hierarchy-cache/spec.md @@ -0,0 +1,19 @@ +## MODIFIED Requirements + +### Requirement: Repository governance must use cache-first hierarchy lookup + +Repository governance instructions SHALL direct contributors and agents to consult the local hierarchy cache before performing manual GitHub lookups for Epic or Feature parenting. + +#### Scenario: Cache-first governance guidance + +- **WHEN** a contributor reads `AGENTS.md` or `openspec/config.yaml` for GitHub issue setup guidance +- **THEN** the instructions tell them to consult the local hierarchy cache first +- **AND** the instructions define when the sync script must be rerun to refresh stale hierarchy metadata +- **AND** the instructions state that the cache is local ephemeral state and must not be committed + +#### Scenario: Session bootstrap refreshes missing or stale cache + +- **WHEN** an agent starts a governance-sensitive session that depends on GitHub hierarchy metadata +- **AND** the local hierarchy cache is missing or stale according to repository-defined freshness rules +- **THEN** the bootstrap guidance SHALL require rerunning the hierarchy cache sync script before continuing with issue-parenting or blocker-resolution work +- **AND** the compact governance flow SHALL treat the refresh as part of deterministic startup rather than an optional later reminder diff --git a/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/tasks.md b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/tasks.md new file mode 100644 index 00000000..adf9d574 --- /dev/null +++ b/openspec/changes/archive/2026-04-10-governance-03-deterministic-agent-governance-loading/tasks.md @@ -0,0 +1,43 @@ +# Tasks: governance-03-deterministic-agent-governance-loading + +## 1. Branch and governance preparation + +- [x] 1.1 Create dedicated worktree branch `feature/governance-03-deterministic-agent-governance-loading` from `origin/dev` before implementation work: `scripts/worktree.sh create feature/governance-03-deterministic-agent-governance-loading`. +- [x] 1.2 In the new worktree directory, bootstrap Python tooling with `hatch env create`. +- [x] 1.3 Run pre-flight checks from the worktree root: `hatch run smart-test-status` and `hatch run contract-test-status`. +- [x] 1.4 Confirm governance-01 and governance-02 outputs remain the dependency baseline for this change and update `openspec/CHANGE_ORDER.md` metadata if sequencing notes need adjustment. +- [x] 1.5 Review current `AGENTS.md`, related instruction surfaces, and existing docs/frontmatter validators to identify the files that must participate in the compact-governance migration. +- [x] 1.6 After the PR merges: run `git worktree remove`, `git branch -d`, and `git worktree prune` per `AGENTS.md`; delete the worktree-local `.venv` (or other env path) if you no longer need that checkout. + +## 2. Spec-first and test-first preparation + +- [x] 2.1 Finalize the `agent-governance-loading` and `github-hierarchy-cache` spec deltas and cross-check scenario completeness. +- [x] 2.2 Add or update tests/validators that cover rule-file frontmatter, deterministic always-load behavior, precedence handling, cache-refresh bootstrap rules, and GitHub metadata completeness / `in progress` ambiguity handling. +- [x] 2.3 Run targeted tests to capture failing-first behavior and record the results in `TDD_EVIDENCE.md` before production edits. + +## 3. Governance implementation + +- [x] 3.1 Replace the long-form `AGENTS.md` body with a compact bootstrap/governance contract that points to canonical rule artifacts. +- [x] 3.2 Create `docs/agent-rules/INDEX.md`, `docs/agent-rules/05-non-negotiable-checklist.md`, and the first domain rule files needed to cover bootstrap, change validation, TDD, quality gates, docs/versioning, and finalization. +- [x] 3.3 Implement or extend validation so governance rule files enforce the required frontmatter schema and deterministic metadata fields. +- [x] 3.4 Update related instruction surfaces and workflow guidance so they reference the canonical governance rule system instead of duplicating long-form policy text. +- [x] 3.5 Update cache-first governance guidance so session bootstrap refreshes `.specfact/backlog/github_hierarchy_cache.md` when it is missing or stale. +- [x] 3.6 Implement or extend governance logic and docs so public-work readiness checks cover parent resolution, labels, project assignment, blockers / blocked-by relationships, and `in progress` issue-state clarification. + +## 4. Validation and documentation + +- [x] 4.1 Re-run targeted and required quality gates until the compact-governance behavior and docs validation pass. +- [ ] 4.2 Run `hatch run specfact code review run --json --out .specfact/code-review.json` and resolve all findings, including warnings. + +**Waiver (task 4.2):** The mandated command is **not satisfied** in environments where the `nold-ai/specfact-codebase` module +that provides `specfact code review` is not installed; the CLI fails immediately with a missing-module error, so +`.specfact/code-review.json` cannot be produced here. Re-run task 4.2 in a worktree with that module available, or keep this +waiver with maintainer approval. +- [x] 4.3 Update user-facing documentation and navigation for the new governance artifact layout and explain how `AGENTS.md` now delegates to canonical rule files. +- [x] 4.4 Run `openspec validate governance-03-deterministic-agent-governance-loading --strict` and resolve all issues. + +## 5. Delivery + +- [x] 5.1 Refresh `TDD_EVIDENCE.md` with passing-after commands and timestamps. +- [x] 5.2 Update `openspec/CHANGE_ORDER.md` implementation status or dependency notes if anything changed during delivery. +- [x] 5.3 Open a PR from `feature/governance-03-deterministic-agent-governance-loading` to `dev` with spec/test/code/docs/code-review evidence. diff --git a/openspec/changes/cli-val-01-behavior-contract-standard/proposal.md b/openspec/changes/cli-val-01-behavior-contract-standard/proposal.md index 3258c25b..fb3f62b4 100644 --- a/openspec/changes/cli-val-01-behavior-contract-standard/proposal.md +++ b/openspec/changes/cli-val-01-behavior-contract-standard/proposal.md @@ -36,6 +36,6 @@ Every SpecFact CLI feature should have a single artifact that answers: "What doe - **GitHub Issue**: #279 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/279 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: proposed diff --git a/openspec/changes/cli-val-02-output-snapshot-stability/proposal.md b/openspec/changes/cli-val-02-output-snapshot-stability/proposal.md index 19e59da6..20a0c442 100644 --- a/openspec/changes/cli-val-02-output-snapshot-stability/proposal.md +++ b/openspec/changes/cli-val-02-output-snapshot-stability/proposal.md @@ -35,6 +35,6 @@ Users who script SpecFact in CI/CD pipelines rely on stable output: help text fo - **GitHub Issue**: #280 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/280 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: proposed diff --git a/openspec/changes/cli-val-03-misuse-safety-proof/proposal.md b/openspec/changes/cli-val-03-misuse-safety-proof/proposal.md index f6e88c7e..25aa611e 100644 --- a/openspec/changes/cli-val-03-misuse-safety-proof/proposal.md +++ b/openspec/changes/cli-val-03-misuse-safety-proof/proposal.md @@ -33,6 +33,6 @@ When a user invokes SpecFact CLI with wrong flags, missing files, invalid YAML, - **GitHub Issue**: #281 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/281 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: proposed diff --git a/openspec/changes/cli-val-04-acceptance-test-runner/proposal.md b/openspec/changes/cli-val-04-acceptance-test-runner/proposal.md index 9fb421c0..37b5a98a 100644 --- a/openspec/changes/cli-val-04-acceptance-test-runner/proposal.md +++ b/openspec/changes/cli-val-04-acceptance-test-runner/proposal.md @@ -36,6 +36,6 @@ The 73+ existing CliRunner tests prove commands work in-process. But SpecFact is - **GitHub Issue**: #282 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/282 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: proposed diff --git a/openspec/changes/cli-val-05-ci-integration/proposal.md b/openspec/changes/cli-val-05-ci-integration/proposal.md index 1d57b19e..7540544f 100644 --- a/openspec/changes/cli-val-05-ci-integration/proposal.md +++ b/openspec/changes/cli-val-05-ci-integration/proposal.md @@ -37,6 +37,6 @@ Validation only has value if it runs automatically. Today, `pr-orchestrator.yml` - **GitHub Issue**: #283 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/283 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: proposed diff --git a/openspec/changes/cli-val-05-ci-integration/specs/cli-validation-ci-gates/spec.md b/openspec/changes/cli-val-05-ci-integration/specs/cli-validation-ci-gates/spec.md index b46ebfb4..319876ea 100644 --- a/openspec/changes/cli-val-05-ci-integration/specs/cli-validation-ci-gates/spec.md +++ b/openspec/changes/cli-val-05-ci-integration/specs/cli-validation-ci-gates/spec.md @@ -64,6 +64,7 @@ The contract-first test system SHALL include CLI behavior contracts as a recogni - **WHEN** CLI behavior contract files exist in `tests/cli-contracts/` - **THEN** CLI scenario validation is included in the test run - **AND** results appear alongside existing contract/exploration/scenario tiers. + ### Requirement: Deterministic Hatch Toolchain in CI The CI pipeline SHALL install a deterministic, compatible Hatch toolchain for Python 3.12 test jobs. diff --git a/openspec/changes/cli-val-06-copilot-test-generation/proposal.md b/openspec/changes/cli-val-06-copilot-test-generation/proposal.md index 7a72926c..88534cd3 100644 --- a/openspec/changes/cli-val-06-copilot-test-generation/proposal.md +++ b/openspec/changes/cli-val-06-copilot-test-generation/proposal.md @@ -34,6 +34,6 @@ SpecFact already has a `generate test-prompt` workflow for creating tests using - **GitHub Issue**: #284 -- **Issue URL**: https://github.com/nold-ai/specfact-cli/issues/284 +- **Issue URL**: - **Repository**: nold-ai/specfact-cli - **Last Synced Status**: proposed diff --git a/openspec/changes/dogfooding-01-full-chain-e2e-proof/design.md b/openspec/changes/dogfooding-01-full-chain-e2e-proof/design.md index 4520a12c..1554fafb 100644 --- a/openspec/changes/dogfooding-01-full-chain-e2e-proof/design.md +++ b/openspec/changes/dogfooding-01-full-chain-e2e-proof/design.md @@ -5,11 +5,13 @@ This change defines an auditable dogfooding path proving the complete business-t ## Goals / Non-Goals **Goals:** + - Produce objective E2E proof artifacts for the full-chain claim. - Ensure proof is reproducible in CI and local workflows. - Tie proof to release readiness and product positioning. **Non-Goals:** + - No unrelated feature expansion. - No synthetic-only demo path; evidence must use real project artifacts. diff --git a/openspec/changes/dogfooding-01-full-chain-e2e-proof/proposal.md b/openspec/changes/dogfooding-01-full-chain-e2e-proof/proposal.md index 86f2d951..0a8a013c 100644 --- a/openspec/changes/dogfooding-01-full-chain-e2e-proof/proposal.md +++ b/openspec/changes/dogfooding-01-full-chain-e2e-proof/proposal.md @@ -2,16 +2,10 @@ ## Why - - - To claim SpecFact CLI as the end-to-end "swiss knife" for agile DevOps teams, the tool must prove its own flow with real artifacts. This change establishes a dedicated dogfooding implementation and evidence path from requirements through architecture, specs, code, tests, and CI evidence output. ## What Changes - - - - **NEW**: Define a dogfooding scenario set using real SpecFact backlog items and requirements - **NEW**: Require one complete end-to-end traceability run: - backlog item -> requirement -> architecture artifact -> spec -> code/test references -> full-chain evidence JSON @@ -20,6 +14,7 @@ To claim SpecFact CLI as the end-to-end "swiss knife" for agile DevOps teams, th - **NEW**: Add CI/report outputs proving wave gate completion for E2E chain ## Capabilities + ### New Capabilities - `dogfooding-full-chain-e2e`: End-to-end self-validation flow for SpecFact CLI that proves requirements-to-evidence traceability in a real project slice. @@ -29,7 +24,6 @@ To claim SpecFact CLI as the end-to-end "swiss knife" for agile DevOps teams, th (none) - --- ## Source Tracking diff --git a/openspec/changes/dogfooding-01-full-chain-e2e-proof/specs/dogfooding-full-chain-e2e/spec.md b/openspec/changes/dogfooding-01-full-chain-e2e-proof/specs/dogfooding-full-chain-e2e/spec.md index b512dd50..4699f207 100644 --- a/openspec/changes/dogfooding-01-full-chain-e2e-proof/specs/dogfooding-full-chain-e2e/spec.md +++ b/openspec/changes/dogfooding-01-full-chain-e2e-proof/specs/dogfooding-full-chain-e2e/spec.md @@ -1,22 +1,27 @@ ## ADDED Requirements ### Requirement: End-to-End Dogfooding Proof + The system SHALL provide a reproducible dogfooding workflow that proves full-chain traceability from backlog to CI evidence. #### Scenario: Full chain proof is generated for a real backlog slice + - **WHEN** dogfooding workflow runs for selected SpecFact backlog items - **THEN** each item is traceable through requirement, architecture, spec, code/test, and evidence outputs - **AND** missing links are reported as gate failures ### Requirement: Dogfooding Evidence Is CI-Consumable + The system SHALL produce machine-readable evidence artifacts for the dogfooding run. #### Scenario: CI validates dogfooding proof + - **WHEN** CI executes the dogfooding full-chain run - **THEN** evidence artifacts are emitted in a stable schema - **AND** wave gate status is derivable from those artifacts #### Scenario: Dogfood proof includes clean-code evidence + - **WHEN** the dogfooding proof runs with code-quality enabled - **THEN** the resulting evidence bundle includes clean-code category results - **AND** release-readiness proof fails if required clean-code categories regress diff --git a/openspec/changes/governance-01-evidence-output/design.md b/openspec/changes/governance-01-evidence-output/design.md index 75561c7e..4d85cf23 100644 --- a/openspec/changes/governance-01-evidence-output/design.md +++ b/openspec/changes/governance-01-evidence-output/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `governance-01-evidence-output` from t ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/governance-01-evidence-output/proposal.md b/openspec/changes/governance-01-evidence-output/proposal.md index cac85bb5..89c2dd87 100644 --- a/openspec/changes/governance-01-evidence-output/proposal.md +++ b/openspec/changes/governance-01-evidence-output/proposal.md @@ -2,9 +2,6 @@ ## Why - - - Enterprise environments require machine-readable evidence that policies were enforced, traceability exists, and exceptions are tracked. Current validation output is human-readable (Markdown/terminal) but not suitable for CI gates, audit systems, or compliance dashboards. A standardized evidence JSON output format β€” covering policy results, traceability coverage, exception status, and timestamps β€” makes SpecFact validation results consumable by any CI/CD pipeline, audit tool, or governance platform. ## Ownership Alignment (2026-04-08) @@ -17,10 +14,8 @@ Enterprise environments require machine-readable evidence that policies were enf ## What Changes - - - - **NEW**: Evidence writer producing standardized JSON artifacts: + ```json { "schema_version": "1.0", @@ -51,6 +46,7 @@ Enterprise environments require machine-readable evidence that policies were enf "ci_exit_code": 0 } ``` + - **NEW**: `--evidence-dir .specfact/evidence/` flag on `specfact validate --full-chain` to persist evidence artifacts per run - **NEW**: `--ci-mode` flag that sets exit codes based on profile enforcement mode: advisory=always 0, mixed=1 for hard-fail rules only, hard=1 for any failure - **NEW**: Evidence artifact naming: `{timestamp}_{run_id}_evidence.json` for audit trail @@ -61,6 +57,7 @@ Enterprise environments require machine-readable evidence that policies were enf - **NEW**: Ownership authority β€” this change is authoritative for evidence JSON envelope/schema; sibling governance changes may add fields only through this envelope contract. ## Capabilities + ### New Capabilities - `governance-evidence-output`: Machine-readable JSON evidence artifacts for CI/CD gates and audit systems, with per-run persistence, CI exit code modes, coverage percentages, exception status, and profile-aware verdicts. @@ -71,7 +68,6 @@ Enterprise environments require machine-readable evidence that policies were enf - `policy-engine`: Results formatted as evidence-compatible structures with run_id and timestamps - `governance-evidence-output`: Extended with a `code_quality` section that remains parallel to `validation_results` rather than introducing a new traceability layer - --- ## Source Tracking diff --git a/openspec/changes/governance-01-evidence-output/specs/full-chain-validation/spec.md b/openspec/changes/governance-01-evidence-output/specs/full-chain-validation/spec.md index ab936839..f64fd80b 100644 --- a/openspec/changes/governance-01-evidence-output/specs/full-chain-validation/spec.md +++ b/openspec/changes/governance-01-evidence-output/specs/full-chain-validation/spec.md @@ -1,9 +1,11 @@ ## MODIFIED Requirements ### Requirement: Full Chain Validation + Full-chain validation SHALL emit governance-ready evidence artifacts. #### Scenario: Evidence artifact is written with stable schema envelope + - **GIVEN** full-chain validation executes with evidence output enabled - **WHEN** command completes - **THEN** evidence includes schema version, timestamp, profile, policy mode, layer summaries, and overall status diff --git a/openspec/changes/governance-01-evidence-output/specs/governance-evidence-output/oscal-trace-delta.md b/openspec/changes/governance-01-evidence-output/specs/governance-evidence-output/oscal-trace-delta.md index 89a5ea4a..3c1e0416 100644 --- a/openspec/changes/governance-01-evidence-output/specs/governance-evidence-output/oscal-trace-delta.md +++ b/openspec/changes/governance-01-evidence-output/specs/governance-evidence-output/oscal-trace-delta.md @@ -1,9 +1,11 @@ ## ADDED Requirements ### Requirement: OSCAL-Aligned Evidence Envelope with Trace Fields + The system SHALL extend the governance evidence JSON envelope to include a `trace` object with `upstream` and `downstream` arrays, aligned with OSCAL Assessment Results model patterns, enabling bidirectional artifact navigation from any evidence record. #### Scenario: Evidence envelope contains trace links for requirement-level artifacts + - **GIVEN** a `BusinessRule` artifact that was validated by SpecFact - **WHEN** the governance evidence envelope is generated for that artifact - **THEN** the evidence JSON includes a `trace` object: @@ -12,21 +14,25 @@ The system SHALL extend the governance evidence JSON envelope to include a `trac - **AND** the envelope validates against the updated evidence schema #### Scenario: Evidence envelope captures per-check results + - **GIVEN** a validation run that checks schema_conformance, gwt_parseable, example_bound, and outcome_linked - **WHEN** the evidence envelope is emitted - **THEN** the `validation.checks` array contains one entry per check with `name`, `result` (pass/fail/error), and optional metadata fields (e.g., `test_id`, `outcome_id`) - **AND** the overall `verdict` field is derivable from the check results without re-running validation #### Scenario: OSCAL-aligned structure for compliance consumers + - **GIVEN** a governance evidence JSON file produced by SpecFact - **WHEN** it is consumed by an OSCAL Assessment Results reader - **THEN** the `validation.verdict` field maps to OSCAL's `finding.target.status` (pass/fail/not-applicable) - **AND** the `artifact.hash` field provides the `subject.resource-id` equivalent for audit traceability ### Requirement: Artifact Hash in Evidence Envelope + The system SHALL include a SHA-256 hash of the validated artifact in every evidence envelope, enabling immutable audit trail construction. #### Scenario: Artifact hash computed and included in evidence + - **GIVEN** a requirement artifact file at `.specfact/requirements/BR-001.req.yaml` - **WHEN** `specfact validate --full-chain --evidence-dir .specfact/evidence/` runs - **THEN** the evidence envelope for BR-001 includes `artifact.hash: "sha256:"` diff --git a/openspec/changes/governance-01-evidence-output/specs/governance-evidence-output/spec.md b/openspec/changes/governance-01-evidence-output/specs/governance-evidence-output/spec.md index 45e0b171..7e4589c8 100644 --- a/openspec/changes/governance-01-evidence-output/specs/governance-evidence-output/spec.md +++ b/openspec/changes/governance-01-evidence-output/specs/governance-evidence-output/spec.md @@ -1,21 +1,25 @@ ## ADDED Requirements ### Requirement: Governance Evidence Output + The system SHALL produce machine-readable governance evidence suitable for CI and audit ingestion. #### Scenario: CI-consumable evidence includes policy and exception context + - **GIVEN** validation runs in CI mode - **WHEN** evidence is generated - **THEN** policy results and active exception references are included - **AND** each exception includes identifier and expiration metadata. #### Scenario: Evidence contains layer-level coverage metrics + - **GIVEN** full-chain validation has transition results - **WHEN** governance evidence is emitted - **THEN** each layer contains pass/fail/advisory counts and coverage percentages - **AND** overall verdict is derivable from the evidence alone. #### Scenario: Evidence carries clean-code results as a parallel quality dimension + - **GIVEN** a validation run includes `specfact review` clean-code output - **WHEN** governance evidence is emitted - **THEN** the envelope includes a top-level `code_quality` section with category counts and verdict diff --git a/openspec/changes/governance-01-evidence-output/specs/policy-engine/spec.md b/openspec/changes/governance-01-evidence-output/specs/policy-engine/spec.md index 492c42bd..92ca0957 100644 --- a/openspec/changes/governance-01-evidence-output/specs/policy-engine/spec.md +++ b/openspec/changes/governance-01-evidence-output/specs/policy-engine/spec.md @@ -1,9 +1,11 @@ ## MODIFIED Requirements ### Requirement: Policy Engine + Policy evaluation outputs SHALL be serializable into governance evidence records. #### Scenario: Policy rule results include evidence-ready fields + - **GIVEN** policy validation completes - **WHEN** evidence serialization runs - **THEN** each rule result includes rule ID, severity, mode, and outcome diff --git a/openspec/changes/governance-02-exception-management/design.md b/openspec/changes/governance-02-exception-management/design.md index 833849de..9098ea61 100644 --- a/openspec/changes/governance-02-exception-management/design.md +++ b/openspec/changes/governance-02-exception-management/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `governance-02-exception-management` f ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/governance-02-exception-management/proposal.md b/openspec/changes/governance-02-exception-management/proposal.md index e7f3b81d..93ae2abc 100644 --- a/openspec/changes/governance-02-exception-management/proposal.md +++ b/openspec/changes/governance-02-exception-management/proposal.md @@ -2,9 +2,6 @@ ## Why - - - Enterprises always need exceptions β€” a legacy service can't comply with the new API versioning policy until migration completes, a regulatory deadline grants a 6-month grace period. But untracked exceptions defeat governance: they become permanent workarounds. Explicit, tracked, time-bound exceptions in config β€” with automatic expiry, monthly digests, and audit trail β€” make governance flexible without losing accountability. ## Ownership Alignment (2026-04-08) @@ -17,10 +14,8 @@ Enterprises always need exceptions β€” a legacy service can't comply with the ne ## What Changes - - - - **NEW**: Exception declaration in `.specfact/exceptions.yaml`: + ```yaml exceptions: - id: EXC-1234 @@ -31,6 +26,7 @@ Enterprises always need exceptions β€” a legacy service can't comply with the ne approved_by: "CIO" created_at: 2026-02-15 ``` + - **NEW**: `specfact exceptions list` β€” show all active, approaching expiry, and expired exceptions - **NEW**: `specfact exceptions add --policy --scope --reason --expires --approved-by ` β€” add a tracked exception - **NEW**: `specfact exceptions check` β€” verify all exceptions are still valid (not expired); flag expired exceptions as hard failures @@ -44,6 +40,7 @@ Enterprises always need exceptions β€” a legacy service can't comply with the ne - **NEW**: Ownership authority β€” this change is authoritative for exception-scope suppression and expiry semantics; evidence schema remains owned by governance-01. ## Capabilities + ### New Capabilities - `exception-management`: Time-bound, tracked policy exceptions with automatic expiry, scope-limited suppression, approaching-expiry warnings, monthly digest generation, and audit trail in evidence artifacts. @@ -54,7 +51,6 @@ Enterprises always need exceptions β€” a legacy service can't comply with the ne - `exception-management`: Extended so clean-code exceptions are expressed by policy rule ID, not by introducing a parallel `principle` exception schema - `governance-evidence-output`: Extended to include exception status in evidence artifacts - --- ## Source Tracking diff --git a/openspec/changes/governance-02-exception-management/specs/exception-management/spec.md b/openspec/changes/governance-02-exception-management/specs/exception-management/spec.md index 53892877..c1aca984 100644 --- a/openspec/changes/governance-02-exception-management/specs/exception-management/spec.md +++ b/openspec/changes/governance-02-exception-management/specs/exception-management/spec.md @@ -1,21 +1,25 @@ ## ADDED Requirements ### Requirement: Exception Management + The system SHALL support explicit, tracked, and time-bound governance exceptions. #### Scenario: Active exception suppresses blocking until expiry + - **GIVEN** `.specfact/exceptions.yaml` contains an active exception for a policy and scope - **WHEN** policy validation runs before expiration - **THEN** matching violation is downgraded from blocking to non-blocking - **AND** evidence records the applied exception ID. #### Scenario: Expired exception restores blocking behavior + - **GIVEN** exception expiry date is in the past - **WHEN** matching policy violation occurs - **THEN** violation is treated per normal mode semantics - **AND** output identifies the exception as expired. #### Scenario: Clean-code exceptions target policy rule identifiers + - **GIVEN** `.specfact/exceptions.yaml` contains `policy: clean-code-principles/banned-generic-public-names` - **WHEN** a matching naming finding is emitted within the declared scope - **THEN** that specific rule is suppressed according to the exception lifecycle diff --git a/openspec/changes/governance-02-exception-management/specs/governance-evidence-output/spec.md b/openspec/changes/governance-02-exception-management/specs/governance-evidence-output/spec.md index d94d3713..c470d1c9 100644 --- a/openspec/changes/governance-02-exception-management/specs/governance-evidence-output/spec.md +++ b/openspec/changes/governance-02-exception-management/specs/governance-evidence-output/spec.md @@ -1,9 +1,11 @@ ## MODIFIED Requirements ### Requirement: Governance Evidence Output + Governance evidence SHALL include exception lifecycle status for active and expired exceptions. #### Scenario: Exception lifecycle is visible in evidence + - **GIVEN** evidence generation runs with configured exceptions - **WHEN** artifact is emitted - **THEN** evidence lists applied, pending-expiry, and expired exception states diff --git a/openspec/changes/governance-02-exception-management/specs/policy-engine/spec.md b/openspec/changes/governance-02-exception-management/specs/policy-engine/spec.md index 481da689..bf993d5f 100644 --- a/openspec/changes/governance-02-exception-management/specs/policy-engine/spec.md +++ b/openspec/changes/governance-02-exception-management/specs/policy-engine/spec.md @@ -1,9 +1,11 @@ ## MODIFIED Requirements ### Requirement: Policy Engine + Policy evaluation SHALL apply scoped exceptions before computing final blocking outcome. #### Scenario: Scope mismatch does not suppress violation + - **GIVEN** exception exists for a different scope than the evaluated artifact - **WHEN** policy rule fails - **THEN** violation is not suppressed diff --git a/openspec/changes/integration-01-cross-change-contracts/design.md b/openspec/changes/integration-01-cross-change-contracts/design.md index a6e841a9..e1679e06 100644 --- a/openspec/changes/integration-01-cross-change-contracts/design.md +++ b/openspec/changes/integration-01-cross-change-contracts/design.md @@ -5,11 +5,13 @@ This change is the integration umbrella for the 2026-02-15 architecture-layer wa ## Goals / Non-Goals **Goals:** + - Define authoritative ownership for shared interfaces and files. - Define compatibility contracts across profile/requirements/architecture/validation/sync/governance/AI layers. - Add explicit integration gates for multi-wave rollout. **Non-Goals:** + - No production code implementation. - No replacement of existing feature proposals. diff --git a/openspec/changes/integration-01-cross-change-contracts/proposal.md b/openspec/changes/integration-01-cross-change-contracts/proposal.md index 956cc94d..98e0a9e3 100644 --- a/openspec/changes/integration-01-cross-change-contracts/proposal.md +++ b/openspec/changes/integration-01-cross-change-contracts/proposal.md @@ -2,16 +2,10 @@ ## Why - - - The architecture integration wave introduces many parallel changes that touch shared files and interfaces (`ProjectBundle` extensions, backlog adapters, policy engine outputs). Without one umbrella integration contract, implementation drift and merge collisions are likely. This change creates a single authoritative contract and ownership model so all dependent changes can compose into one end-to-end system. ## What Changes - - - - **NEW**: Define a cross-change integration contract for shared interfaces: - ProjectBundle extension namespaces and merge order - Backlog adapter extension protocol boundaries @@ -26,6 +20,7 @@ The architecture integration wave introduces many parallel changes that touch sh - **NEW**: Add integration acceptance gate definition for Wave 6+ and Wave 8 closure ## Capabilities + ### New Capabilities - `cross-change-integration-contract`: A single integration contract that defines interface boundaries, ownership authority, and compatibility rules across all architecture integration changes. @@ -34,7 +29,6 @@ The architecture integration wave introduces many parallel changes that touch sh (none) - --- ## Source Tracking diff --git a/openspec/changes/integration-01-cross-change-contracts/specs/cross-change-integration-contract/spec.md b/openspec/changes/integration-01-cross-change-contracts/specs/cross-change-integration-contract/spec.md index cc83cc61..6d88225c 100644 --- a/openspec/changes/integration-01-cross-change-contracts/specs/cross-change-integration-contract/spec.md +++ b/openspec/changes/integration-01-cross-change-contracts/specs/cross-change-integration-contract/spec.md @@ -1,25 +1,31 @@ ## ADDED Requirements ### Requirement: Cross-Change Ownership Contract + The system SHALL define authoritative ownership boundaries for shared interfaces and overlapping implementation files across active architecture integration changes. #### Scenario: Shared interface has one owner + - **WHEN** multiple changes modify the same interface family - **THEN** exactly one change is designated owner for canonical interface semantics - **AND** dependent changes align to that canonical contract ### Requirement: Cross-Change Compatibility Contract + The system SHALL define compatibility constraints for shared payloads and extension namespaces used across architecture integration changes. #### Scenario: Shared payload compatibility is validated + - **WHEN** a dependent change introduces payload extensions - **THEN** the extension preserves compatibility with the owner-defined envelope - **AND** migration guidance is required for any non-additive change ### Requirement: Integration Gate for Wave Progression + The system SHALL require objective integration gate criteria to close each architecture integration wave. #### Scenario: Wave cannot close without gate evidence + - **WHEN** a wave completion is proposed - **THEN** required gate evidence is present and traceable - **AND** unresolved cross-change conflicts block wave closure diff --git a/openspec/changes/openspec-01-intent-trace/CHANGE_VALIDATION.md b/openspec/changes/openspec-01-intent-trace/CHANGE_VALIDATION.md index 52126942..e505e2ef 100644 --- a/openspec/changes/openspec-01-intent-trace/CHANGE_VALIDATION.md +++ b/openspec/changes/openspec-01-intent-trace/CHANGE_VALIDATION.md @@ -17,6 +17,7 @@ ## Breaking Changes Detected None. All interface extensions are additive and optional: + - `parse_change_proposal()` returns `dict[str, Any]` β€” adding optional `"intent_trace"` key is non-breaking - `--import-intent` CLI flag has no default effect (opt-in) - New files (`intent_trace_validator.py`, `intent-trace.schema.json`) have no existing callers @@ -26,6 +27,7 @@ None. All interface extensions are additive and optional: **Constraint**: `_parse_proposal_content()` at `openspec_parser.py:335` has type annotation `dict[str, str]` (return type). If intent trace data (a nested dict) were added here, `@beartype` would raise a type error. **Required implementation approach**: Intent trace extraction MUST be done in `parse_change_proposal()` (returns `dict[str, Any]`) by: + 1. Calling `_parse_proposal_content(content)` as usual β†’ returns `dict[str, str]` 2. Separately extracting the YAML fenced block under `## Intent Trace` from `content` 3. Parsing with `yaml.safe_load()` and assigning to `result["intent_trace"]` diff --git a/openspec/changes/openspec-01-intent-trace/design.md b/openspec/changes/openspec-01-intent-trace/design.md index 22e7db53..d7347e6c 100644 --- a/openspec/changes/openspec-01-intent-trace/design.md +++ b/openspec/changes/openspec-01-intent-trace/design.md @@ -9,6 +9,7 @@ The principle is: **"OpenSpec owns the intent. SpecFact owns the evidence."** Op ## Goals / Non-Goals **Goals:** + - Define the `## Intent Trace` section YAML schema and JSON Schema validator - Extend the OpenSpec bridge adapter to parse and import intent artifacts when the section is present - Keep the `## Intent Trace` section strictly optional β€” existing proposals without it are unaffected @@ -16,6 +17,7 @@ The principle is: **"OpenSpec owns the intent. SpecFact owns the evidence."** Op - Produce `.specfact/requirements/{id}.req.yaml` artifacts from imported intent data **Non-Goals:** + - Forcing all existing OpenSpec proposals to add an `## Intent Trace` section - Building a new proposal authoring tool β€” the section is hand-authored YAML in Markdown - Replacing requirements-01/02 commands β€” the bridge adapter imports intent; the requirements module validates and traces it @@ -25,6 +27,7 @@ The principle is: **"OpenSpec owns the intent. SpecFact owns the evidence."** Op ### D1: YAML fenced block vs structured Markdown headings for Intent Trace **Decision**: YAML fenced block under `## Intent Trace` heading + ```yaml intent_trace: business_outcomes: @@ -38,6 +41,7 @@ intent_trace: when: "..." then: "..." ``` + **Rationale**: YAML is machine-parseable with a single `yaml.safe_load()` call and maps directly to Pydantic models. Structured Markdown headings require custom parsing logic that is brittle and hard to validate with JSON Schema. YAML fenced blocks are already used in GitHub Actions, Docker Compose, and Kubernetes manifests β€” authors are familiar with the pattern. **Alternative rejected**: Structured `### Business Outcomes / ### Business Rules` Markdown sub-sections β€” readable but not JSON Schema validatable. diff --git a/openspec/changes/openspec-01-intent-trace/specs/openspec-bridge-adapter/spec.md b/openspec/changes/openspec-01-intent-trace/specs/openspec-bridge-adapter/spec.md index 2012e709..e2dbb00e 100644 --- a/openspec/changes/openspec-01-intent-trace/specs/openspec-bridge-adapter/spec.md +++ b/openspec/changes/openspec-01-intent-trace/specs/openspec-bridge-adapter/spec.md @@ -1,21 +1,25 @@ ## MODIFIED Requirements ### Requirement: OpenSpec Bridge Adapter Import + The system SHALL import OpenSpec change proposals into SpecFact's project bundle with full backwards compatibility when the `## Intent Trace` section is absent, and include intent context when the section is present. #### Scenario: Proposal import without Intent Trace section is unchanged + - **GIVEN** an OpenSpec proposal that has no `## Intent Trace` section - **WHEN** `specfact sync bridge --adapter openspec` is run - **THEN** the import behaviour is identical to the pre-change behaviour - **AND** no error, warning, or advisory is emitted related to missing intent trace #### Scenario: Proposal import includes intent context when section is present + - **GIVEN** an OpenSpec proposal with a valid `## Intent Trace` section - **WHEN** `specfact sync bridge --adapter openspec` is run (without `--import-intent`) - **THEN** the proposal's intent trace metadata is attached to the project bundle as read-only context - **AND** `specfact project health-check` can report that intent context is available for the change #### Scenario: `openspec validate --strict` validates intent trace when present + - **GIVEN** an OpenSpec change with a proposal containing a `## Intent Trace` section - **WHEN** `openspec validate --strict` is run - **THEN** the validator checks the YAML block against `intent-trace.schema.json` diff --git a/openspec/changes/openspec-01-intent-trace/specs/openspec-bridge-intent-import/spec.md b/openspec/changes/openspec-01-intent-trace/specs/openspec-bridge-intent-import/spec.md index 6d04cad0..0492a350 100644 --- a/openspec/changes/openspec-01-intent-trace/specs/openspec-bridge-intent-import/spec.md +++ b/openspec/changes/openspec-01-intent-trace/specs/openspec-bridge-intent-import/spec.md @@ -1,48 +1,57 @@ ## ADDED Requirements ### Requirement: Bridge Adapter Intent Import + The system SHALL extend `specfact sync bridge --adapter openspec` with an `--import-intent` flag that reads the `## Intent Trace` YAML block from imported proposals and creates corresponding `.specfact/requirements/{id}.req.yaml` artifacts. #### Scenario: Intent import creates BusinessOutcome artifacts + - **GIVEN** an OpenSpec proposal with a `## Intent Trace` section containing at least one `business_outcomes` entry - **WHEN** `specfact sync bridge --adapter openspec --import-intent` is run - **THEN** a `.specfact/requirements/{id}.req.yaml` file is created for each `BusinessOutcome` in the intent trace - **AND** each artifact validates against the `BusinessOutcome` Pydantic schema without errors #### Scenario: Intent import creates BusinessRule artifacts + - **GIVEN** an OpenSpec proposal with `business_rules` entries in the `## Intent Trace` section - **WHEN** `specfact sync bridge --adapter openspec --import-intent` is run - **THEN** each `BusinessRule` (id, outcome_ref, given, when, then) is stored in the corresponding `.req.yaml` artifact under its parent `BusinessOutcome` - **AND** the `outcome_ref` is resolved to a valid `BusinessOutcome` ID in the imported requirements #### Scenario: Intent import skips existing artifacts without --overwrite + - **GIVEN** a `.specfact/requirements/BO-001.req.yaml` file already exists - **WHEN** `specfact sync bridge --adapter openspec --import-intent` is run without `--overwrite` - **THEN** the existing file is not modified - **AND** the CLI output notes the skipped artifact with its ID #### Scenario: Intent import overwrites with --overwrite flag + - **GIVEN** a `.specfact/requirements/BO-001.req.yaml` file already exists - **WHEN** `specfact sync bridge --adapter openspec --import-intent --overwrite` is run - **THEN** the existing file is updated with the content from the proposal's intent trace section - **AND** the CLI output confirms the overwritten artifact ID #### Scenario: Import without --import-intent ignores intent trace section + - **GIVEN** an OpenSpec proposal with a `## Intent Trace` section - **WHEN** `specfact sync bridge --adapter openspec` is run without `--import-intent` - **THEN** no `.specfact/requirements/` artifacts are created - **AND** the section is validated but not imported ### Requirement: Task-Level Requirement References + The system SHALL support an optional `requirement_refs` list field on individual tasks in OpenSpec `tasks.md` files, linking tasks to specific `BusinessRule` or `ArchitecturalConstraint` IDs. #### Scenario: Bridge adapter parses requirement_refs in tasks + - **GIVEN** a `tasks.md` file with a task containing `requirement_refs: ["BR-001", "AC-002"]` - **WHEN** the bridge adapter imports the proposal - **THEN** the imported task record includes the requirement ref IDs - **AND** they are included in the project bundle's task metadata #### Scenario: Advisory validation warns on unresolved requirement refs + - **GIVEN** a task with `requirement_refs: ["BR-999"]` where BR-999 does not exist in `.specfact/requirements/` - **WHEN** `specfact sync bridge --adapter openspec` is run - **THEN** the CLI emits an advisory warning: `[ADVISORY] Task X: requirement_refs contains unknown ID BR-999` diff --git a/openspec/changes/openspec-01-intent-trace/specs/openspec-intent-trace-schema/spec.md b/openspec/changes/openspec-01-intent-trace/specs/openspec-intent-trace-schema/spec.md index b9fa93ae..f0620149 100644 --- a/openspec/changes/openspec-01-intent-trace/specs/openspec-intent-trace-schema/spec.md +++ b/openspec/changes/openspec-01-intent-trace/specs/openspec-intent-trace-schema/spec.md @@ -1,42 +1,50 @@ ## ADDED Requirements ### Requirement: Intent Trace Section Schema + The system SHALL define a JSON Schema at `openspec/schemas/intent-trace.schema.json` that validates the `## Intent Trace` YAML block in OpenSpec proposal files. #### Scenario: Valid intent trace section passes schema validation + - **GIVEN** an OpenSpec proposal with a correctly structured `## Intent Trace` YAML block - **WHEN** `openspec validate --strict` is run - **THEN** the intent trace section validates without errors - **AND** the validation output confirms intent trace section is present and valid #### Scenario: Invalid intent trace section fails schema validation + - **GIVEN** an OpenSpec proposal with a `## Intent Trace` YAML block missing a required field (e.g., `id` on a `BusinessOutcome`) - **WHEN** `openspec validate --strict` is run - **THEN** the validation exits with a non-zero code - **AND** the error message identifies the specific field violation and the line context #### Scenario: Missing intent trace section is valid (section is optional) + - **GIVEN** an OpenSpec proposal without any `## Intent Trace` section - **WHEN** `openspec validate --strict` is run - **THEN** the validation passes without intent-trace errors - **AND** no warning about missing intent trace is emitted in normal mode #### Scenario: Intent trace schema includes schema version field + - **GIVEN** an intent trace YAML block with `schema_version: "1.0"` - **WHEN** the bridge adapter reads the block - **THEN** it accepts the artifact and records the schema version - **AND** if the schema version is unknown the adapter emits a clear error with supported versions ### Requirement: Intent Trace Evidence Field in Archive + The system SHALL support an optional `evidence` field in change archive metadata pointing to the evidence JSON envelope file produced during implementation. #### Scenario: Archive metadata includes evidence reference + - **GIVEN** an archived change that generated a governance evidence artifact - **WHEN** the archive metadata is read - **THEN** the `evidence` field contains a relative path to the `.specfact/evidence/` JSON file - **AND** the path resolves to a readable file on disk #### Scenario: Archive without evidence field is valid + - **GIVEN** an archived change that did not produce governance evidence - **WHEN** the archive metadata is validated - **THEN** validation passes without errors related to the missing evidence field diff --git a/openspec/changes/openspec-01-intent-trace/tasks.md b/openspec/changes/openspec-01-intent-trace/tasks.md index fc8d320c..949b54e0 100644 --- a/openspec/changes/openspec-01-intent-trace/tasks.md +++ b/openspec/changes/openspec-01-intent-trace/tasks.md @@ -5,6 +5,7 @@ Per `openspec/config.yaml`, tests MUST precede production code for any behavior-changing task. Order: + 1. Spec deltas (already in `specs/`) 2. Tests derived from spec scenarios β€” run and expect failure 3. Production code β€” implement until tests pass @@ -113,6 +114,7 @@ Do not implement production code until tests exist and have been run (expecting ## 9. GitHub issue creation - [ ] 9.1 Create GitHub issue: + ```bash gh issue create \ --repo nold-ai/specfact-cli \ @@ -121,6 +123,7 @@ Do not implement production code until tests exist and have been run (expecting --label "enhancement" \ --label "change-proposal" ``` + - [ ] 9.2 Link issue to project: `gh project item-add 1 --owner nold-ai --url ` - [ ] 9.3 Update `proposal.md` Source Tracking section with issue number and URL - [ ] 9.4 Link branch to issue: `gh issue develop --repo nold-ai/specfact-cli --name feature/openspec-01-intent-trace` @@ -130,6 +133,7 @@ Do not implement production code until tests exist and have been run (expecting - [ ] 10.1 `git add` all changed files; commit with `feat: add OpenSpec Intent Trace section and bridge adapter import` - [ ] 10.2 `git push -u origin feature/openspec-01-intent-trace` - [ ] 10.3 Create PR: + ```bash gh pr create \ --repo nold-ai/specfact-cli \ @@ -138,6 +142,7 @@ Do not implement production code until tests exist and have been run (expecting --title "feat: OpenSpec Intent Trace bridge adapter integration" \ --body-file /tmp/pr-body-openspec-01.md ``` + - [ ] 10.4 Link PR to project: `gh project item-add 1 --owner nold-ai --url ` - [ ] 10.5 Set project status to "In Progress" diff --git a/openspec/changes/profile-01-config-layering/design.md b/openspec/changes/profile-01-config-layering/design.md index 915179fc..4c47d34a 100644 --- a/openspec/changes/profile-01-config-layering/design.md +++ b/openspec/changes/profile-01-config-layering/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `profile-01-config-layering` from the ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/profile-01-config-layering/proposal.md b/openspec/changes/profile-01-config-layering/proposal.md index e1d88b94..435acd41 100644 --- a/openspec/changes/profile-01-config-layering/proposal.md +++ b/openspec/changes/profile-01-config-layering/proposal.md @@ -2,9 +2,6 @@ ## Why - - - SpecFact treats every user the same β€” a solo developer and an enterprise architecture board get identical defaults, enforcement levels, and module activation. This blocks adoption at both ends: solos drown in ceremony they don't need, enterprises can't enforce baselines across hundreds of repos. A profile-driven initialization that deterministically selects modules, templates, policies, and enforcement modes makes SpecFact scale from single-developer projects to regulated enterprise environments without configuration sprawl. ## Ownership Alignment (2026-04-08) @@ -39,6 +36,7 @@ modules/profile/ ``` **`module-package.yaml` declares:** + - `name: profile` - `version: 0.1.0` - `commands: [profile init, profile show, profile diff]` @@ -70,6 +68,7 @@ modules/profile/ ``` **`module-package.yaml` declares:** + - `name: profile` - `version: 0.1.0` - `commands: [profile init, profile show, profile diff]` @@ -78,9 +77,6 @@ modules/profile/ ## What Changes - - - - **NEW**: Profile module in `modules/profile/` with config layering engine: profile defaults β†’ org baseline (read-only) β†’ repo overlay β†’ developer local. Highest priority last. - **NEW**: Four built-in profiles shipped as YAML (`solo.yaml`, `startup.yaml`, `mid_size.yaml`, `enterprise.yaml`) defining: enabled modules, policy enforcement mode, required requirements fields, config sources, and enforcement location. - **EXTEND**: Tier profiles also define the default clean-code pack mode inherited by `specfact/clean-code-principles`: `solo -> advisory`, `startup -> advisory then mixed`, `mid_size -> mixed`, `enterprise -> hard`. @@ -101,6 +97,7 @@ modules/profile/ | Enforcement location | local warnings only | local + CI advisory | CI mixed (some hard) | CI hard-fail + evidence | ## Capabilities + ### New Capabilities - `profile-config-layering`: Profile-driven config resolution with deterministic layering (profile defaults β†’ org baseline β†’ repo overlay β†’ dev local), divergence detection, and tier-aware module/policy activation. @@ -110,7 +107,6 @@ modules/profile/ - `init-module-state`: Extended with `--profile` flag for tier-aware initialization; default behavior preserved as implicit `solo` profile. - --- ## Source Tracking diff --git a/openspec/changes/profile-01-config-layering/specs/init-module-state/spec.md b/openspec/changes/profile-01-config-layering/specs/init-module-state/spec.md index 4fc5cb39..fe4e37eb 100644 --- a/openspec/changes/profile-01-config-layering/specs/init-module-state/spec.md +++ b/openspec/changes/profile-01-config-layering/specs/init-module-state/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Init Module State + The system SHALL initialize module state according to the active profile selected during `specfact init`. #### Scenario: Profile activates expected module set + - **GIVEN** `specfact init --profile startup` - **WHEN** init writes module state - **THEN** enabled modules include sync and ceremony capabilities for startup - **AND** modules outside the startup profile default set remain disabled unless explicitly enabled. #### Scenario: Backward compatible default behavior + - **GIVEN** `specfact init` is executed without `--profile` - **WHEN** module state is generated - **THEN** behavior matches current default compatibility profile diff --git a/openspec/changes/profile-01-config-layering/specs/profile-config-layering/spec.md b/openspec/changes/profile-01-config-layering/specs/profile-config-layering/spec.md index 94f9e99d..89c11d18 100644 --- a/openspec/changes/profile-01-config-layering/specs/profile-config-layering/spec.md +++ b/openspec/changes/profile-01-config-layering/specs/profile-config-layering/spec.md @@ -1,27 +1,32 @@ ## ADDED Requirements ### Requirement: Profile Config Layering + The system SHALL resolve configuration using deterministic layer precedence. #### Scenario: Layer precedence is deterministic + - **GIVEN** values are present in profile defaults, org baseline, repo overlay, and developer-local override - **WHEN** configuration is resolved - **THEN** precedence is `profile defaults < org baseline < repo overlay < developer local` - **AND** the resolved output records the winning source for each key. #### Scenario: Profile-specific defaults are applied + - **GIVEN** `specfact init --profile enterprise` - **WHEN** profile config is generated - **THEN** policy mode defaults to enterprise-grade enforcement - **AND** requirements schema includes enterprise-required fields. #### Scenario: Clean-code defaults are inherited from the selected tier + - **GIVEN** `specfact init --profile startup` - **WHEN** profile config is generated - **THEN** the clean-code policy pack defaults to advisory mode with gradual promotion to mixed - **AND** no separate clean-code profile selector is required in the resolved config #### Scenario: Invalid profile is rejected + - **GIVEN** `specfact init --profile unknown` - **WHEN** command validation runs - **THEN** the command exits with a validation error diff --git a/openspec/changes/profile-02-central-config-sources/design.md b/openspec/changes/profile-02-central-config-sources/design.md index a712fb84..5af958dc 100644 --- a/openspec/changes/profile-02-central-config-sources/design.md +++ b/openspec/changes/profile-02-central-config-sources/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `profile-02-central-config-sources` fr ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/profile-02-central-config-sources/proposal.md b/openspec/changes/profile-02-central-config-sources/proposal.md index 120c0205..263f38bd 100644 --- a/openspec/changes/profile-02-central-config-sources/proposal.md +++ b/openspec/changes/profile-02-central-config-sources/proposal.md @@ -2,17 +2,12 @@ ## Why - - - Mid-size and enterprise teams need centralized configuration baselines that individual repos inherit without copy-pasting. Today every repo has its own `.specfact/config.yaml`, leading to configuration drift across hundreds of repos. A read-only central config source that repos pull from β€” with local overlays and divergence warnings β€” gives organizations consistent governance while letting individual teams adapt to their specific needs. ## What Changes - - - - **NEW**: Config source declaration in `.specfact/profile.yaml`: + ```yaml config_sources: - git+ssh://github.com/myorg/engineering-standards/.specfact # Read-only baseline @@ -20,6 +15,7 @@ Mid-size and enterprise teams need centralized configuration baselines that indi overlay: policy_mode: mixed # Local override (highest priority) ``` + - **NEW**: `specfact profile pull` β€” fetch and cache central config sources locally (`.specfact/cache/config-sources/`) - **NEW**: `specfact profile diff` (extended) β€” compare local resolved config against central baseline, showing divergence with source annotations - **NEW**: Config resolution order: profile defaults β†’ central baselines (in order) β†’ local overlay. Central baselines are **read-only** β€” local changes cannot modify them, only override via overlay. @@ -29,6 +25,7 @@ Mid-size and enterprise teams need centralized configuration baselines that indi - **EXTEND**: Profile module (profile-01) extended with central config resolution and pull commands ## Capabilities + ### New Capabilities - `central-config-sources`: Read-only central configuration baselines with git-based source URIs, local overlay support, divergence detection, staleness warnings, and cached resolution. Organizations define baselines once, repos inherit and optionally override. @@ -37,7 +34,6 @@ Mid-size and enterprise teams need centralized configuration baselines that indi - `profile-config-layering`: Extended with central config source resolution in the layering order (profile defaults β†’ central baselines β†’ local overlay) - --- ## Source Tracking diff --git a/openspec/changes/profile-02-central-config-sources/specs/central-config-sources/spec.md b/openspec/changes/profile-02-central-config-sources/specs/central-config-sources/spec.md index cabcafc7..75ed3639 100644 --- a/openspec/changes/profile-02-central-config-sources/specs/central-config-sources/spec.md +++ b/openspec/changes/profile-02-central-config-sources/specs/central-config-sources/spec.md @@ -1,15 +1,18 @@ ## ADDED Requirements ### Requirement: Central Config Sources + The system SHALL support read-only central configuration sources with local overlays. #### Scenario: Pull read-only baseline from central source + - **GIVEN** `.specfact/profile.yaml` defines a central git config source - **WHEN** profile resolution runs - **THEN** baseline values are loaded from that source - **AND** baseline files are treated as read-only inputs. #### Scenario: Local overlay overrides baseline + - **GIVEN** baseline sets `policy_mode: advisory` - **AND** repo overlay sets `policy_mode: mixed` - **WHEN** configuration is resolved @@ -17,6 +20,7 @@ The system SHALL support read-only central configuration sources with local over - **AND** output includes baseline-versus-overlay provenance. #### Scenario: Divergence warning is emitted + - **GIVEN** local values diverge from central baseline policy keys - **WHEN** config check runs - **THEN** the system emits a divergence warning diff --git a/openspec/changes/profile-02-central-config-sources/specs/profile-config-layering/spec.md b/openspec/changes/profile-02-central-config-sources/specs/profile-config-layering/spec.md index 16f16bce..e05fb3f1 100644 --- a/openspec/changes/profile-02-central-config-sources/specs/profile-config-layering/spec.md +++ b/openspec/changes/profile-02-central-config-sources/specs/profile-config-layering/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Profile Config Layering + The system SHALL incorporate central config sources into profile layering without breaking existing local-only workflows. #### Scenario: Local-only repositories remain valid + - **GIVEN** no central source is configured - **WHEN** profile layering resolves config - **THEN** resolution still works with profile, repo, and local layers - **AND** no network dependency is required. #### Scenario: Source attribution includes central baseline + - **GIVEN** central baseline is configured - **WHEN** resolved config is inspected - **THEN** keys sourced from baseline are marked as central diff --git a/openspec/changes/profile-03-domain-overlays/design.md b/openspec/changes/profile-03-domain-overlays/design.md index 488e01f1..dce913c2 100644 --- a/openspec/changes/profile-03-domain-overlays/design.md +++ b/openspec/changes/profile-03-domain-overlays/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `profile-03-domain-overlays` from the ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/profile-03-domain-overlays/proposal.md b/openspec/changes/profile-03-domain-overlays/proposal.md index 2e964839..662ca02e 100644 --- a/openspec/changes/profile-03-domain-overlays/proposal.md +++ b/openspec/changes/profile-03-domain-overlays/proposal.md @@ -2,17 +2,12 @@ ## Why - - - Different business units within an enterprise have different requirements β€” the payments team needs regulatory references and risk owners on every requirement, the platform team doesn't. A single enterprise profile can't cover this variance without becoming unwieldy. Domain-specific overlays that extend the base profile with additional required fields, constraints, and policies let organizations enforce domain-specific governance without forking the entire profile system. ## What Changes - - - - **NEW**: Domain overlay definitions at `.specfact/profiles/{domain}.yaml`: + ```yaml inherit: enterprise # Base profile to extend requirements_schema: @@ -26,6 +21,7 @@ Different business units within an enterprise have different requirements β€” th policy_overrides: require-data-classification: hard # Override from mixed to hard for this domain ``` + - **NEW**: `specfact profile overlays list` β€” show available domain overlays - **NEW**: `specfact profile overlays apply ` β€” apply a domain overlay to the current repo - **NEW**: Profile-aware requirements validation β€” when a domain overlay defines additional required fields, `specfact requirements validate` checks those fields @@ -35,6 +31,7 @@ Different business units within an enterprise have different requirements β€” th - **EXTEND**: Profile resolution order becomes: profile defaults β†’ central baselines β†’ domain overlay β†’ local overlay ## Capabilities + ### New Capabilities - `domain-overlays`: Domain-specific profile overlays that extend base profiles with additional required fields, architectural constraints, and policy overrides. Distributed via central config sources or marketplace. @@ -44,7 +41,6 @@ Different business units within an enterprise have different requirements β€” th - `profile-config-layering`: Extended with domain overlay in resolution order - `requirements-data-model`: Requirements validation respects domain-specific required fields - --- ## Source Tracking diff --git a/openspec/changes/profile-03-domain-overlays/specs/domain-overlays/spec.md b/openspec/changes/profile-03-domain-overlays/specs/domain-overlays/spec.md index e884a681..74fa2480 100644 --- a/openspec/changes/profile-03-domain-overlays/specs/domain-overlays/spec.md +++ b/openspec/changes/profile-03-domain-overlays/specs/domain-overlays/spec.md @@ -1,15 +1,18 @@ ## ADDED Requirements ### Requirement: Domain Overlays + The system SHALL allow domain-specific overlays to extend profile requirements and policy constraints. #### Scenario: Domain overlay adds required requirement fields + - **GIVEN** an enterprise profile with `payments` overlay - **WHEN** requirements schema is resolved - **THEN** overlay-required fields include `regulatory_reference` and `risk_owner` - **AND** requirement validation fails when those fields are missing. #### Scenario: Domain overlay adds architectural constraints + - **GIVEN** overlay defines mandatory shared payment gateway usage - **WHEN** architecture validation runs - **THEN** solutions missing that integration are flagged diff --git a/openspec/changes/profile-03-domain-overlays/specs/profile-config-layering/spec.md b/openspec/changes/profile-03-domain-overlays/specs/profile-config-layering/spec.md index 18e9616c..e15f6efc 100644 --- a/openspec/changes/profile-03-domain-overlays/specs/profile-config-layering/spec.md +++ b/openspec/changes/profile-03-domain-overlays/specs/profile-config-layering/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Profile Config Layering + The system SHALL merge domain overlays after central profile resolution and before developer-local overrides. #### Scenario: Overlay precedence is enforced + - **GIVEN** base enterprise profile and domain overlay define conflicting requirement schema rules - **WHEN** profile resolution executes - **THEN** overlay rules win over base profile rules - **AND** developer-local overrides can still adjust non-locked keys. #### Scenario: Unknown overlay is rejected + - **GIVEN** requested overlay name is not available - **WHEN** initialization runs - **THEN** command fails with clear overlay-not-found diagnostics diff --git a/openspec/changes/profile-03-domain-overlays/specs/requirements-data-model/spec.md b/openspec/changes/profile-03-domain-overlays/specs/requirements-data-model/spec.md index af690086..99ab66e2 100644 --- a/openspec/changes/profile-03-domain-overlays/specs/requirements-data-model/spec.md +++ b/openspec/changes/profile-03-domain-overlays/specs/requirements-data-model/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Requirements Data Model + The system SHALL support schema-driven required fields introduced by domain overlays. #### Scenario: Overlay-required fields enforced at model validation + - **GIVEN** overlay declares additional required fields - **WHEN** a requirement document is parsed - **THEN** missing overlay-required fields cause validation errors - **AND** error output identifies the overlay and missing fields. #### Scenario: Base schema compatibility preserved + - **GIVEN** no domain overlay is active - **WHEN** requirement documents are validated - **THEN** base requirements schema behavior remains unchanged. diff --git a/openspec/changes/profile-04-safe-project-artifact-writes/.openspec.yaml b/openspec/changes/profile-04-safe-project-artifact-writes/.openspec.yaml new file mode 100644 index 00000000..98d7681c --- /dev/null +++ b/openspec/changes/profile-04-safe-project-artifact-writes/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-04-09 diff --git a/openspec/changes/profile-04-safe-project-artifact-writes/CHANGE_VALIDATION.md b/openspec/changes/profile-04-safe-project-artifact-writes/CHANGE_VALIDATION.md new file mode 100644 index 00000000..8f19cf91 --- /dev/null +++ b/openspec/changes/profile-04-safe-project-artifact-writes/CHANGE_VALIDATION.md @@ -0,0 +1,17 @@ +# CHANGE VALIDATION + +- **Change**: `profile-04-safe-project-artifact-writes` +- **Date**: 2026-04-09 +- **Method**: `openspec validate profile-04-safe-project-artifact-writes --strict` +- **Result**: PASS + +## Notes + +- Proposal, design, specs, and tasks are present and parse successfully. +- The change is intentionally scoped as the core policy/contract side of a paired cross-repo safety effort. +- GitHub tracking is synced to issue [#490][issue-490] under parent feature [#365][issue-365], with bug linkage to + [#487][issue-487]. + +[issue-365]: https://github.com/nold-ai/specfact-cli/issues/365 +[issue-487]: https://github.com/nold-ai/specfact-cli/issues/487 +[issue-490]: https://github.com/nold-ai/specfact-cli/issues/490 diff --git a/openspec/changes/profile-04-safe-project-artifact-writes/design.md b/openspec/changes/profile-04-safe-project-artifact-writes/design.md new file mode 100644 index 00000000..6f0575cb --- /dev/null +++ b/openspec/changes/profile-04-safe-project-artifact-writes/design.md @@ -0,0 +1,138 @@ +## Context + +Core init/setup flows currently decide write behavior locally inside command helpers such as `ide_setup.py` and `modules/init/src/commands.py`. That makes file mutation semantics inconsistent: some paths skip existing files, some overwrite, and some merge only part of the payload without an explicit ownership model. Issue `#487` exposed the most visible failure mode: `.vscode/settings.json` is a user-owned config file that SpecFact touches for one narrow purpose, but the current workflow can still wipe unrelated settings if the write path degrades from merge to replacement. + +This is a cross-cutting change because the same trust boundary exists for any local artifact under a user repository. The design therefore needs a reusable policy, not a one-off patch in `create_vscode_settings()`. + +## Goals / Non-Goals + +**Goals:** + +- Define a single core contract for user-project artifact writes. +- Separate artifact ownership from mutation mechanics so commands can declare what they own. +- Preserve unrelated user configuration by default for mergeable structured files. +- Make destructive replacement explicit, recoverable, and auditable. +- Add CI/static enforcement so future init/setup work cannot reintroduce raw overwrite behavior. + +**Non-Goals:** + +- Rebuild every existing local-write path in one change across both repos. +- Introduce interactive patch review for every init command in this first slice. +- Support arbitrary semantic merges for all file formats; unsupported formats can fail-safe or use create-only/explicit-replace behavior. + +## Decisions + +### 1. Introduce a core `safe_project_write` layer with declared write modes + +Core will add a shared helper that accepts: + +- target path +- artifact owner id +- write mode (`create_only`, `merge_structured`, `append_managed_block`, `explicit_replace`) +- managed keys or managed block selectors +- backup/recovery policy + +Rationale: + +- Command code should describe intent, not implement bespoke overwrite logic. +- A central helper is the only realistic way to enforce policy in CI. + +Alternatives considered: + +- Fix only `.vscode/settings.json` merge logic. Rejected because the same failure pattern would persist elsewhere. +- Rely on `--force` flags alone. Rejected because the unsafe default remains. + +### 2. Treat project artifacts as partially owned unless SpecFact is authoritative for the full file + +The helper will require ownership classification: + +- full-file ownership: SpecFact may replace with backup/explicit confirmation semantics +- partial ownership: SpecFact may modify only declared keys/sections/blocks +- unowned: command must fail unless it is create-only + +For `.vscode/settings.json`, SpecFact owns only its prompt recommendation entries, not the document. + +Rationale: + +- Ownership is the boundary between safe reconciliation and unacceptable overwrite. +- This generalizes to YAML/JSON/TOML configs and managed markdown blocks. + +Alternatives considered: + +- Infer ownership heuristically from file path. Rejected because path-based assumptions are fragile and opaque. + +### 3. Structured-file reconciliation will preserve unrelated user data and only rewrite managed sections + +For JSON settings files, the merge logic will: + +- parse existing content +- preserve all non-managed keys +- remove/refresh only prior SpecFact-managed entries +- write back normalized JSON + +If parsing fails, the default behavior will be fail-safe with guidance, not empty-file replacement. Explicit replace may still exist behind force-style intent plus backup. + +Rationale: + +- The bug exists because full-document replacement was allowed for a partial-ownership file. + +Alternatives considered: + +- Best-effort fallback to `{}` on parse error. Rejected because that recreates silent data loss. + +### 4. Backups and recovery metadata are mandatory for lossy operations + +Any `explicit_replace` or fallback-recovery path will create a timestamped backup under a SpecFact-managed recovery location and emit actionable output naming: + +- original path +- backup path +- reason replacement was required + +Rationale: + +- Even explicit destructive actions need a reversible path. + +### 5. Add a CI gate for unsafe user-project writes + +The repo will add a gate with two signals: + +- static scan/rule: block direct writes to likely user-project artifacts from init/setup flows unless routed through the safe-write helper +- regression tests: fixture repositories with existing user config verifying no unrelated keys are lost + +**Initial protected-path set (first gate):** start enforcement with `.vscode/settings.json`, `.github/copilot-instructions.md`, +all files matching `.cursor/rules/*.mdc`, and SpecFact-owned `pyproject.toml` sections (tool tables this project controls). +Adjust the allowlist as the helper and CI rule mature. + +Rationale: + +- Policy without enforcement will drift. +- Fixture tests catch behavior regressions the static rule cannot prove. + +Alternatives considered: + +- Tests only. Rejected because new raw-write code paths could land without touching existing fixtures. +- Static rule only. Rejected because safe helper misuse still needs behavioral coverage. + +## Risks / Trade-offs + +- `[Risk]` Initial scope may not cover every write path in one pass. β†’ Mitigation: enforce the policy first for core init/setup and pair it with a modules-runtime adoption change. +- `[Risk]` Structured merge rules may become format-specific and verbose. β†’ Mitigation: support only a narrow set of sanctioned merge strategies and fail-safe otherwise. +- `[Risk]` Static detection may produce false positives on safe writes outside init/setup. β†’ Mitigation: scope the first gate to user-project artifact paths and allow helper-based exemptions only. +- `[Risk]` Backup files can clutter repos if stored locally. β†’ Mitigation: store recovery artifacts in a dedicated SpecFact-managed location outside normal source files and document cleanup. + +## Migration Plan + +1. Add the core safe-write abstraction and ownership model. +2. Move `init ide` settings mutation onto the helper and cover the `#487` regression with fixtures. +3. Route other core init/setup artifact writes through the helper where applicable. +4. Add the CI/static gate and regression fixtures. +5. Land the paired modules-runtime adoption change so bundle commands use the same contract. + +Rollback strategy: + +- If helper rollout causes unexpected breakage, commands can temporarily fail-safe (skip with warning) rather than performing legacy overwrite behavior. + +## Open Questions + +- Which existing user-project artifact paths should be in the first β€œprotected path” CI rule set beyond `.vscode/settings.json`? +- Should explicit destructive replacement remain non-interactive in CI only via `--force`, or require an additional machine-readable confirmation flag? diff --git a/openspec/changes/profile-04-safe-project-artifact-writes/proposal.md b/openspec/changes/profile-04-safe-project-artifact-writes/proposal.md new file mode 100644 index 00000000..73d45759 --- /dev/null +++ b/openspec/changes/profile-04-safe-project-artifact-writes/proposal.md @@ -0,0 +1,44 @@ +# Change: Safe Project Artifact Writes For Init And IDE Setup + +## Why + +`specfact init` and `specfact init ide` currently mutate user-owned project artifacts such as `.vscode/settings.json` without a first-class safety contract. Issue [#487](https://github.com/nold-ai/specfact-cli/issues/487) showed that a single setup run can destroy unrelated local configuration, forcing manual git restore and hand repair; that failure mode is unacceptable for any tool that writes into customer repositories. + +## What Changes + +- **NEW**: Introduce a core safe-write policy for project artifacts that classifies targets as create-only, mergeable, append-only, or replace-only-with-explicit-approval. +- **NEW**: Add a structured write planning flow for init/setup commands that records whether an operation will create, merge, skip, back up, or fail before touching an existing user file. +- **NEW**: Require backup and recovery metadata for destructive or lossy local mutations initiated by core setup flows. +- **NEW**: Add conflict handling rules for structured files such as `.vscode/settings.json` so SpecFact-managed keys are merged into existing documents instead of replacing the whole artifact. +- **EXTEND**: `specfact init ide` to preserve non-SpecFact settings, strip only prior SpecFact-managed prompt recommendations when needed, and fail safely on malformed settings files unless the user explicitly chooses a replacement path. +- **EXTEND**: `specfact init` and related bootstrap helpers to route project-file writes through the same safe-write contract instead of ad hoc `write_text` or overwrite behavior. +- **EXTEND**: Documentation for init/setup commands with explicit guarantees about preservation, backup behavior, and how users can preview or force replacements when necessary. + +## Capabilities + +### New Capabilities + +- `project-artifact-write-safety`: Policy, planning, and recovery rules for any core command that creates or mutates user-project artifacts. + +### Modified Capabilities + +- `init-ide-prompt-source-selection`: `specfact init ide` must reconcile prompt recommendations with existing IDE settings without deleting unrelated user configuration. +- `module-owned-ide-prompts`: Core setup flows that materialize bundle-owned IDE assets must use the safe-write policy when touching user-project files. + +## Impact + +- Affected code: `src/specfact_cli/utils/ide_setup.py`, `src/specfact_cli/modules/init/src/commands.py`, and any shared core helpers introduced for safe project-file mutations. +- Affected docs: `README.md`, `docs/getting-started/installation.md`, `docs/getting-started/quickstart.md`, and core CLI/init reference pages. +- Integration points: installed bundle prompt exports from `specfact-cli-modules`; paired runtime adoption change required in `nold-ai/specfact-cli-modules` so bundle commands follow the same guarantees. +- Dependencies: linked bug [#487](https://github.com/nold-ai/specfact-cli/issues/487); sync under parent feature [#365](https://github.com/nold-ai/specfact-cli/issues/365) Configuration Profiles. + +## Source Tracking + +- **GitHub Issue**: #490 +- **Issue URL**: +- **Repository**: nold-ai/specfact-cli +- **Last Synced Status**: open +- **Parent Feature**: #365 +- **Parent Feature URL**: +- **Related Bug**: #487 +- **Related Bug URL**: diff --git a/openspec/changes/profile-04-safe-project-artifact-writes/specs/init-ide-prompt-source-selection/spec.md b/openspec/changes/profile-04-safe-project-artifact-writes/specs/init-ide-prompt-source-selection/spec.md new file mode 100644 index 00000000..2aa851a0 --- /dev/null +++ b/openspec/changes/profile-04-safe-project-artifact-writes/specs/init-ide-prompt-source-selection/spec.md @@ -0,0 +1,28 @@ +## ADDED Requirements + +### Requirement: Init IDE SHALL preserve unrelated VS Code settings + +`specfact init ide` SHALL reconcile prompt recommendations into `.vscode/settings.json` without deleting unrelated user-managed settings. + +#### Scenario: Existing non-SpecFact settings survive prompt export + +- **WHEN** a repository already contains `.vscode/settings.json` with Python, test, or formatter settings +- **AND** the user runs `specfact init ide` +- **THEN** the command SHALL preserve those unrelated settings +- **AND** SHALL update only SpecFact-managed prompt recommendation entries + +#### Scenario: Selective prompt export removes only prior SpecFact-managed recommendations + +- **WHEN** the user runs `specfact init ide --prompts ` +- **THEN** prior SpecFact-managed prompt recommendations outside the selected subset MAY be removed +- **AND** unrelated `.github/prompts/` recommendations and non-SpecFact settings SHALL remain unchanged + +### Requirement: Init IDE SHALL fail safe on malformed settings documents + +`specfact init ide` SHALL not replace malformed or unparsable VS Code settings with an empty or generated document by default. + +#### Scenario: Malformed settings file blocks destructive rewrite + +- **WHEN** `.vscode/settings.json` exists but cannot be parsed as JSON +- **THEN** `specfact init ide` SHALL stop with an actionable error +- **AND** SHALL leave the existing file unchanged unless explicit replacement is requested through the safe-write policy diff --git a/openspec/changes/profile-04-safe-project-artifact-writes/specs/module-owned-ide-prompts/spec.md b/openspec/changes/profile-04-safe-project-artifact-writes/specs/module-owned-ide-prompts/spec.md new file mode 100644 index 00000000..1e7d5a3a --- /dev/null +++ b/openspec/changes/profile-04-safe-project-artifact-writes/specs/module-owned-ide-prompts/spec.md @@ -0,0 +1,17 @@ +## ADDED Requirements + +### Requirement: Core materialization of module-owned IDE assets SHALL use safe project writes + +When core setup flows materialize module-owned IDE assets into a user repository, they SHALL route all local file mutations through the core safe-write policy. + +#### Scenario: Module-owned prompt export uses safe-write helper for settings mutation + +- **WHEN** `specfact init ide` exports bundle-owned prompt files and updates a related IDE config artifact +- **THEN** the config mutation SHALL use the safe-write helper with declared ownership metadata +- **AND** the command SHALL preserve unrelated user-managed content in the target artifact + +#### Scenario: Module-owned template copy does not silently replace existing user customization + +- **WHEN** a core setup flow copies a module-owned template asset into a target path that already exists in the user repository +- **THEN** the flow SHALL skip, merge, or require explicit replacement according to the declared safe-write mode +- **AND** SHALL NOT silently overwrite the existing file diff --git a/openspec/changes/profile-04-safe-project-artifact-writes/specs/project-artifact-write-safety/spec.md b/openspec/changes/profile-04-safe-project-artifact-writes/specs/project-artifact-write-safety/spec.md new file mode 100644 index 00000000..145ae517 --- /dev/null +++ b/openspec/changes/profile-04-safe-project-artifact-writes/specs/project-artifact-write-safety/spec.md @@ -0,0 +1,49 @@ +## ADDED Requirements + +### Requirement: Core commands SHALL classify project artifact writes by ownership and mutation mode + +The system SHALL require core init/setup flows to declare whether a target artifact is create-only, mergeable, append-managed, or explicit-replace before writing into a user repository. + +#### Scenario: Partial-ownership artifact cannot use implicit full replacement + +- **WHEN** a core command targets a user-project artifact that SpecFact owns only partially +- **THEN** the command SHALL use a partial-ownership write mode such as structured merge or managed-block append +- **AND** SHALL NOT replace the full file implicitly + +#### Scenario: Unowned existing artifact fails safe + +- **WHEN** a core command would modify an existing artifact with no declared SpecFact-owned section or full-file ownership +- **THEN** the command SHALL stop with an actionable conflict message +- **AND** SHALL NOT mutate the artifact unless an explicit replacement mode is requested + +### Requirement: Lossy project artifact mutations SHALL create recovery material + +The system SHALL create backup and recovery metadata for any lossy local artifact mutation initiated by a core command. + +#### Scenario: Explicit replacement emits backup path + +- **WHEN** a core command performs an explicit replace of an existing project artifact +- **THEN** a backup copy SHALL be created in a SpecFact-managed recovery location before replacement +- **AND** the command output SHALL identify the backup path and original target + +#### Scenario: Failed structured merge leaves original file untouched + +- **WHEN** structured reconciliation cannot be completed safely +- **THEN** the original project artifact SHALL remain unchanged +- **AND** the command SHALL report why reconciliation failed and how to proceed safely + +### Requirement: CI SHALL detect unsafe core writes to user-project artifacts + +The repository SHALL enforce a CI or quality gate that flags unsafe write paths for user-project artifacts touched by core init/setup flows. + +#### Scenario: Raw overwrite path is rejected in CI + +- **WHEN** a core init/setup code path writes a protected user-project artifact without using the sanctioned safe-write helper +- **THEN** the quality gate SHALL fail +- **AND** the failure output SHALL identify the offending path or call site + +#### Scenario: Regression fixture preserves unrelated user configuration + +- **WHEN** CI runs regression fixtures for existing user-owned project configs +- **THEN** init/setup commands SHALL preserve unrelated user-managed content +- **AND** only declared SpecFact-managed sections or keys may change diff --git a/openspec/changes/profile-04-safe-project-artifact-writes/tasks.md b/openspec/changes/profile-04-safe-project-artifact-writes/tasks.md new file mode 100644 index 00000000..9abbd77c --- /dev/null +++ b/openspec/changes/profile-04-safe-project-artifact-writes/tasks.md @@ -0,0 +1,35 @@ +## 1. Branch, coordination, and issue sync + +- [ ] 1.1 Create `bugfix/profile-04-safe-project-artifact-writes` in a dedicated worktree from `origin/dev` and bootstrap Hatch in that worktree. +- [ ] 1.2 Sync the change proposal to GitHub under parent feature `#365`, link bug `#487`, and update `proposal.md` Source Tracking with issue metadata. +- [ ] 1.3 Confirm the paired modules-side change `project-runtime-01-safe-artifact-write-policy` is available and note the dependency in both PR descriptions/change evidence. + +## 2. Specs, regression fixtures, and failing evidence + +- [ ] 2.1 Add or update regression fixtures for existing user-owned project artifacts such as `.vscode/settings.json` with unrelated custom settings. +- [ ] 2.2 Write tests from the new scenarios covering partial ownership, malformed settings fail-safe behavior, backup creation, and preservation of unrelated settings. +- [ ] 2.3 Write tests for the CI/static unsafe-write gate so direct writes to protected project artifacts are rejected unless routed through the sanctioned helper. +- [ ] 2.4 Run the targeted tests before implementation, capture the failing results, and record commands/timestamps in `openspec/changes/profile-04-safe-project-artifact-writes/TDD_EVIDENCE.md`. + +## 3. Core safe-write implementation + +- [ ] 3.1 Implement the core safe-write helper and ownership model with `@beartype` and `@icontract` on public APIs. +- [ ] 3.2 Route `src/specfact_cli/utils/ide_setup.py` settings mutation through the helper so `.vscode/settings.json` preserves unrelated user-managed settings and strips only SpecFact-managed entries when needed. +- [ ] 3.3 Route applicable init/setup artifact copy flows through the helper or explicit safe modes, including fail-safe handling for malformed structured files and backup creation for explicit replacement. +- [ ] 3.4 Implement the CI/static guard for protected user-project artifacts in init/setup code paths and integrate it into the relevant local/CI quality workflow. + +## 4. Verification, docs, and cross-repo handoff + +- [ ] 4.1 Re-run the targeted tests and any broader init/setup regression coverage, capture passing results, and update `TDD_EVIDENCE.md`. +- [ ] 4.2 Research and update affected docs (`README.md`, installation/quickstart/init references) to document preservation guarantees, backup behavior, and explicit replacement semantics. +- [ ] 4.3 Run quality gates: `hatch run format`, `hatch run type-check`, `hatch run lint`, `hatch run yaml-lint`, `hatch run contract-test`, and `hatch run smart-test`. +- [ ] 4.4 Run `hatch run ./scripts/verify-modules-signature.py --require-signature`; if any bundled module manifests changed, bump versions, re-sign as required, and re-run verification. +- [ ] 4.5 Ensure `.specfact/code-review.json` is fresh, remediate all findings, and record the final review command/timestamp in `TDD_EVIDENCE.md` or PR notes. +- [ ] 4.6 Apply the appropriate version/changelog update for a bugfix release if implementation changes user-facing behavior, then open a PR to `dev` referencing the paired modules change. + +## 5. Worktree cleanup + +- [ ] 5.1 Remove the worktree used for this change (for example `git worktree remove ../specfact-cli-worktrees/bugfix/profile-04-safe-project-artifact-writes`). +- [ ] 5.2 Delete the local branch after merge (`git branch -d bugfix/profile-04-safe-project-artifact-writes`). +- [ ] 5.3 Prune stale worktree metadata (`git worktree prune`). +- [ ] 5.4 Record cleanup completion in `TDD_EVIDENCE.md` alongside the 4.x verification notes. diff --git a/openspec/changes/requirements-01-data-model/design.md b/openspec/changes/requirements-01-data-model/design.md index 497373d5..e7e36dec 100644 --- a/openspec/changes/requirements-01-data-model/design.md +++ b/openspec/changes/requirements-01-data-model/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `requirements-01-data-model` from the ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/requirements-01-data-model/proposal.md b/openspec/changes/requirements-01-data-model/proposal.md index f3caa0db..c2c66ac9 100644 --- a/openspec/changes/requirements-01-data-model/proposal.md +++ b/openspec/changes/requirements-01-data-model/proposal.md @@ -2,16 +2,10 @@ ## Why - - - Business requirements have no formal representation in SpecFact. They exist only as unstructured text in backlog items (acceptance criteria, descriptions). This means the most impactful validation gap β€” "are we building the right thing?" β€” cannot be programmatically checked. A Pydantic domain model for business requirements with versioned YAML storage under `.specfact/requirements/` gives requirements first-class status in the traceability chain, enabling all downstream validation (Req β†’ Arch β†’ Spec β†’ Code β†’ Tests). ## What Changes - - - - **NEW**: Pydantic domain models in `src/specfact_cli/models/requirements.py`: - `BusinessOutcome` β€” success metrics and quantified business value - `BusinessRule` β€” rule ID, name, Given/When/Then scenario, MoSCoW priority @@ -24,6 +18,7 @@ Business requirements have no formal representation in SpecFact. They exist only - **EXTEND**: `ProjectBundle` extended with optional `requirements` field via arch-07 schema extension system (namespace: `requirements.business_requirements`) ## Capabilities + ### New Capabilities - `requirements-data-model`: Pydantic domain models for business requirements (BusinessOutcome, BusinessRule, ArchitecturalConstraint, BusinessRequirement, RequirementTrace) with versioned YAML storage and profile-aware field validation. @@ -32,7 +27,6 @@ Business requirements have no formal representation in SpecFact. They exist only - `data-models`: ProjectBundle extended with requirements field via arch-07 schema extensions - --- ## Source Tracking diff --git a/openspec/changes/requirements-01-data-model/specs/data-models/spec.md b/openspec/changes/requirements-01-data-model/specs/data-models/spec.md index 450e5da4..44f78b09 100644 --- a/openspec/changes/requirements-01-data-model/specs/data-models/spec.md +++ b/openspec/changes/requirements-01-data-model/specs/data-models/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Data Models + The system SHALL extend project models to include requirements payloads with schema versioning. #### Scenario: Project bundle accepts requirements namespace + - **GIVEN** a project bundle with requirements entries - **WHEN** model validation runs - **THEN** the requirements namespace is accepted - **AND** existing non-requirements fields remain backward compatible. #### Scenario: Schema version is required for requirements artifacts + - **GIVEN** a requirement document without `schema_version` - **WHEN** it is loaded - **THEN** validation fails diff --git a/openspec/changes/requirements-01-data-model/specs/requirements-data-model/intentspec-delta.md b/openspec/changes/requirements-01-data-model/specs/requirements-data-model/intentspec-delta.md index d0ed64e4..fc7f6d46 100644 --- a/openspec/changes/requirements-01-data-model/specs/requirements-data-model/intentspec-delta.md +++ b/openspec/changes/requirements-01-data-model/specs/requirements-data-model/intentspec-delta.md @@ -1,21 +1,25 @@ ## ADDED Requirements ### Requirement: IntentSpec Schema Compatibility + The system SHALL ensure `BusinessOutcome` and `BusinessRule` schemas are compatible with the IntentSpec.org JSON Schema standard (5 fields: Objective, User Goal, Outcomes, Edge Cases, Verification), so that IntentSpec-formatted intent documents can be imported without data loss. #### Scenario: BusinessOutcome maps to all 5 IntentSpec fields + - **GIVEN** an IntentSpec-formatted YAML document with fields: objective, user_goal, outcomes, edge_cases, verification - **WHEN** it is imported via `specfact requirements capture --format intentspec` - **THEN** the resulting `BusinessOutcome` record preserves all 5 IntentSpec fields in the stored artifact - **AND** the imported artifact validates against the `BusinessOutcome` Pydantic schema without errors #### Scenario: SQUER 7-question answers map to IntentSpec fields + - **GIVEN** a completed SQUER intent interview with 7 answers - **WHEN** the interview output is serialized to a `BusinessOutcome` artifact - **THEN** the serialization produces all 5 IntentSpec fields as a superset (SQUER answers cover objective, user_goal, outcomes, edge_cases, and verification) - **AND** no data is silently dropped from the SQUER answers during the mapping ### Requirement: Traceability Invariants + The system SHALL enforce three traceability invariants as preconditions on the publish gate for requirement artifacts: 1. **Traceability invariant**: Every shipped feature SHALL trace backward to at least one `BusinessOutcome` and forward through `BusinessRule` (G/W/T), `ArchitecturalConstraint`, specs, contracts, and tests. @@ -23,12 +27,14 @@ The system SHALL enforce three traceability invariants as preconditions on the p 3. **Intent schema conformance invariant**: `BusinessOutcome`, `BusinessRule`, and `ArchitecturalConstraint` documents SHALL validate against their canonical schemas before entering the pipeline. #### Scenario: Traceability invariant enforced on publish gate + - **GIVEN** a `BusinessOutcome` with no downstream spec reference - **WHEN** `specfact enforce stage --preset strict` runs the publish gate - **THEN** the gate blocks with a BLOCK verdict - **AND** the blocking reason identifies the orphaned `BusinessOutcome` ID and the missing spec link #### Scenario: Intent schema conformance checked before pipeline entry + - **GIVEN** a `BusinessRule` YAML file with a missing `given` field - **WHEN** `specfact requirements validate` is run - **THEN** the command exits non-zero diff --git a/openspec/changes/requirements-01-data-model/specs/requirements-data-model/spec.md b/openspec/changes/requirements-01-data-model/specs/requirements-data-model/spec.md index 4ea4c664..6c90d62c 100644 --- a/openspec/changes/requirements-01-data-model/specs/requirements-data-model/spec.md +++ b/openspec/changes/requirements-01-data-model/specs/requirements-data-model/spec.md @@ -1,15 +1,18 @@ ## ADDED Requirements ### Requirement: Requirements Data Model + The system SHALL define structured business requirement artifacts stored under `.specfact/requirements/`. #### Scenario: Requirement artifact captures business intent and constraints + - **GIVEN** a requirement file `.specfact/requirements/REQ-123.req.yaml` - **WHEN** it is validated - **THEN** it includes business outcome, business rules, and architectural constraints - **AND** each business rule has a stable rule identifier. #### Scenario: Trace references are represented explicitly + - **GIVEN** requirement trace metadata - **WHEN** artifacts are parsed - **THEN** architecture, spec, code, and test references are stored as explicit lists diff --git a/openspec/changes/requirements-02-module-commands/design.md b/openspec/changes/requirements-02-module-commands/design.md index f70d8055..5c0f2309 100644 --- a/openspec/changes/requirements-02-module-commands/design.md +++ b/openspec/changes/requirements-02-module-commands/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `requirements-02-module-commands` from ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/requirements-02-module-commands/proposal.md b/openspec/changes/requirements-02-module-commands/proposal.md index e809d6ec..4f84942a 100644 --- a/openspec/changes/requirements-02-module-commands/proposal.md +++ b/openspec/changes/requirements-02-module-commands/proposal.md @@ -2,9 +2,6 @@ ## Why - - - Even with a formal data model (requirements-01), there are no CLI commands for working with business requirements. Teams need to extract structured requirements from existing backlog items (reverse-engineer from AC text), author new requirements with profile-aware templates, and validate requirements completeness β€” all from the terminal. This module is the primary user-facing entry point for the upstream traceability chain. ## Ownership Alignment (2026-04-08) @@ -40,6 +37,7 @@ modules/requirements/ ``` **`module-package.yaml` declares:** + - `name: requirements` - `version: 0.1.0` - `commands: [requirements extract, requirements author, requirements validate, requirements list]` @@ -72,6 +70,7 @@ modules/requirements/ ``` **`module-package.yaml` declares:** + - `name: requirements` - `version: 0.1.0` - `commands: [requirements extract, requirements author, requirements validate, requirements list]` @@ -81,9 +80,6 @@ modules/requirements/ ## What Changes - - - - **NEW**: Requirements module in `modules/requirements/` implementing `ModuleIOContract`: - `import_to_bundle`: Extract requirements from backlog items into ProjectBundle - `export_from_bundle`: Generate requirements documents (YAML, Markdown) from bundle @@ -96,6 +92,7 @@ modules/requirements/ - **NEW**: Profile-aware templates: solo requires only As_a/I_want/So_that; startup adds Business_outcome + Business_rules; mid-size uses org-defined schema; enterprise adds Regulatory_reference + Risk_owner ## Capabilities + ### New Capabilities - `requirements-module`: CLI commands for extracting requirements from backlog items, authoring with profile-aware templates, validating completeness per profile schema, and listing with traceability coverage status. Implements ModuleIOContract for requirements lifecycle. @@ -105,7 +102,6 @@ modules/requirements/ - `module-io-contract`: New implementation of ModuleIOContract for the requirements domain (import from backlog, export to YAML/Markdown, sync, validate) - `backlog-adapter`: Extended with requirement extraction hooks β€” adapters provide raw AC text, extractor parses into structured BusinessRequirement models - --- ## Source Tracking diff --git a/openspec/changes/requirements-02-module-commands/specs/backlog-adapter/spec.md b/openspec/changes/requirements-02-module-commands/specs/backlog-adapter/spec.md index 9a401ffa..f08b2213 100644 --- a/openspec/changes/requirements-02-module-commands/specs/backlog-adapter/spec.md +++ b/openspec/changes/requirements-02-module-commands/specs/backlog-adapter/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Backlog Adapter + The system SHALL expose backlog acceptance-criteria content to requirements extraction workflows. #### Scenario: Adapter returns acceptance criteria payload for extraction + - **GIVEN** a backlog item selected for extraction - **WHEN** requirements extraction requests source fields - **THEN** adapter returns title, description, acceptance-criteria text, and item identity - **AND** extraction proceeds without provider-specific parsing in command handlers. #### Scenario: Missing acceptance criteria is surfaced explicitly + - **GIVEN** a backlog item with no acceptance criteria - **WHEN** extraction runs - **THEN** item is reported as incomplete input diff --git a/openspec/changes/requirements-02-module-commands/specs/module-io-contract/spec.md b/openspec/changes/requirements-02-module-commands/specs/module-io-contract/spec.md index 3a4e15ee..dbe03fb8 100644 --- a/openspec/changes/requirements-02-module-commands/specs/module-io-contract/spec.md +++ b/openspec/changes/requirements-02-module-commands/specs/module-io-contract/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Module Io Contract + The requirements module SHALL implement all `ModuleIOContract` operations. #### Scenario: Import operation maps backlog items to requirements + - **GIVEN** backlog input for import - **WHEN** `import_to_bundle` runs - **THEN** requirements are added to the bundle with stable IDs - **AND** parse diagnostics are included for partial failures. #### Scenario: Validate operation enforces profile schema + - **GIVEN** requirements bundle and active profile schema - **WHEN** `validate_bundle` runs - **THEN** missing required fields are reported diff --git a/openspec/changes/requirements-02-module-commands/specs/requirements-module/spec.md b/openspec/changes/requirements-02-module-commands/specs/requirements-module/spec.md index 769f2813..3cd5861a 100644 --- a/openspec/changes/requirements-02-module-commands/specs/requirements-module/spec.md +++ b/openspec/changes/requirements-02-module-commands/specs/requirements-module/spec.md @@ -1,21 +1,25 @@ ## ADDED Requirements ### Requirement: Requirements Module + The system SHALL provide requirements CLI commands for extract, author, validate, and list. #### Scenario: Extract command creates requirement artifacts + - **GIVEN** `specfact requirements extract --from-backlog --output .specfact/requirements/` - **WHEN** extraction succeeds - **THEN** one or more `*.req.yaml` files are produced - **AND** each file includes schema version and source backlog reference. #### Scenario: Author command applies profile-aware template fields + - **GIVEN** `specfact requirements author --template story` - **WHEN** active profile is `solo` - **THEN** authoring prompts require only solo-required fields - **AND** optional advanced fields remain non-blocking. #### Scenario: Validate and list expose completeness and trace coverage + - **GIVEN** requirement artifacts with trace references - **WHEN** `specfact requirements validate` and `specfact requirements list --show-coverage` run - **THEN** completeness and coverage are reported per requirement diff --git a/openspec/changes/requirements-03-backlog-sync/design.md b/openspec/changes/requirements-03-backlog-sync/design.md index 4409f9ce..4f030a96 100644 --- a/openspec/changes/requirements-03-backlog-sync/design.md +++ b/openspec/changes/requirements-03-backlog-sync/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `requirements-03-backlog-sync` from th ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/requirements-03-backlog-sync/proposal.md b/openspec/changes/requirements-03-backlog-sync/proposal.md index 669d6f40..59062bf8 100644 --- a/openspec/changes/requirements-03-backlog-sync/proposal.md +++ b/openspec/changes/requirements-03-backlog-sync/proposal.md @@ -2,9 +2,6 @@ ## Why - - - When backlog items change, requirements aren't updated. When requirements change, backlog items aren't updated. The two drift apart silently, creating a traceability gap that grows with every sprint. Teams discover the drift only during audits or after building the wrong thing. A bidirectional sync between backlog items and `.specfact/requirements/` using the sync kernel makes requirements and backlog items a single source of truth β€” with drift detection as the safety net. ## Ownership Alignment (2026-04-08) @@ -17,9 +14,6 @@ When backlog items change, requirements aren't updated. When requirements change ## What Changes - - - - **NEW**: `specfact requirements sync --from-backlog --project --preview` β€” pull structured requirements from backlog AC text, update `.specfact/requirements/` - **NEW**: `specfact requirements sync --to-backlog --project --preview` β€” push requirement-derived fields back to backlog items (missing AC, business value gaps, architectural constraints) - **NEW**: `specfact requirements drift --from-backlog --project ` β€” detect divergence between local requirements and backlog items without making changes @@ -30,6 +24,7 @@ When backlog items change, requirements aren't updated. When requirements change - **EXTEND**: Spec-Kit backlog extension awareness β€” before creating issues during push (requirements β†’ backlog), the sync SHALL query `ToolCapabilities.extension_commands` (from speckit-02) to detect active spec-kit backlog extensions (Jira, ADO, Linear, GitHub Projects, Trello). When a spec-kit backlog extension is active, the sync SHALL scan spec-kit feature `tasks.md` files for existing issue references (e.g., `PROJ-123`, `AB#456`) and import them as pre-existing mappings. Issue creation is skipped for tasks that already have spec-kit extension mappings, preventing duplicate issues. This detection is implemented in `speckit-03-change-proposal-bridge` (specfact-cli-modules) and consumed here via the adapter interface. ## Capabilities + ### New Capabilities - `requirements-backlog-sync`: Bidirectional sync between `.specfact/requirements/` and backlog items (GitHub, ADO, Jira, Linear) via sync kernel. Includes pull (extract from backlog), push (update backlog), and drift detection. @@ -39,7 +34,6 @@ When backlog items change, requirements aren't updated. When requirements change - `backlog-adapter`: Extended with requirements field extraction and update methods for bidirectional sync; extended with spec-kit backlog extension issue mapping import - `requirements-module`: Extended with sync and drift commands; extended with spec-kit duplicate issue prevention - --- ## Source Tracking diff --git a/openspec/changes/requirements-03-backlog-sync/specs/backlog-adapter/spec.md b/openspec/changes/requirements-03-backlog-sync/specs/backlog-adapter/spec.md index 78c0ef01..539d8fb2 100644 --- a/openspec/changes/requirements-03-backlog-sync/specs/backlog-adapter/spec.md +++ b/openspec/changes/requirements-03-backlog-sync/specs/backlog-adapter/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Backlog Adapter + The backlog adapter SHALL support requirement-aware pull and push sync operations. #### Scenario: Pull sync maps backlog changes to requirements updates + - **GIVEN** backlog item acceptance criteria changed since last sync - **WHEN** requirements sync pull runs - **THEN** corresponding requirement artifact is updated in preview patch - **AND** changed fields are listed in sync output. #### Scenario: Push sync updates backlog fields from requirements + - **GIVEN** requirement has updated business value fields - **WHEN** push sync apply runs - **THEN** mapped backlog fields are updated through adapter write APIs diff --git a/openspec/changes/requirements-03-backlog-sync/specs/requirements-backlog-sync/spec.md b/openspec/changes/requirements-03-backlog-sync/specs/requirements-backlog-sync/spec.md index f7d893d9..b133ca60 100644 --- a/openspec/changes/requirements-03-backlog-sync/specs/requirements-backlog-sync/spec.md +++ b/openspec/changes/requirements-03-backlog-sync/specs/requirements-backlog-sync/spec.md @@ -1,15 +1,18 @@ ## ADDED Requirements ### Requirement: Requirements Backlog Sync + The system SHALL support bidirectional backlog and requirements synchronization using sync-kernel session semantics. #### Scenario: Preview-first sync does not write upstream + - **GIVEN** `specfact requirements sync --from-backlog github --preview` - **WHEN** sync executes - **THEN** a patch preview is generated - **AND** no upstream write is performed. #### Scenario: Drift is detected and reported + - **GIVEN** backlog and requirement artifacts diverged for the same story - **WHEN** sync analysis runs - **THEN** drift is flagged with field-level differences diff --git a/openspec/changes/requirements-03-backlog-sync/specs/requirements-module/spec.md b/openspec/changes/requirements-03-backlog-sync/specs/requirements-module/spec.md index c6a01596..4e4e05aa 100644 --- a/openspec/changes/requirements-03-backlog-sync/specs/requirements-module/spec.md +++ b/openspec/changes/requirements-03-backlog-sync/specs/requirements-module/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Requirements Module + The requirements module SHALL include sync workflows backed by the sync kernel. #### Scenario: Sync command uses shared session model + - **GIVEN** requirements sync starts - **WHEN** session is created - **THEN** sync output includes session ID and status - **AND** unresolved conflicts can be resumed later. #### Scenario: Sync command supports apply mode explicitly + - **GIVEN** preview output is accepted - **WHEN** apply mode is requested - **THEN** patch operations are executed with write guards diff --git a/openspec/changes/traceability-01-index-and-orphans/design.md b/openspec/changes/traceability-01-index-and-orphans/design.md index 5d5d1444..fb4877d8 100644 --- a/openspec/changes/traceability-01-index-and-orphans/design.md +++ b/openspec/changes/traceability-01-index-and-orphans/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `traceability-01-index-and-orphans` fr ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/traceability-01-index-and-orphans/proposal.md b/openspec/changes/traceability-01-index-and-orphans/proposal.md index e074d9ca..a3dc8e32 100644 --- a/openspec/changes/traceability-01-index-and-orphans/proposal.md +++ b/openspec/changes/traceability-01-index-and-orphans/proposal.md @@ -2,9 +2,6 @@ ## Why - - - As the number of requirements, specs, and code modules grows, manually tracking traceability becomes impossible. Teams need a fast, queryable index that maps every artifact to its upstream/downstream counterparts β€” and actively detects orphans (artifacts with broken or missing links). This index is the backbone for the full-chain validation, coverage dashboards, and ceremony enrichment. Without it, traceability is a write-once artifact that decays the moment someone adds a new endpoint without linking it. ## Ownership Alignment (2026-04-08) @@ -40,6 +37,7 @@ modules/trace/ ``` **`module-package.yaml` declares:** + - `name: trace` - `version: 0.1.0` - `commands: [trace index, trace show, trace orphans, trace matrix]` @@ -71,6 +69,7 @@ modules/trace/ ``` **`module-package.yaml` declares:** + - `name: trace` - `version: 0.1.0` - `commands: [trace index, trace show, trace orphans, trace matrix]` @@ -79,9 +78,6 @@ modules/trace/ ## What Changes - - - - **NEW**: Trace module in `modules/trace/` with auto-maintained traceability index - **NEW**: `specfact trace index --rebuild` β€” scan all requirements, architecture, specs, code, and test artifacts to build a comprehensive traceability index stored at `.specfact/trace/index.json` - **NEW**: `specfact trace show REQ-123` β€” query upstream/downstream links for any artifact (requirement, component, spec operation, code module, test) @@ -91,6 +87,7 @@ modules/trace/ - **NEW**: TraceIndex model with bidirectional links: each entry stores both `upstream_refs` and `downstream_refs` ## Capabilities + ### New Capabilities - `traceability-index`: Auto-maintained bidirectional traceability index mapping requirements β†’ architecture β†’ specs β†’ code β†’ tests, with orphan detection, incremental updates, and matrix export in markdown/CSV/JSON. @@ -99,7 +96,6 @@ modules/trace/ (none) - --- ## Source Tracking diff --git a/openspec/changes/traceability-01-index-and-orphans/specs/traceability-index/spec.md b/openspec/changes/traceability-01-index-and-orphans/specs/traceability-index/spec.md index 82c27e50..603615fa 100644 --- a/openspec/changes/traceability-01-index-and-orphans/specs/traceability-index/spec.md +++ b/openspec/changes/traceability-01-index-and-orphans/specs/traceability-index/spec.md @@ -1,21 +1,25 @@ ## ADDED Requirements ### Requirement: Traceability Index + The system SHALL maintain a queryable index of upstream and downstream links across requirements, architecture, specs, code, and tests. #### Scenario: Rebuild index captures all artifact layers + - **GIVEN** `specfact trace index --rebuild` - **WHEN** index generation completes - **THEN** `.specfact/trace/index.json` contains entries for requirement, architecture, spec, code, and test artifacts - **AND** each entry contains both upstream and downstream references. #### Scenario: Orphan command reports missing linkage by type + - **GIVEN** at least one artifact has missing required references - **WHEN** `specfact trace orphans` runs - **THEN** output groups orphan findings by artifact type - **AND** each finding includes artifact identifier and missing link category. #### Scenario: Matrix export supports machine and human formats + - **GIVEN** a built index - **WHEN** `specfact trace matrix --format markdown|csv|json` runs - **THEN** matrix output includes requirement-centered chain rows diff --git a/openspec/changes/validation-02-full-chain-engine/design.md b/openspec/changes/validation-02-full-chain-engine/design.md index 7cf04d2a..43089e44 100644 --- a/openspec/changes/validation-02-full-chain-engine/design.md +++ b/openspec/changes/validation-02-full-chain-engine/design.md @@ -5,11 +5,13 @@ This change implements proposal scope for `validation-02-full-chain-engine` from ## Goals / Non-Goals **Goals:** + - Define an implementation approach that stays within the proposal scope. - Keep compatibility with existing module registry, adapter bridge, and contract-first patterns. - Preserve offline-first behavior and deterministic CLI execution. **Non-Goals:** + - No production code implementation in this stage. - No schema-breaking changes outside declared capabilities. - No dependency expansion beyond the proposal and plan. diff --git a/openspec/changes/validation-02-full-chain-engine/proposal.md b/openspec/changes/validation-02-full-chain-engine/proposal.md index 18a24659..14335d51 100644 --- a/openspec/changes/validation-02-full-chain-engine/proposal.md +++ b/openspec/changes/validation-02-full-chain-engine/proposal.md @@ -2,9 +2,6 @@ ## Why - - - Validation today operates only at the spec-code level (`specfact validate` checks spec deltas and contract enforcement). There is no way to validate the entire chain from business requirements through architecture to code and tests. This means a project can pass all technical validations while building entirely the wrong thing. A `--full-chain` validation mode that checks every layer transition β€” Req β†’ Arch β†’ Spec β†’ Code β†’ Tests β€” and reports gaps, orphans, and coverage metrics, unlocks the core value proposition: end-to-end traceability with actionable evidence. ## Ownership Alignment (2026-04-08) @@ -47,9 +44,6 @@ modules/validate/ ## What Changes - - - - **EXTEND**: `specfact validate` extended with `--full-chain` flag that runs validation across all layer transitions: - Req β†’ Arch: Every business rule mapped to component; every architectural constraint has ADR - Arch β†’ Spec: Every component has OpenAPI/AsyncAPI spec @@ -60,6 +54,7 @@ modules/validate/ - **NEW**: Full-chain validation orchestrator in `modules/validate/src/validate/engine/full_chain.py` β€” runs all layer transition checks, aggregates results, computes coverage metrics - **NEW**: Layer transition rules with profile-dependent severity: solo gets advisory-only, enterprise gets hard-fail with evidence - **NEW**: Machine-readable evidence output (JSON) for CI gates: + ```json { "schema_version": "1.0", @@ -75,10 +70,12 @@ modules/validate/ "overall": "PASS_WITH_ADVISORY" } ``` + - **NEW**: `--evidence-dir .specfact/evidence/` flag for persisting validation evidence artifacts - **EXTEND**: Policy engine integration β€” layer transition severities configurable via policy-engine-01 policy rules ## Capabilities + ### New Capabilities - `full-chain-validation`: End-to-end validation across all traceability layers (Req β†’ Arch β†’ Spec β†’ Code β†’ Tests) with profile-dependent severity, orphan detection, coverage metrics, and machine-readable evidence output for CI gates. @@ -88,7 +85,6 @@ modules/validate/ - `sidecar-validation`: Extended with `--full-chain` flag; existing spec-delta validation preserved as-is when flag is omitted - `full-chain-validation`: Extended with optional code-quality side-channel reporting that remains parallel to the Req β†’ Arch β†’ Spec β†’ Code β†’ Tests transitions - --- ## Source Tracking diff --git a/openspec/changes/validation-02-full-chain-engine/specs/full-chain-validation/spec.md b/openspec/changes/validation-02-full-chain-engine/specs/full-chain-validation/spec.md index 75223044..9cb91564 100644 --- a/openspec/changes/validation-02-full-chain-engine/specs/full-chain-validation/spec.md +++ b/openspec/changes/validation-02-full-chain-engine/specs/full-chain-validation/spec.md @@ -1,27 +1,32 @@ ## ADDED Requirements ### Requirement: Full Chain Validation + The system SHALL validate Requirement -> Architecture -> Spec -> Code -> Test transitions and emit layered evidence. #### Scenario: Full-chain command emits transition-level results + - **GIVEN** `specfact validate --full-chain --output json --evidence-dir .specfact/evidence/` - **WHEN** validation runs - **THEN** report includes transition groups `req_to_arch`, `arch_to_spec`, `spec_to_code`, and `code_to_tests` - **AND** each group reports pass/fail/advisory counts. #### Scenario: Severity respects policy mode and profile + - **GIVEN** enterprise profile with hard mode - **WHEN** a required Req -> Arch mapping is missing - **THEN** overall validation exits non-zero - **AND** evidence marks the violation as blocking. #### Scenario: Orphan detection is included in evidence + - **GIVEN** specs without requirement links exist - **WHEN** full-chain validation runs - **THEN** orphan entries are listed in evidence - **AND** orphan summary is included in overall status computation. #### Scenario: Code quality can be included without becoming a chain layer + - **GIVEN** `specfact validate --full-chain --with-code-quality` is executed - **WHEN** the validation run completes - **THEN** the evidence output includes a `code_quality` summary sourced from `specfact review` diff --git a/openspec/changes/validation-02-full-chain-engine/specs/sidecar-validation/spec.md b/openspec/changes/validation-02-full-chain-engine/specs/sidecar-validation/spec.md index 2d10b970..ab258666 100644 --- a/openspec/changes/validation-02-full-chain-engine/specs/sidecar-validation/spec.md +++ b/openspec/changes/validation-02-full-chain-engine/specs/sidecar-validation/spec.md @@ -1,15 +1,18 @@ ## MODIFIED Requirements ### Requirement: Sidecar Validation + The sidecar validation capability SHALL support full-chain payload checks in addition to spec-code checks. #### Scenario: Sidecar consumes full-chain input set + - **GIVEN** requirement and architecture artifact paths are provided - **WHEN** sidecar validation runs - **THEN** sidecar validates layered chain references - **AND** results are merged into full-chain evidence output. #### Scenario: Existing spec-code validation remains supported + - **GIVEN** sidecar is invoked without requirements/architecture inputs - **WHEN** validation executes - **THEN** existing spec-code validation behavior continues unchanged. diff --git a/openspec/config.yaml b/openspec/config.yaml index 1bcde599..91ba81c9 100644 --- a/openspec/config.yaml +++ b/openspec/config.yaml @@ -88,6 +88,12 @@ rules: - **Repository**: /, - **Last Synced Status**: ). - After creation, update proposal.md Source Tracking section with issue number, URL, repository, and status. + - >- + Resolve Parent Feature or Epic from `.specfact/backlog/github_hierarchy_cache.md` first (regenerate + via `python scripts/sync_github_hierarchy_cache.py` when missing or stale). The cache is ephemeral + local state and MUST NOT be committed. **Pending:** until backlog commands read this cache + automatically, treat this as contributor/agent workflow (docs + local script), not enforced CLI + behavior for every `specfact backlog` path. - Source tracking: Only track public repos (specfact-cli, platform-frontend). Skip for internal repos (specfact-cli-internal) specs: @@ -157,6 +163,11 @@ rules: - Place this task after quality gates and documentation, before PR creation. - Include git workflow tasks: branch creation (first task), PR creation (last task) - For public-facing changes in public repos (specfact-cli, platform-frontend): + - >- + Before GitHub issue creation or parent linking, consult `.specfact/backlog/github_hierarchy_cache.md`; + rerun `python scripts/sync_github_hierarchy_cache.py` when the cache is missing or stale. Treat this + cache as ephemeral local state, not a committed OpenSpec artifact. **Pending:** wire cache-first + lookup into backlog add/sync codepaths when the governance hierarchy-cache work lands end-to-end. - Include GitHub issue creation task with format: - title `[Change] ` - labels `enhancement` and `change-proposal` diff --git a/openspec/specs/adapter-development-guide/spec.md b/openspec/specs/adapter-development-guide/spec.md index 51a65ee6..2b24f0aa 100644 --- a/openspec/specs/adapter-development-guide/spec.md +++ b/openspec/specs/adapter-development-guide/spec.md @@ -1,13 +1,17 @@ # adapter-development-guide Specification ## Purpose + TBD - created by archiving change arch-08-documentation-discrepancies-remediation. Update Purpose after archive. + ## Requirements + ### Requirement: Full BridgeAdapter interface documented The adapter development guide (or extended creating-custom-bridges) SHALL document the full BridgeAdapter interface: detect, import_artifact, export_artifact, load_change_tracking, save_change_tracking (or equivalent), with contracts and usage notes. #### Scenario: Developer implements adapter + - **GIVEN** the adapter development guide (or extended creating-custom-bridges) - **WHEN** a developer implements an adapter - **THEN** the full BridgeAdapter interface is documented @@ -18,6 +22,7 @@ The adapter development guide (or extended creating-custom-bridges) SHALL docume The ToolCapabilities model and its role in adapter selection (e.g. sync modes) SHALL be documented, with reference to code (e.g. models/bridge.py) if needed. #### Scenario: Developer declares or uses capabilities + - **GIVEN** the adapter documentation - **WHEN** a developer needs to declare or use adapter capabilities - **THEN** ToolCapabilities model is documented @@ -28,6 +33,7 @@ The ToolCapabilities model and its role in adapter selection (e.g. sync modes) S The adapter guide SHALL provide at least one code reference or minimal example (e.g. base adapter, existing OpenSpec/SpecKit adapter) so that implementation is clear. #### Scenario: Developer follows adapter guide + - **GIVEN** the adapter guide - **WHEN** a developer follows the guide - **THEN** at least one code reference or minimal example is provided @@ -38,8 +44,8 @@ The adapter guide SHALL provide at least one code reference or minimal example ( The adapter development content SHALL be reachable from the docs navigation and from bridge/architecture documentation. #### Scenario: User looks for adapter development + - **GIVEN** the published docs - **WHEN** a user looks for adapter or bridge development - **THEN** the adapter development content is reachable from the docs navigation - **AND** from bridge/architecture documentation - diff --git a/openspec/specs/ado-field-value-selection/spec.md b/openspec/specs/ado-field-value-selection/spec.md index a155966e..30972192 100644 --- a/openspec/specs/ado-field-value-selection/spec.md +++ b/openspec/specs/ado-field-value-selection/spec.md @@ -1,8 +1,11 @@ # ado-field-value-selection Specification ## Purpose + TBD - created by archiving change backlog-core-07-ado-required-custom-fields-and-picklists. Update Purpose after archive. + ## Requirements + ### Requirement: Interactive constrained value selection for ADO custom fields The system SHALL provide an interactive picker for ADO mapped custom fields that expose constrained allowed values. @@ -20,4 +23,3 @@ The system SHALL provide an interactive picker for ADO mapped custom fields that - **WHEN** interactive add requests that field - **THEN** the command falls back to text input with a warning - **AND** add-time validation still checks persisted constraints when available. - diff --git a/openspec/specs/adr-template/spec.md b/openspec/specs/adr-template/spec.md index a03aec9b..c972cb15 100644 --- a/openspec/specs/adr-template/spec.md +++ b/openspec/specs/adr-template/spec.md @@ -1,13 +1,17 @@ # adr-template Specification ## Purpose + TBD - created by archiving change arch-08-documentation-discrepancies-remediation. Update Purpose after archive. + ## Requirements + ### Requirement: ADR template exists The docs SHALL provide an ADR template with at least: title, status, context, decision, consequences. #### Scenario: Maintainer records new decision + - **GIVEN** the docs repository - **WHEN** a maintainer wants to record a new architectural decision - **THEN** an ADR template exists (e.g. in docs/architecture/adr/template.md) @@ -18,6 +22,7 @@ The docs SHALL provide an ADR template with at least: title, status, context, de The ADR directory SHALL contain at least one ADR (e.g. for module-first architecture) following the template. #### Scenario: Reader opens architecture docs + - **GIVEN** the ADR directory - **WHEN** a reader opens the architecture documentation - **THEN** at least one ADR is present following the template @@ -28,8 +33,8 @@ The ADR directory SHALL contain at least one ADR (e.g. for module-first architec ADRs SHALL be linked from docs/architecture/README.md or docs/reference/architecture.md so they can be found without searching the repo. #### Scenario: User navigates architecture docs + - **GIVEN** the docs site (e.g. docs.specfact.io) - **WHEN** a user navigates architecture or reference docs - **THEN** ADRs are linked - **AND** discoverable from the menu or architecture index - diff --git a/openspec/specs/agent-governance-loading/spec.md b/openspec/specs/agent-governance-loading/spec.md new file mode 100644 index 00000000..0c0e1e43 --- /dev/null +++ b/openspec/specs/agent-governance-loading/spec.md @@ -0,0 +1,117 @@ +# agent-governance-loading Specification + +## Purpose +TBD - created by archiving change governance-03-deterministic-agent-governance-loading. Update Purpose after archive. +## Requirements +### Requirement: Compact AGENTS bootstrap contract + +The repository SHALL keep `AGENTS.md` as the mandatory bootstrap governance surface, but it SHALL remain compact and SHALL delegate long-form operational detail to canonical rule artifacts. + +#### Scenario: Session bootstrap reads compact governance contract + +- **WHEN** an agent starts work in the repository +- **THEN** it reads `AGENTS.md` first +- **AND** `AGENTS.md` defines a mandatory bootstrap sequence rather than embedding the full long-form governance corpus +- **AND** the bootstrap sequence requires loading `docs/agent-rules/INDEX.md` +- **AND** the bootstrap sequence requires loading the canonical non-negotiable checklist before code implementation work + +#### Scenario: AGENTS stays compact while preserving enforcement + +- **WHEN** repository governance is updated +- **THEN** `AGENTS.md` SHALL retain only the bootstrap contract, invariant governance rules, and canonical references needed for startup +- **AND** detailed workflow, validation, or finalization guidance SHALL live in dedicated rule artifacts rather than being duplicated inline + +### Requirement: Deterministic rule index and loading semantics + +The repository SHALL provide a canonical rule index that deterministically decides which governance rule files must be loaded for a given task. + +#### Scenario: Always-load rule subset + +- **WHEN** an agent loads the governance rule index +- **THEN** the index SHALL identify a mandatory always-load subset +- **AND** that subset SHALL include the non-negotiable checklist +- **AND** the index SHALL define the order in which always-load rules are processed + +#### Scenario: Applicability-based rule loading + +- **WHEN** a task involves worktree management, OpenSpec change selection, GitHub issue coordination, TDD gating, quality verification, or finalization +- **THEN** the index SHALL map those task signals to specific `docs/agent-rules/*.md` files +- **AND** the index SHALL define which rule files are required versus optional for that task class +- **AND** the loading decision SHALL not depend on heuristic file-name guessing alone + +#### Scenario: Deterministic precedence + +- **WHEN** governance instructions overlap across `AGENTS.md`, the rule index, rule files, and change-local artifacts +- **THEN** the repository SHALL define a single precedence order for which instruction wins +- **AND** the precedence order SHALL include explicit handling for direct user override where repository governance permits it + +### Requirement: Governance rule files use machine-readable frontmatter + +Every dedicated governance rule artifact SHALL include machine-readable frontmatter that defines how and when the rule applies. + +#### Scenario: Required frontmatter fields are present + +- **WHEN** a file under `docs/agent-rules/` is intended to govern agent behavior +- **THEN** it SHALL include frontmatter fields for rule identity, applicability, priority, blocking semantics, and stop conditions +- **AND** it SHALL declare whether the file is always loaded +- **AND** it SHALL declare whether user interaction is required when the rule blocks progress + +#### Scenario: Frontmatter drives deterministic behavior + +- **WHEN** an agent evaluates a rule file with frontmatter +- **THEN** it can determine from metadata whether the rule is mandatory for the current task +- **AND** it can determine whether the rule requires a hard stop, read-only continuation, or interactive clarification +- **AND** it does not need to infer those semantics solely from unstructured prose + +### Requirement: Governance must define explicit stop and continue behavior + +The governance system SHALL define explicit blocking behavior for stale changes, concurrency ambiguity, missing metadata, and TDD gate violations. + +#### Scenario: Blocking ambiguity requires user clarification + +- **WHEN** a selected change is stale, superseded, ambiguous, or linked to GitHub work already in progress +- **THEN** the applicable rule SHALL require the agent to stop implementation work +- **AND** the rule SHALL state that the ambiguity must be surfaced to the user for clarification +- **AND** the rule SHALL define whether read-only investigation may continue while implementation remains blocked + +#### Scenario: TDD gate remains non-bypassable in compact governance + +- **WHEN** a task changes behavior in code +- **THEN** the applicable rule SHALL still require spec updates, test creation, failing-test evidence, implementation, and passing evidence in that order +- **AND** compact governance SHALL not weaken or omit that sequence + +### Requirement: Public GitHub work must pass metadata completeness checks + +The governance system SHALL define explicit readiness checks for linked GitHub change issues before implementation proceeds for public repository work. + +#### Scenario: Parent and dependency metadata must be complete + +- **WHEN** an agent prepares to implement a publicly tracked change with a linked GitHub issue +- **THEN** the applicable governance rules SHALL require verifying the issue's parent relationship, blockers, and blocked-by relationships against current repository GitHub reality +- **AND** the parent lookup SHALL use the local hierarchy cache first and refresh the cache when repository-defined freshness rules require it +- **AND** implementation SHALL not proceed if the required parent or dependency metadata is missing or ambiguous + +#### Scenario: Labels and project assignment must be complete + +- **WHEN** an agent prepares to implement a publicly tracked change with a linked GitHub issue +- **THEN** the applicable governance rules SHALL require verifying that the issue has the required labels and project assignment for repository workflow completeness +- **AND** implementation SHALL not proceed until that metadata is complete or the user explicitly directs an allowed exception path + +#### Scenario: Live GitHub issue state can block implementation + +- **WHEN** an agent prepares to implement a publicly tracked change with a linked GitHub issue +- **AND** the issue is already marked `in progress` +- **THEN** the governance rules SHALL treat that state as a concurrency ambiguity +- **AND** the agent SHALL stop implementation work and ask the user to clarify whether the change is already being worked in another session +- **AND** the rules SHALL define whether only read-only investigation may continue while implementation remains blocked + +### Requirement: Canonical aliases prevent instruction drift + +Repository instruction surfaces other than `AGENTS.md` SHALL reference the canonical governance rule system instead of embedding duplicate long-form policy text. + +#### Scenario: Alias instruction surfaces stay synchronized + +- **WHEN** a contributor reads another repository instruction surface such as `CLAUDE.md` or generated IDE guidance +- **THEN** the surface SHALL reference the canonical rule system for governance semantics +- **AND** it SHALL avoid copying long-form governance content that could drift from the canonical source + diff --git a/openspec/specs/agent-instruction-clean-code-charter/spec.md b/openspec/specs/agent-instruction-clean-code-charter/spec.md index 89e8b424..4f21f7b9 100644 --- a/openspec/specs/agent-instruction-clean-code-charter/spec.md +++ b/openspec/specs/agent-instruction-clean-code-charter/spec.md @@ -1,20 +1,25 @@ # agent-instruction-clean-code-charter Specification ## Purpose + TBD - created by archiving change clean-code-01-principle-gates. Update Purpose after archive. + ## Requirements + ### Requirement: Agent Instruction Clean-Code Charter + The repository SHALL expose the 7-principle clean-code charter consistently across its instruction surfaces without creating drift-prone duplicate sources of truth. #### Scenario: Core instruction surfaces reference the charter consistently + - **GIVEN** a contributor opens `AGENTS.md`, `CLAUDE.md`, `.cursor/rules/clean-code-principles.mdc`, or `.github/copilot-instructions.md` - **WHEN** they inspect clean-code guidance - **THEN** each surface points to the same clean-code charter semantics - **AND** any shorter alias surface references the canonical charter rather than redefining it independently #### Scenario: Generated IDE aliases stay lightweight + - **GIVEN** platform-specific instruction files are generated from `ai-integration-03-instruction-files` - **WHEN** clean-code guidance is included - **THEN** the generated file contains a short clean-code alias reference - **AND** the full charter text is not duplicated into every generated alias - diff --git a/openspec/specs/agile-feature-hierarchy/spec.md b/openspec/specs/agile-feature-hierarchy/spec.md index 8cfaf61e..160941c0 100644 --- a/openspec/specs/agile-feature-hierarchy/spec.md +++ b/openspec/specs/agile-feature-hierarchy/spec.md @@ -1,19 +1,34 @@ # agile-feature-hierarchy Specification ## Purpose + Keep the public SpecFact CLI backlog organized as a three-level GitHub hierarchy of Epic -> Feature -> User Story, with `openspec/CHANGE_ORDER.md` kept in sync with the current planning structure. + ## Requirements + ### Requirement: GitHub Agile Feature Hierarchy -The project governance workflow SHALL maintain a three-level GitHub planning hierarchy of Epic -> Feature -> User Story for the public SpecFact CLI backlog. + +The project governance workflow SHALL maintain a three-level GitHub planning hierarchy: Epic β†’ Feature β†’ User Story, for +the public SpecFact CLI backlog. It SHALL expose current Epic and Feature metadata through a repo-local hierarchy cache +before manual GitHub lookups are used. #### Scenario: Feature issues group user stories under the correct epic + - **GIVEN** the public backlog contains Epic issues and change-proposal issues - **WHEN** the hierarchy setup work is completed - **THEN** each planned Feature issue is linked to its parent Epic - **AND** each grouped User Story issue is assigned to the correct Feature #### Scenario: CHANGE_ORDER stays aligned with the GitHub hierarchy + - **GIVEN** new Epic or Feature-level hierarchy items are introduced in GitHub - **WHEN** the change is updated - **THEN** `openspec/CHANGE_ORDER.md` reflects the current Epic and Feature sequencing metadata - **AND** stale issue state such as archived-but-open items is reconciled during validation + +#### Scenario: Local cache is consulted before manual hierarchy lookup + +- **GIVEN** a contributor needs a parent Feature or Epic while creating or syncing a change issue +- **WHEN** the local hierarchy cache is present and current +- **THEN** the contributor can resolve the parent relationship from the cache without an additional GitHub lookup +- **AND** the sync script is rerun only when the cache is stale or missing diff --git a/openspec/specs/ai-refinement/spec.md b/openspec/specs/ai-refinement/spec.md index d35519aa..175b38c7 100644 --- a/openspec/specs/ai-refinement/spec.md +++ b/openspec/specs/ai-refinement/spec.md @@ -1,6 +1,7 @@ # ai-refinement Specification ## Purpose + TBD - created by archiving change add-template-driven-backlog-refinement. Update Purpose after archive. ## Requirements diff --git a/openspec/specs/api-error-diagnostics/spec.md b/openspec/specs/api-error-diagnostics/spec.md index d0579b02..9216cf48 100644 --- a/openspec/specs/api-error-diagnostics/spec.md +++ b/openspec/specs/api-error-diagnostics/spec.md @@ -1,8 +1,11 @@ # api-error-diagnostics Specification ## Purpose + TBD - created by archiving change improve-ado-backlog-refine-error-logging. Update Purpose after archive. + ## Requirements + ### Requirement: ADO PATCH failure debug logging When an ADO PATCH request fails (HTTP 4xx/5xx), the system SHALL log structured diagnostic data in debug mode so the failing field and server message are identifiable. @@ -75,4 +78,3 @@ The system SHALL safely parse and truncate response bodies to avoid large logs a - **WHEN** building the error summary for debug log or user message - **THEN** the implementation uses `response.text[:N]` (e.g. 500–2000 chars) as fallback for message extraction - **AND** JSON parsing failures do not suppress logging; a safe string is used instead - diff --git a/openspec/specs/backlog-adapter/spec.md b/openspec/specs/backlog-adapter/spec.md index 6a925861..79c78f53 100644 --- a/openspec/specs/backlog-adapter/spec.md +++ b/openspec/specs/backlog-adapter/spec.md @@ -1,8 +1,11 @@ # backlog-adapter Specification ## Purpose + TBD - created by archiving change add-generic-backlog-abstraction. Update Purpose after archive. + ## Requirements + ### Requirement: BacklogAdapter Interface The system SHALL provide a standard `BacklogAdapter` interface that all backlog sources (GitHub, ADO, JIRA, GitLab, etc.) must implement. @@ -88,4 +91,3 @@ The system SHALL allow custom bridge field mappings for backlog converter workfl - **WHEN** custom mapping configuration is malformed - **THEN** converter execution SHALL continue with default mapping behavior - **AND** SHALL emit warning/debug context for troubleshooting. - diff --git a/openspec/specs/backlog-add-slash-prompt/spec.md b/openspec/specs/backlog-add-slash-prompt/spec.md index 4213900b..dde3f42c 100644 --- a/openspec/specs/backlog-add-slash-prompt/spec.md +++ b/openspec/specs/backlog-add-slash-prompt/spec.md @@ -1,8 +1,11 @@ # backlog-add-slash-prompt Specification ## Purpose + TBD - created by archiving change backlog-core-04-installed-runtime-discovery-and-add-prompt. Update Purpose after archive. + ## Requirements + ### Requirement: Backlog Add Slash Prompt The system SHALL provide and install a slash prompt for `backlog add` consistent with other backlog workflows. @@ -20,4 +23,3 @@ The system SHALL provide and install a slash prompt for `backlog add` consistent - **WHEN** template copying runs for an IDE target - **THEN** a `specfact.backlog-add` prompt file is created in the IDE-specific destination - **AND** installation behavior matches existing prompt commands. - diff --git a/openspec/specs/backlog-add/spec.md b/openspec/specs/backlog-add/spec.md index fe60a02e..5aa30325 100644 --- a/openspec/specs/backlog-add/spec.md +++ b/openspec/specs/backlog-add/spec.md @@ -1,8 +1,11 @@ # backlog-add Specification ## Purpose + TBD - created by archiving change backlog-core-02-interactive-issue-creation. Update Purpose after archive. + ## Requirements + ### Requirement: Backlog adapter create method The system SHALL extend backlog adapters with a create method that accepts a unified payload and returns the created item (id, key, url). @@ -227,27 +230,31 @@ The system SHALL support optional `--sprint ` so the created issue ca The system SHALL provide `specfact backlog add` command that creates backlog items with the same functionality as the deleted backlog-core implementation. #### Scenario: Add command creates GitHub issue + - **WHEN** the user runs `specfact backlog add --adapter github --project-id --type story --title "Test" --body "Body"` - **THEN** a GitHub issue is created with the specified title, body, and type - **AND** the command outputs the created issue ID, key, and URL #### Scenario: Add command creates ADO work item + - **WHEN** the user runs `specfact backlog add --adapter ado --project-id --type story --title "Test"` - **THEN** an ADO work item is created with the specified title and type - **AND** required custom fields are validated and included in payload #### Scenario: Interactive mode prompts for missing fields + - **WHEN** the user runs `specfact backlog add` without required fields - **THEN** interactive prompts request title, body, type, and parent - **AND** validation ensures parent exists before create #### Scenario: DoR validation before create + - **WHEN** the user runs `specfact backlog add --check-dor` - **THEN** the item is validated against `.specfact/dor.yaml` rules - **AND** creation proceeds only if DoR criteria are met #### Scenario: Ceremony alias works + - **WHEN** the user runs `specfact backlog ceremony add` - **THEN** the command forwards to `specfact backlog add` - **AND** all add options are available - diff --git a/openspec/specs/backlog-analyze-deps/spec.md b/openspec/specs/backlog-analyze-deps/spec.md index a4a82ba6..fe2b3908 100644 --- a/openspec/specs/backlog-analyze-deps/spec.md +++ b/openspec/specs/backlog-analyze-deps/spec.md @@ -1,23 +1,28 @@ # backlog-analyze-deps Specification ## Purpose + TBD - created by archiving change backlog-02-migrate-core-commands. Update Purpose after archive. + ## Requirements + ### Requirement: Restore backlog dependency analysis The system SHALL provide `specfact backlog analyze-deps` for dependency graph analysis. #### Scenario: Analyze-deps shows item dependencies + - **WHEN** the user runs `specfact backlog analyze-deps --project-id ` - **THEN** the backlog dependency graph is built - **AND** parent/child and blocking relationships are displayed #### Scenario: Cycle detection highlights issues + - **WHEN** the dependency graph contains cycles - **THEN** cycles are detected and reported as warnings - **AND** affected items are listed for resolution #### Scenario: Impact surface for selected item + - **WHEN** the user analyzes deps for a specific item - **THEN** upstream and downstream dependencies are highlighted - diff --git a/openspec/specs/backlog-daily-markdown-normalization/spec.md b/openspec/specs/backlog-daily-markdown-normalization/spec.md index f6356151..96812fe2 100644 --- a/openspec/specs/backlog-daily-markdown-normalization/spec.md +++ b/openspec/specs/backlog-daily-markdown-normalization/spec.md @@ -1,23 +1,29 @@ # backlog-daily-markdown-normalization Specification ## Purpose + TBD - created by archiving change backlog-scrum-05-summarize-markdown-output. Update Purpose after archive. + ## Requirements + ### Requirement: Normalize HTML and Markdown for summarize output The system SHALL normalize all backlog item descriptions and comments included in `specfact backlog daily --summarize` and `--summarize-to` output so that the resulting prompt contains **only Markdown-formatted text** (no raw HTML tags or HTML entities), regardless of whether the underlying provider stores content as HTML (e.g. ADO) or Markdown (e.g. GitHub, Markdown-style ADO comments). #### Scenario: HTML comments from ADO are converted to Markdown + - **WHEN** `specfact backlog daily --summarize` or `--summarize-to` includes work items whose description or comments are stored as HTML (e.g. ADO discussion/comments) - **THEN** the system converts that HTML content into readable Markdown before including it in the summarize prompt - **AND** the resulting output does not contain raw HTML tags or un-decoded HTML entities (e.g. `<div>`, `

`, `
`) #### Scenario: Existing Markdown comments are preserved as Markdown + - **WHEN** `specfact backlog daily --summarize` or `--summarize-to` includes items whose description or comments are already stored as Markdown (e.g. GitHub issues, Markdown-formatted ADO comments) - **THEN** the system preserves the original Markdown semantics when building the summarize prompt (headings, lists, code fences, emphasis) - **AND** the system does not degrade Markdown into a less structured format (e.g. by stripping list markers or collapsing headings) #### Scenario: Mixed HTML and Markdown sources produce a consistent Markdown prompt + - **WHEN** the daily summarize command aggregates items from sources that use different underlying formats (HTML and Markdown) - **THEN** the combined summarize output is a single, consistent Markdown document suitable for LLM consumption - **AND** no raw HTML tags or entities appear anywhere in the per-item body or comments sections @@ -27,18 +33,20 @@ The system SHALL normalize all backlog item descriptions and comments included i The system SHALL render the same normalized Markdown summarize content differently depending on whether it is running in an interactive terminal session or in a non-interactive / CI environment, while always preserving a prompt-ready Markdown representation that tools can consume. #### Scenario: Interactive terminal shows rich Markdown view + - **WHEN** a user runs `specfact backlog daily --summarize` in an interactive terminal that supports rich output (e.g. TTY, not redirected to a file) - **THEN** the CLI MAY render the summarize content using a Markdown-aware terminal view (for example, Rich Markdown rendering) - **AND** the user sees a readable, formatted standup summary prompt (headings, lists, emphasis) instead of raw Markdown or HTML - **AND** the underlying content remains logically the same as the Markdown text used for `--summarize-to` (same sections and text, just rendered differently) #### Scenario: Non-interactive or CI environments emit plain Markdown + - **WHEN** `specfact backlog daily --summarize` or `--summarize-to` is run in a non-interactive environment (e.g. CI/CD job, output redirected to a file or piped) - **THEN** the system emits plain, prompt-ready Markdown text without ANSI color codes or interactive formatting controls - **AND** the output still satisfies the existing summarize requirement to include instruction text, filter context, and per-item data (including normalized body and comments) #### Scenario: Summarize-to file output is always Markdown-only + - **WHEN** the user runs `specfact backlog daily --summarize-to ` - **THEN** the file at `` contains only normalized Markdown content (no raw HTML tags or entities, no terminal control codes) - **AND** the file is suitable for direct copy/paste into IDE slash commands or Copilot prompts without additional cleanup - diff --git a/openspec/specs/backlog-delta/spec.md b/openspec/specs/backlog-delta/spec.md index a3776608..c1fd9244 100644 --- a/openspec/specs/backlog-delta/spec.md +++ b/openspec/specs/backlog-delta/spec.md @@ -1,26 +1,32 @@ # backlog-delta Specification ## Purpose + TBD - created by archiving change backlog-02-migrate-core-commands. Update Purpose after archive. + ## Requirements + ### Requirement: Restore backlog delta subcommands The system SHALL provide `specfact backlog delta` with subcommands for backlog change analysis. #### Scenario: Delta status shows backlog changes + - **WHEN** the user runs `specfact backlog delta status --project-id ` - **THEN** current backlog state is compared to baseline - **AND** added/updated/deleted items are listed #### Scenario: Delta impact analyzes item effects + - **WHEN** the user runs `specfact backlog delta impact ` - **THEN** dependent items and cascade effects are identified #### Scenario: Delta cost-estimate calculates effort + - **WHEN** the user runs `specfact backlog delta cost-estimate` - **THEN** story points and business value deltas are aggregated #### Scenario: Delta rollback-analysis shows revert options + - **WHEN** the user runs `specfact backlog delta rollback-analysis` - **THEN** safe rollback paths and risks are presented - diff --git a/openspec/specs/backlog-map-fields/spec.md b/openspec/specs/backlog-map-fields/spec.md index 80001ece..083b605d 100644 --- a/openspec/specs/backlog-map-fields/spec.md +++ b/openspec/specs/backlog-map-fields/spec.md @@ -1,8 +1,11 @@ # backlog-map-fields Specification ## Purpose + TBD - created by archiving change backlog-core-05-user-modules-bootstrap. Update Purpose after archive. + ## Requirements + ### Requirement: Provider auth and field discovery checks The system SHALL verify auth context and discover provider fields/metadata before accepting mappings. diff --git a/openspec/specs/backlog-module-ownership/spec.md b/openspec/specs/backlog-module-ownership/spec.md index 72c68217..69a67dea 100644 --- a/openspec/specs/backlog-module-ownership/spec.md +++ b/openspec/specs/backlog-module-ownership/spec.md @@ -1,23 +1,29 @@ # backlog-module-ownership Specification ## Purpose + TBD - created by archiving change backlog-module-ownership-cleanup. Update Purpose after archive. + ## Requirements + ### Requirement: Backlog Feature Commands Must Be Module-Owned The system SHALL treat `nold-ai/specfact-backlog` as the sole owner of user-facing backlog and policy command surfaces, including the active proposal backlog and GitHub planning artifacts that track future backlog and ceremony feature work. #### Scenario: Core does not directly own backlog feature commands + - **WHEN** command registration is resolved in `specfact-cli` - **THEN** user-facing backlog feature commands are provided by the installed backlog module - **AND** core does not ship a parallel built-in backlog command surface for the same feature commands. #### Scenario: Core keeps only shared backlog framework contracts + - **WHEN** backlog ownership is resolved after migration - **THEN** core retains only shared provider integrations, generic data models, and minimal backlog contracts reused outside the backlog bundle - **AND** backlog-only command implementations, prompt resources, templates, and refinement helpers are not owned by core. #### Scenario: Active backlog proposals are not tracked as core-owned implementation work + - **WHEN** a pending OpenSpec change or linked GitHub issue describes backlog, scrum, kanban, safe, ceremony, or policy command behavior that belongs to `specfact-backlog` - **THEN** that work is assigned to the modules repo planning hierarchy rather than remaining a core-repo implementation story - **AND** the core repo retains only the shared contracts or bridge points, if any, that support the owning bundle. @@ -27,6 +33,7 @@ The system SHALL treat `nold-ai/specfact-backlog` as the sole owner of user-faci Backlog-specific prompts, prompt templates, and backlog template semantics SHALL be owned by the backlog module, not by `specfact-cli` core. #### Scenario: Backlog refinement assets are not exported from core + - **WHEN** backlog-specific prompt/template resources are resolved - **THEN** they come from the backlog module resource set - **AND** core retains only generic framework/template infrastructure, if any. @@ -36,7 +43,7 @@ Backlog-specific prompts, prompt templates, and backlog template semantics SHALL The system SHALL not rely on duplicate backlog command overlap handling for normal runtime registration. #### Scenario: Backlog registration is single-owned + - **WHEN** the backlog module is installed and enabled - **THEN** normal registration does not require suppressing duplicate backlog command collisions between core and module code - **AND** users do not see duplicate backlog-extension warnings caused by split ownership. - diff --git a/openspec/specs/backlog-refinement/spec.md b/openspec/specs/backlog-refinement/spec.md index 6fe5f64b..bd2a4853 100644 --- a/openspec/specs/backlog-refinement/spec.md +++ b/openspec/specs/backlog-refinement/spec.md @@ -1,8 +1,11 @@ # backlog-refinement Specification ## Purpose + TBD - created by archiving change add-template-driven-backlog-refinement. Update Purpose after archive. + ## Requirements + ### Requirement: Backlog Item Refinement Command The system SHALL provide a `specfact backlog refine` command that enables teams to standardize backlog items using AI-assisted template matching and refinement. diff --git a/openspec/specs/backlog-sync/spec.md b/openspec/specs/backlog-sync/spec.md index 5354d04d..f86cc961 100644 --- a/openspec/specs/backlog-sync/spec.md +++ b/openspec/specs/backlog-sync/spec.md @@ -1,27 +1,33 @@ # backlog-sync Specification ## Purpose + TBD - created by archiving change backlog-02-migrate-core-commands. Update Purpose after archive. + ## Requirements + ### Requirement: Restore backlog sync command functionality The system SHALL provide `specfact backlog sync` command for bidirectional backlog synchronization. #### Scenario: Sync from OpenSpec to backlog + - **WHEN** the user runs `specfact backlog sync --adapter github --project-id ` - **THEN** OpenSpec changes are exported to GitHub issues/ADO work items - **AND** state mapping preserves status semantics #### Scenario: Bidirectional sync with cross-adapter + - **WHEN** the user runs sync with cross-adapter configuration - **THEN** state is mapped between adapters using canonical status - **AND** lossless round-trip preserves content #### Scenario: Sync with bundle integration + - **WHEN** sync is run within an OpenSpec bundle context - **THEN** synced items update bundle state and source tracking #### Scenario: Ceremony alias works + - **WHEN** the user runs `specfact backlog ceremony sync` - **THEN** the command forwards to `specfact backlog sync` - diff --git a/openspec/specs/backlog-verify-readiness/spec.md b/openspec/specs/backlog-verify-readiness/spec.md index 9096ac92..48e790cc 100644 --- a/openspec/specs/backlog-verify-readiness/spec.md +++ b/openspec/specs/backlog-verify-readiness/spec.md @@ -1,19 +1,23 @@ # backlog-verify-readiness Specification ## Purpose + TBD - created by archiving change backlog-02-migrate-core-commands. Update Purpose after archive. + ## Requirements + ### Requirement: Restore Definition of Ready validation The system SHALL provide `specfact backlog verify-readiness` for DoR validation. #### Scenario: Verify-readiness checks DoR criteria + - **WHEN** the user runs `specfact backlog verify-readiness --project-id ` - **THEN** each backlog item is validated against `.specfact/dor.yaml` - **AND** items passing/failing DoR are reported #### Scenario: DoR failures show actionable guidance + - **WHEN** an item fails DoR validation - **THEN** specific missing criteria are listed - **AND** remediation hints are provided - diff --git a/openspec/specs/basedpyright-runner/spec.md b/openspec/specs/basedpyright-runner/spec.md index 7e39e161..70a56637 100644 --- a/openspec/specs/basedpyright-runner/spec.md +++ b/openspec/specs/basedpyright-runner/spec.md @@ -1,24 +1,30 @@ # basedpyright-runner Specification ## Purpose + TBD - created by archiving change code-review-03-type-governance-runners. Update Purpose after archive. + ## Requirements + ### Requirement: basedpyright Type-Safety Finding Extraction + The system SHALL parse `basedpyright --outputjson` output and map all diagnostics to `category="type_safety"`, filtered to the provided changed files only. #### Scenario: Type error maps to type_safety finding + - **GIVEN** basedpyright JSON output with a type error in `file_a.py` - **WHEN** `run_basedpyright(files=[file_a.py])` is called - **THEN** a `ReviewFinding` is returned with `category="type_safety"`, `tool="basedpyright"`, `severity="error"` - **AND** `file` and `line` are correctly populated #### Scenario: Only changed files are reported + - **GIVEN** basedpyright errors in multiple files but only `file_a.py` is in the provided list - **WHEN** `run_basedpyright(files=[file_a.py])` is called - **THEN** only findings from `file_a.py` are returned #### Scenario: basedpyright unavailable produces tool_error + - **GIVEN** basedpyright binary is unavailable - **WHEN** `run_basedpyright(files=[...])` is called - **THEN** one `ReviewFinding` with `category="tool_error"` is returned - diff --git a/openspec/specs/bridge-adapter/spec.md b/openspec/specs/bridge-adapter/spec.md index 07e0db96..61d1b367 100644 --- a/openspec/specs/bridge-adapter/spec.md +++ b/openspec/specs/bridge-adapter/spec.md @@ -3,7 +3,9 @@ ## Purpose The bridge adapter architecture provides a universal abstraction layer for integrating SpecFact with external tools and formats, including specification tools (Spec-Kit, OpenSpec), backlog management tools (GitHub Issues, Azure DevOps, Jira, Linear), and validation systems. The architecture uses a plugin-based adapter registry pattern that enables extensibility for future tool integrations while maintaining clean separation of concerns. + ## Requirements + ### Requirement: OpenSpec Adapter Type The system SHALL support OpenSpec as a bridge adapter type. @@ -485,6 +487,7 @@ The ADO adapter SHALL ensure organization is always included before project in A **And** this applies even when collection is already in base_url (on-premise) **Example URLs**: + - Cloud: `https://dev.azure.com/myorg/myproject/_apis/wit/wiql?api-version=7.1` - On-premise: `https://server/myorg/myproject/_apis/wit/wiql?api-version=7.1` @@ -645,4 +648,3 @@ The system SHALL support provider-specific templates for mapping adapter data to - **THEN** custom rules override template rules - **AND** adapter-specific data is still accessible via `raw_data` - **AND** unified graph model is used regardless of adapter - diff --git a/openspec/specs/bridge-registry/spec.md b/openspec/specs/bridge-registry/spec.md index 856c8d44..35a3e101 100644 --- a/openspec/specs/bridge-registry/spec.md +++ b/openspec/specs/bridge-registry/spec.md @@ -1,8 +1,11 @@ # bridge-registry Specification ## Purpose + TBD - created by archiving change arch-05-bridge-registry. Update Purpose after archive. + ## Requirements + ### Requirement: Bridge registry provides converter registration and lookup The system SHALL provide a `BridgeRegistry` that supports module-driven registration and lookup of service schema converters. @@ -86,4 +89,3 @@ The `BridgeConfig` spec-kit presets SHALL map all 7 core spec-kit slash commands - **WHEN** the preset is constructed - **THEN** `commands` dict contains the same 7 entries as the classic preset - **AND** artifact path patterns use `docs/specs/` prefix - diff --git a/openspec/specs/bundle-command-surface-alignment/spec.md b/openspec/specs/bundle-command-surface-alignment/spec.md index 94dcf3c7..06bed513 100644 --- a/openspec/specs/bundle-command-surface-alignment/spec.md +++ b/openspec/specs/bundle-command-surface-alignment/spec.md @@ -1,8 +1,11 @@ # bundle-command-surface-alignment Specification ## Purpose + TBD - created by archiving change module-migration-10-bundle-command-surface-alignment. Update Purpose after archive. + ## Requirements + ### Requirement: Documented Grouped Commands Must Resolve In Installed Official Bundles The system SHALL ensure that grouped CLI commands documented for a shipped release resolve in an environment where the corresponding official bundles are installed. @@ -50,4 +53,3 @@ The system SHALL fail validation when a documented grouped command path is missi - **WHEN** a documented grouped command path is not mounted by the installed bundle - **THEN** validation fails with the missing command path and owning bundle id - **AND** the report distinguishes this from help-only or docs-only command coverage. - diff --git a/openspec/specs/bundle-extraction/spec.md b/openspec/specs/bundle-extraction/spec.md index e63c7830..c2f8a40d 100644 --- a/openspec/specs/bundle-extraction/spec.md +++ b/openspec/specs/bundle-extraction/spec.md @@ -1,8 +1,11 @@ # bundle-extraction Specification ## Purpose + TBD - created by archiving change module-migration-02-bundle-extraction. Update Purpose after archive. + ## Requirements + ### Requirement: Each bundle has a canonical package directory in specfact-cli-modules Five bundle package directories SHALL be created in `specfact-cli-modules/packages/`, one per workflow-domain category defined by `module-migration-01`. @@ -123,4 +126,3 @@ Every `module-package.yaml` in `src/specfact_cli/modules/*/` SHALL have its `int - **WHEN** `hatch run ./scripts/verify-modules-signature.py --require-signature` is run - **THEN** the verification SHALL fail with an explicit error naming the affected module - **AND** SHALL indicate whether the failure is a checksum mismatch or signature mismatch - diff --git a/openspec/specs/bundle-mapping/spec.md b/openspec/specs/bundle-mapping/spec.md index e3d8e093..4f4811c1 100644 --- a/openspec/specs/bundle-mapping/spec.md +++ b/openspec/specs/bundle-mapping/spec.md @@ -1,8 +1,11 @@ # bundle-mapping Specification ## Purpose + TBD - created by archiving change verification-01-wave1-delta-closure. Update Purpose after archive. + ## Requirements + ### Requirement: Confidence-Based Routing The system SHALL route bundle mappings based on confidence thresholds: auto-assign (>=0.8), prompt user (0.5-0.8), require explicit selection (<0.5). @@ -104,4 +107,3 @@ The system SHALL provide an interactive prompt for bundle selection with confide - **WHEN** user selects "Q" option - **THEN** the system skips the item without mapping - diff --git a/openspec/specs/bundle-test-parity/spec.md b/openspec/specs/bundle-test-parity/spec.md index 359b6a1f..edf8ebe0 100644 --- a/openspec/specs/bundle-test-parity/spec.md +++ b/openspec/specs/bundle-test-parity/spec.md @@ -1,8 +1,11 @@ # bundle-test-parity Specification ## Purpose + TBD - created by archiving change module-migration-02-bundle-extraction. Update Purpose after archive. + ## Requirements + ### Requirement: Tests for bundle code live in specfact-cli-modules All tests that exercise the 17 migrated modules (or their bundle namespaces) SHALL be inventoried in specfact-cli and SHALL be present in specfact-cli-modules so that they run against the canonical bundle source in `packages/*/src/`. @@ -39,4 +42,3 @@ specfact-cli-modules SHALL provide the same quality gates as specfact-cli for bu - **WHEN** CI runs on push/PR - **THEN** workflows SHALL run format, type-check, lint, test (and contract-test, coverage threshold where applicable) - **AND** Python version(s) SHALL match specfact-cli (e.g. 3.11, 3.12, 3.13) if a matrix is used - diff --git a/openspec/specs/category-command-groups/spec.md b/openspec/specs/category-command-groups/spec.md index 817e64b7..1b7527da 100644 --- a/openspec/specs/category-command-groups/spec.md +++ b/openspec/specs/category-command-groups/spec.md @@ -1,8 +1,11 @@ # category-command-groups Specification ## Purpose + TBD - created by archiving change module-migration-01-categorize-and-group. Update Purpose after archive. + ## Requirements + ### Requirement: Category group commands aggregate member module sub-apps Each category group SHALL expose its member modules as sub-commands, preserving all existing sub-command names from each module. @@ -84,4 +87,3 @@ The `spec` module's existing `specfact spec` command conflicts with the `specfac - **THEN** the sub-command for the `spec` module SHALL appear as `api` (not `spec`) - **AND** the `spec` module's `validate`, `backward-compat`, `generate-tests`, and `mock` sub-commands SHALL be accessible via `specfact spec api ` - **AND** the flat shim `specfact spec ` SHALL still delegate to `specfact spec api ` during the migration window - diff --git a/openspec/specs/ceremony-cockpit/spec.md b/openspec/specs/ceremony-cockpit/spec.md index 511d6f24..89987de9 100644 --- a/openspec/specs/ceremony-cockpit/spec.md +++ b/openspec/specs/ceremony-cockpit/spec.md @@ -1,8 +1,11 @@ # ceremony-cockpit Specification ## Purpose + TBD - created by archiving change ceremony-cockpit-01-ceremony-aliases. Update Purpose after archive. + ## Requirements + ### Requirement: Ceremony aliases The system SHALL provide ceremony-oriented entry points under backlog: `specfact backlog ceremony standup` (delegates to `backlog daily`), `specfact backlog ceremony refinement` (delegates to `backlog refine`), `specfact backlog ceremony planning` (delegates to `backlog sprint-summary` when installed). Optional: `backlog ceremony flow` β†’ `backlog flow`, `backlog ceremony pi-summary` β†’ `backlog pi-summary` when those commands exist. @@ -70,4 +73,3 @@ The system SHALL apply exceptions-first default section order (blockers, policy **Acceptance Criteria**: - Order is default when data available; existing backlog daily behavior is extended, not replaced; backward compatible. - diff --git a/openspec/specs/ci-integration/spec.md b/openspec/specs/ci-integration/spec.md index 6e3553e7..1dc944a8 100644 --- a/openspec/specs/ci-integration/spec.md +++ b/openspec/specs/ci-integration/spec.md @@ -1,70 +1,87 @@ # ci-integration Specification ## Purpose + TBD - created by archiving change ci-docs-sync-check. Update Purpose after archive. + ## Requirements + ### Requirement: Branch Protection Configuration + The system SHALL configure branch protection to require docs sync check. #### Scenario: Main branch protection + - **WHEN** branch protection is configured - **THEN** main branch SHALL require docs sync check - **AND** prevent merges with failing checks #### Scenario: Develop branch protection + - **WHEN** branch protection is configured - **THEN** develop branch SHALL require docs sync check - **AND** prevent merges with failing checks ### Requirement: Status Check Integration + The system SHALL integrate docs sync check as a required status check. #### Scenario: Required status check setup + - **WHEN** CI integration is complete - **THEN** docs sync check SHALL be required status check - **AND** appear in branch protection settings #### Scenario: Status check enforcement + - **WHEN** PR has failing docs sync check - **THEN** PR SHALL be blocked from merge - **AND** clear error SHALL be shown ### Requirement: Exemption Label Support + The system SHALL support exemption labels for intentional documentation exemptions. #### Scenario: Docs exempt label + - **WHEN** PR has `docs-exempt` label - **THEN** docs sync check SHALL be skipped - **AND** PR SHALL not be blocked #### Scenario: No exemption label + - **WHEN** PR doesn't have exemption label - **THEN** docs sync check SHALL run normally - **AND** enforce documentation requirements ### Requirement: Error Reporting Integration + The system SHALL integrate error reporting with GitHub UI. #### Scenario: Clear error messages + - **WHEN** docs sync check fails - **THEN** error messages SHALL appear in GitHub UI - **AND** be clearly formatted #### Scenario: Actionable guidance + - **WHEN** docs sync check fails - **THEN** output SHALL provide actionable guidance - **AND** list specific documents to update ### Requirement: Configuration Management + The system SHALL manage CI configuration appropriately. #### Scenario: Configuration file updates + - **WHEN** CI integration is implemented - **THEN** configuration files SHALL be updated - **AND** changes SHALL be version controlled #### Scenario: Backward compatibility + - **WHEN** CI integration is implemented - **THEN** existing workflows SHALL not be disrupted - **AND** backward compatibility SHALL be maintained - diff --git a/openspec/specs/ci-log-artifacts/spec.md b/openspec/specs/ci-log-artifacts/spec.md index 4dbb5412..7e671b47 100644 --- a/openspec/specs/ci-log-artifacts/spec.md +++ b/openspec/specs/ci-log-artifacts/spec.md @@ -1,8 +1,11 @@ # ci-log-artifacts Specification ## Purpose + TBD - created by archiving change ci-01-pr-orchestrator-log-artifacts. Update Purpose after archive. + ## Requirements + ### Requirement: Full Test Logs from Smart-Test-Full in CI The PR orchestrator workflow SHALL run the full test suite via `hatch run smart-test-full` (or equivalent) so that test and coverage logs are written under `logs/tests/`, and those logs SHALL be uploaded as workflow artifacts so they can be downloaded when a run fails. @@ -91,4 +94,3 @@ The documentation SHALL describe where to find test and repro log artifacts in G - At least one doc page (e.g. `docs/guides/troubleshooting.md` or `docs/contributing/`) includes a subsection on CI artifacts - Section is copy-paste or link friendly (e.g. "Go to the run β†’ Artifacts β†’ download test-logs or repro-logs") - diff --git a/openspec/specs/clean-code-compliance-gate/spec.md b/openspec/specs/clean-code-compliance-gate/spec.md index 6caeaa24..dd12dd93 100644 --- a/openspec/specs/clean-code-compliance-gate/spec.md +++ b/openspec/specs/clean-code-compliance-gate/spec.md @@ -1,20 +1,25 @@ # clean-code-compliance-gate Specification ## Purpose + TBD - created by archiving change clean-code-01-principle-gates. Update Purpose after archive. + ## Requirements + ### Requirement: Clean-Code Compliance Gate + The repository SHALL consume the expanded review-module clean-code categories and block clean-code regressions before merge. #### Scenario: Repo review includes expanded clean-code categories + - **GIVEN** the review module exposes clean-code categories `naming`, `kiss`, `yagni`, `dry`, and `solid` - **WHEN** `specfact review` runs against specfact-cli in CI or local gated mode - **THEN** those categories are included in the review result - **AND** regressions in blocking clean-code rules fail the gated run #### Scenario: Zero-finding dogfood baseline stays a prerequisite + - **GIVEN** `code-review-zero-findings` has not yet reached its zero-finding proof - **WHEN** implementation work for this change is evaluated - **THEN** clean-code gating cannot be considered complete - **AND** the change remains blocked until the prerequisite evidence exists - diff --git a/openspec/specs/clean-code-loc-nesting-check/spec.md b/openspec/specs/clean-code-loc-nesting-check/spec.md index 123419bd..040b683c 100644 --- a/openspec/specs/clean-code-loc-nesting-check/spec.md +++ b/openspec/specs/clean-code-loc-nesting-check/spec.md @@ -1,20 +1,25 @@ # clean-code-loc-nesting-check Specification ## Purpose + TBD - created by archiving change clean-code-01-principle-gates. Update Purpose after archive. + ## Requirements + ### Requirement: Staged LOC, Nesting, and Parameter Checks + The repository SHALL adopt the expanded KISS metrics through a staged rollout that starts with the Phase A thresholds from the 2026-03-22 plan. #### Scenario: Phase A thresholds are enforced first + - **GIVEN** the clean-code review checks are enabled for specfact-cli - **WHEN** LOC-per-function findings are evaluated - **THEN** warning and error thresholds start at `>80` and `>120` - **AND** nesting-depth and parameter-count checks are active in the same review run #### Scenario: Phase B remains deferred until cleanup is complete + - **GIVEN** stricter LOC thresholds of `>40` and `>80` are planned - **WHEN** this change is implemented - **THEN** Phase B remains documented as a future tightening step - **AND** the current change does not silently promote Phase B to a hard gate - diff --git a/openspec/specs/clean-code-semgrep-rules/spec.md b/openspec/specs/clean-code-semgrep-rules/spec.md index 788d3cdf..5556b6f6 100644 --- a/openspec/specs/clean-code-semgrep-rules/spec.md +++ b/openspec/specs/clean-code-semgrep-rules/spec.md @@ -1,33 +1,41 @@ # clean-code-semgrep-rules Specification ## Purpose + TBD - created by archiving change code-review-05-semgrep-clean-code-rules. Update Purpose after archive. + ## Requirements + ### Requirement: Five Custom Semgrep Rules for Project-Specific Anti-Patterns + The system SHALL provide five semgrep rules covering get+modify in same method, unguarded nested attribute access, cross-layer calls, module-level network instantiation, and print() in src/. Each rule SHALL be validated with bad/good fixture pairs. #### Scenario: get+modify rule fires on combined read/write method + - **GIVEN** `bad_get_modify.py` contains a method that both reads and writes state - **WHEN** semgrep runs the `get-modify-same-method` rule on that file - **THEN** at least one match is reported #### Scenario: get+modify rule does not fire on separated methods + - **GIVEN** `good_get_modify.py` separates read and write into different methods - **WHEN** semgrep runs the rule - **THEN** no match is reported #### Scenario: Nested attribute access rule fires on unguarded a.b.c + - **GIVEN** `bad_nested_access.py` contains `result = obj.config.value` without None-check - **WHEN** semgrep runs the `unguarded-nested-access` rule - **THEN** a match is reported #### Scenario: Cross-layer rule fires on mixed repository and http_client calls + - **GIVEN** a function calls both `repository.find_by_id(...)` and `http_client.post(...)` - **WHEN** semgrep runs the `cross-layer-call` rule - **THEN** a match is reported #### Scenario: print-in-src rule fires on print() in src/ files + - **GIVEN** a file in `src/` contains `print("debug")` - **WHEN** semgrep runs the `print-in-src` rule - **THEN** a match is reported - diff --git a/openspec/specs/cli-output/spec.md b/openspec/specs/cli-output/spec.md index 30779c35..0deb9241 100644 --- a/openspec/specs/cli-output/spec.md +++ b/openspec/specs/cli-output/spec.md @@ -1,8 +1,11 @@ # cli-output Specification ## Purpose + TBD - created by archiving change enhance-cli-terminal-output. Update Purpose after archive. + ## Requirements + ### Requirement: Terminal Capability Detection The system SHALL detect terminal capabilities to determine appropriate output formatting. @@ -289,4 +292,3 @@ The project SHALL maintain one canonical section per released version and accura - **WHEN** maintainers review `CHANGELOG.md` - **THEN** there is a single `0.34.0` section - **AND** features shipped in that release are listed under that release (not left under `Unreleased`). - diff --git a/openspec/specs/cli-performance/spec.md b/openspec/specs/cli-performance/spec.md index b4fd7002..11922f87 100644 --- a/openspec/specs/cli-performance/spec.md +++ b/openspec/specs/cli-performance/spec.md @@ -1,8 +1,11 @@ # cli-performance Specification ## Purpose + TBD - created by archiving change optimize-startup-performance. Update Purpose after archive. + ## Requirements + ### Requirement: Metadata-Based Startup Check Optimization The CLI SHALL track version and check timestamps in metadata to optimize startup performance. @@ -117,4 +120,3 @@ The startup check execution logic SHALL be conditional based on metadata. - **When** `print_startup_checks()` is called - **Then** checks are executed only when metadata conditions are met - **And** metadata is updated after checks complete - diff --git a/openspec/specs/code-review-module/spec.md b/openspec/specs/code-review-module/spec.md index e2ecf931..87763d13 100644 --- a/openspec/specs/code-review-module/spec.md +++ b/openspec/specs/code-review-module/spec.md @@ -1,56 +1,69 @@ # code-review-module Specification ## Purpose + TBD - created by archiving change code-review-01-module-scaffold. Update Purpose after archive. + ## Requirements + ### Requirement: Code Review Module Registration + The `nold-ai/specfact-code-review` module SHALL be installable and extend `specfact code` with a `review` subgroup exposing `run`, `ledger`, and `rules` subcommands. #### Scenario: Module install surfaces review subgroup + - **GIVEN** the module is installed via `specfact module install nold-ai/specfact-code-review` - **WHEN** the user runs `specfact code --help` - **THEN** a `review` subgroup appears in the command list - **AND** `specfact code review --help` shows `run`, `ledger`, and `rules` subcommands #### Scenario: module-package.yaml has required fields + - **GIVEN** `packages/specfact-code-review/module-package.yaml` exists - **WHEN** the module loader parses it - **THEN** `bundle_group_command` equals `code`, `tier` equals `official`, `name` equals `nold-ai/specfact-code-review` - **AND** `core_compatibility` matches `>=0.40.0,<1.0.0` #### Scenario: Module not installed produces no surface + - **GIVEN** the module is NOT installed - **WHEN** the user runs `specfact code --help` - **THEN** no `review` subgroup appears and no error is raised #### Scenario: Duplicate install is idempotent + - **GIVEN** the module is already installed - **WHEN** the user installs it again - **THEN** no duplicate `review` entries appear in `specfact code --help` ### Requirement: Self-referential scan β€” review module can scan itself without errors + The `specfact-code-review` module SHALL be able to review its own source files (including the files that implement the reviewer) without infinite loops, false positives from meta-scanning, or unhandled exceptions. #### Scenario: Review run on specfact-cli repo completes without tool_error findings + - **WHEN** `specfact review` is run with the specfact-cli repo as the target - **THEN** no finding with `tool` equal to `code-review-module` or `category` equal to `tool_error` is produced - **AND** the run exits with code 0 (assuming all other findings are resolved) #### Scenario: Tool error finding is surfaced as error severity + - **WHEN** any configured tool fails to invoke (e.g., missing binary) - **THEN** a finding with `category="tool_error"` and `severity="error"` is produced - **AND** the finding message includes the tool name and failure reason ### Requirement: CI gate integration β€” review must be runnable non-interactively + The review module SHALL support a `--ci` or equivalent non-interactive flag that suppresses prompts, writes machine-readable output to `.specfact/code-review.json`, and exits with code 1 on any finding at severity `error` or higher. #### Scenario: Non-interactive CI run writes JSON report and exits non-zero on errors + - **WHEN** `specfact review run --ci` is executed and error-severity findings exist - **THEN** `.specfact/code-review.json` is written with `overall_verdict: "FAIL"` and `ci_exit_code: 1` - **AND** the process exits with code 1 #### Scenario: Non-interactive CI run exits zero on clean codebase + - **WHEN** `specfact review run --ci` is executed and no findings exist - **THEN** `.specfact/code-review.json` is written with `overall_verdict: "PASS"` and `ci_exit_code: 0` - **AND** the process exits with code 0 - diff --git a/openspec/specs/codebase-validation-depth/spec.md b/openspec/specs/codebase-validation-depth/spec.md index 6268af06..807f89f9 100644 --- a/openspec/specs/codebase-validation-depth/spec.md +++ b/openspec/specs/codebase-validation-depth/spec.md @@ -1,8 +1,11 @@ # codebase-validation-depth Specification ## Purpose + TBD - created by archiving change validation-01-deep-validation. Update Purpose after archive. + ## Requirements + ### Requirement: Sidecar Validation for Unmodified Code The CLI SHALL support thorough in-depth validation of a target repository without modifying the target's source (sidecar mode). @@ -130,4 +133,3 @@ The documentation SHALL describe three validation modes: (1) quick check (repro) - Single reference section or guide covering all three use cases - Commands are copy-pasteable; any required env or config is stated - Link from README or getting-started to this section where appropriate - diff --git a/openspec/specs/command-package-runtime-validation/spec.md b/openspec/specs/command-package-runtime-validation/spec.md index bcc33085..ff082599 100644 --- a/openspec/specs/command-package-runtime-validation/spec.md +++ b/openspec/specs/command-package-runtime-validation/spec.md @@ -1,9 +1,11 @@ # command-package-runtime-validation Specification ## Purpose + TBD - created by archiving change cli-val-07-command-package-runtime-validation. Update Purpose after archive. ## Requirements + ### Requirement: Command Inventory Covers Core And Official Bundles The system SHALL derive a validation inventory that covers the released core commands and every official command package shipped from `specfact-cli-modules`. diff --git a/openspec/specs/command-registry/spec.md b/openspec/specs/command-registry/spec.md index 3619afd2..196e287c 100644 --- a/openspec/specs/command-registry/spec.md +++ b/openspec/specs/command-registry/spec.md @@ -1,8 +1,11 @@ # command-registry Specification ## Purpose + TBD - created by archiving change arch-01-cli-modular-command-registry. Update Purpose after archive. + ## Requirements + ### Requirement: CommandRegistry with Lazy Load The CLI SHALL provide a **CommandRegistry** that registers command groups by name with a loader and metadata, and resolves the Typer app only when requested (lazy load). @@ -56,4 +59,3 @@ The CLI SHALL support a **CommandMetadata** model (or equivalent dict schema) wi - Metadata is stored at registration time - Accessing metadata does not trigger module load - diff --git a/openspec/specs/confidence-scoring/spec.md b/openspec/specs/confidence-scoring/spec.md index ea75de32..69acf8af 100644 --- a/openspec/specs/confidence-scoring/spec.md +++ b/openspec/specs/confidence-scoring/spec.md @@ -1,8 +1,11 @@ # confidence-scoring Specification ## Purpose + TBD - created by archiving change bundle-mapper-01-mapping-strategy. Update Purpose after archive. + ## Requirements + ### Requirement: Explicit Label Signal The system SHALL score explicit bundle labels (e.g., "bundle:xyz", "project:abc") with highest priority and 100% confidence when bundle exists. @@ -106,4 +109,3 @@ The system SHALL use configurable confidence thresholds for routing decisions. - **WHEN** config contains non-numeric threshold values - **THEN** mapper initialization does not fail - **AND** default threshold values are used - diff --git a/openspec/specs/contract-runner/spec.md b/openspec/specs/contract-runner/spec.md index 6c12b6eb..aff99851 100644 --- a/openspec/specs/contract-runner/spec.md +++ b/openspec/specs/contract-runner/spec.md @@ -1,33 +1,41 @@ # contract-runner Specification ## Purpose + TBD - created by archiving change code-review-04-contract-test-runners. Update Purpose after archive. + ## Requirements + ### Requirement: icontract Decorator AST Scan and CrossHair Fast Pass + The system SHALL AST-scan changed Python files for public functions missing `@require`/`@ensure` decorators, and run CrossHair (2s/path timeout) for counterexample discovery. #### Scenario: Public function without @require produces contracts finding + - **GIVEN** a Python file with `def process_data(x):` without icontract decorators - **WHEN** `run_contract_check(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `category="contracts"` and `severity="warning"` #### Scenario: Public function with decorators produces no finding + - **GIVEN** a file with a public function decorated with both `@require` and `@ensure` - **WHEN** `run_contract_check(files=[...])` is called - **THEN** no contract-related finding is returned for that function #### Scenario: Private functions excluded from scan + - **GIVEN** a file with `def _private_helper(x):` without decorators - **WHEN** `run_contract_check(files=[...])` is called - **THEN** no finding is produced for `_private_helper` #### Scenario: CrossHair counterexample maps to contracts warning + - **GIVEN** CrossHair finds a counterexample for a function - **WHEN** `run_contract_check(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `category="contracts"`, `severity="warning"`, `tool="crosshair"` #### Scenario: CrossHair timeout or unavailability degrades gracefully + - **GIVEN** CrossHair hits the 2s timeout or is not installed - **WHEN** `run_contract_check(files=[...])` is called - **THEN** the AST scan still runs and no exception propagates - diff --git a/openspec/specs/core-cli-reference/spec.md b/openspec/specs/core-cli-reference/spec.md index a7e25be0..c1d234ab 100644 --- a/openspec/specs/core-cli-reference/spec.md +++ b/openspec/specs/core-cli-reference/spec.md @@ -1,8 +1,11 @@ # core-cli-reference Specification ## Purpose + TBD - created by archiving change docs-05-core-site-ia-restructure. Update Purpose after archive. + ## Requirements + ### Requirement: Core CLI reference pages exist The system SHALL provide dedicated reference pages for core CLI commands. @@ -26,4 +29,3 @@ The system SHALL provide dedicated reference pages for core CLI commands. - **GIVEN** the docs/core-cli/upgrade.md page exists - **WHEN** a user reads the page - **THEN** it documents the specfact upgrade command and its options - diff --git a/openspec/specs/core-decoupling-cleanup/spec.md b/openspec/specs/core-decoupling-cleanup/spec.md index fa2298b9..1d6910f6 100644 --- a/openspec/specs/core-decoupling-cleanup/spec.md +++ b/openspec/specs/core-decoupling-cleanup/spec.md @@ -1,8 +1,11 @@ # core-decoupling-cleanup Specification ## Purpose + TBD - created by archiving change module-migration-06-core-decoupling-cleanup. Update Purpose after archive. + ## Requirements + ### Requirement: Core Package Ownership Boundary The `specfact-cli` core package SHALL include only components required for permanent core runtime responsibilities and SHALL not retain bundle-only implementation structures after module extraction/slimming. @@ -66,4 +69,3 @@ Package-specific artifacts not required by CLI core SHALL be removed from specfa - **GIVEN** `sync_runtime` implementation is owned by `specfact-project` in specfact-cli-modules - **WHEN** decoupling migration updates test ownership - **THEN** legacy core tests under `specfact-cli/tests/unit/sync/` are migrated to `specfact-cli-modules/tests/unit/specfact_project/sync_runtime/` and core boundary test `test_core_repo_does_not_host_sync_runtime_unit_tests` enforces this. - diff --git a/openspec/specs/core-docs-client-search/spec.md b/openspec/specs/core-docs-client-search/spec.md index cf6dcdd7..29a19ad6 100644 --- a/openspec/specs/core-docs-client-search/spec.md +++ b/openspec/specs/core-docs-client-search/spec.md @@ -1,8 +1,11 @@ # core-docs-client-search Specification ## Purpose + TBD - created by archiving change docs-13-core-nav-search-theme-roles. Update Purpose after archive. + ## Requirements + ### Requirement: Core docs pages SHALL be searchable from a client-side index Core docs pages SHALL be searchable from the site experience using a client-side index built from repository content. @@ -20,4 +23,3 @@ Core docs pages SHALL be searchable from the site experience using a client-side - **WHEN** the index is built - **THEN** it only includes pages owned by the core docs site - **AND** it does not present module-site pages as if they were local core pages - diff --git a/openspec/specs/core-docs-data-driven-nav/spec.md b/openspec/specs/core-docs-data-driven-nav/spec.md index 39d65b56..4ac8fecd 100644 --- a/openspec/specs/core-docs-data-driven-nav/spec.md +++ b/openspec/specs/core-docs-data-driven-nav/spec.md @@ -1,8 +1,11 @@ # core-docs-data-driven-nav Specification ## Purpose + TBD - created by archiving change docs-13-core-nav-search-theme-roles. Update Purpose after archive. + ## Requirements + ### Requirement: Core docs navigation SHALL render from structured navigation data Core docs navigation SHALL be rendered from a structured data source rather than duplicated as hardcoded sidebar markup. @@ -19,4 +22,3 @@ Core docs navigation SHALL be rendered from a structured data source rather than - **GIVEN** a core docs section link changes - **WHEN** the navigation data source is updated - **THEN** the sidebar rendering reflects the change without duplicating the link structure in hardcoded template markup - diff --git a/openspec/specs/core-docs-expertise-paths/spec.md b/openspec/specs/core-docs-expertise-paths/spec.md index 3480a307..59e0d711 100644 --- a/openspec/specs/core-docs-expertise-paths/spec.md +++ b/openspec/specs/core-docs-expertise-paths/spec.md @@ -1,8 +1,11 @@ # core-docs-expertise-paths Specification ## Purpose + TBD - created by archiving change docs-13-core-nav-search-theme-roles. Update Purpose after archive. + ## Requirements + ### Requirement: Core docs SHALL expose expertise-aware or role-aware entry paths Core docs SHALL expose expertise-aware or role-aware paths that help users find the right entry points for their current level. @@ -20,4 +23,3 @@ Core docs SHALL expose expertise-aware or role-aware paths that help users find - **WHEN** a new or returning user arrives at the site - **THEN** the page highlights clear core-docs starting paths by task or audience - **AND** any module-specific depth continues to link out to the modules site rather than being duplicated in core - diff --git a/openspec/specs/core-docs-progressive-nav/spec.md b/openspec/specs/core-docs-progressive-nav/spec.md index bcf9ae5a..481af8ec 100644 --- a/openspec/specs/core-docs-progressive-nav/spec.md +++ b/openspec/specs/core-docs-progressive-nav/spec.md @@ -1,8 +1,11 @@ # core-docs-progressive-nav Specification ## Purpose + TBD - created by archiving change docs-05-core-site-ia-restructure. Update Purpose after archive. + ## Requirements + ### Requirement: Sidebar provides 6-section progressive navigation The system SHALL provide a sidebar with 6 sections in a specific order. @@ -31,4 +34,3 @@ The system SHALL provide a sidebar with 6 sections in a specific order. - **GIVEN** a file was moved from its old location - **WHEN** a user visits the old URL - **THEN** they are redirected to the new URL via jekyll-redirect-from - diff --git a/openspec/specs/core-docs-theme-toggle/spec.md b/openspec/specs/core-docs-theme-toggle/spec.md index d193f7a6..1940d450 100644 --- a/openspec/specs/core-docs-theme-toggle/spec.md +++ b/openspec/specs/core-docs-theme-toggle/spec.md @@ -1,8 +1,11 @@ # core-docs-theme-toggle Specification ## Purpose + TBD - created by archiving change docs-13-core-nav-search-theme-roles. Update Purpose after archive. + ## Requirements + ### Requirement: Core docs SHALL support a persisted light/dark theme toggle Core docs SHALL support a persisted light/dark theme toggle for the docs site shell. @@ -20,4 +23,3 @@ Core docs SHALL support a persisted light/dark theme toggle for the docs site sh - **WHEN** the user reads the site in either theme - **THEN** text, navigation, and code blocks remain readable - **AND** the shell styling does not obscure canonical links or content hierarchy - diff --git a/openspec/specs/core-lean-package/spec.md b/openspec/specs/core-lean-package/spec.md index 96b9a904..a3cdc6c6 100644 --- a/openspec/specs/core-lean-package/spec.md +++ b/openspec/specs/core-lean-package/spec.md @@ -1,8 +1,11 @@ # core-lean-package Specification ## Purpose + TBD - created by archiving change module-migration-03-core-slimming. Update Purpose after archive. + ## Requirements + ### Requirement: The installed specfact-cli wheel contains only the 3 core module directories in this change After this change, the `specfact-cli` wheel SHALL include module source only for: `init`, `module_registry`, `upgrade`. The auth module directory and the remaining 17 extracted module directories (project, plan, import_cmd, sync, migrate, backlog, policy_engine, analyze, drift, validate, repro, contract, spec, sdd, generate, enforce, patch_mode) SHALL NOT be present in the installed package. @@ -99,4 +102,3 @@ The `src/specfact_cli/cli.py` and registry SHALL mount category group Typer apps - **WHEN** any of the 21 original module commands is invoked via its category group path - **THEN** the command SHALL execute successfully - **AND** no command SHALL be permanently lost β€” only the routing has changed from flat to category-scoped - diff --git a/openspec/specs/core-module-isolation/spec.md b/openspec/specs/core-module-isolation/spec.md index b5a72e81..1796147c 100644 --- a/openspec/specs/core-module-isolation/spec.md +++ b/openspec/specs/core-module-isolation/spec.md @@ -1,28 +1,35 @@ # core-module-isolation Specification ## Purpose + TBD - created by archiving change arch-04-core-contracts-interfaces. Update Purpose after archive. + ## Requirements + ### Requirement: Static analysis test enforces zero core-to-module imports The system SHALL provide a pytest test `tests/unit/test_core_module_isolation.py` that parses AST of core CLI code and fails if any import from `specfact_cli.modules.*` is found. #### Scenario: Test scans core directories for module imports + - **WHEN** test_core_has_no_module_imports runs - **THEN** it SHALL scan all Python files in: cli.py, registry/, models/, utils/, contracts/ - **AND** SHALL parse each file's AST looking for Import and ImportFrom nodes #### Scenario: Test fails on direct module import + - **WHEN** core code contains `import specfact_cli.modules.backlog` - **THEN** test SHALL fail with message: ": imports specfact_cli.modules.backlog" - **AND** SHALL list exact file path and line number #### Scenario: Test fails on from-import of module code + - **WHEN** core code contains `from specfact_cli.modules.sync.src import commands` - **THEN** test SHALL fail with message: ": imports from specfact_cli.modules.sync.src" - **AND** SHALL prevent PR merge via CI #### Scenario: Test allows non-module imports + - **WHEN** core code contains `import specfact_cli.models` - **THEN** test SHALL pass - **AND** SHALL NOT flag imports from non-module core directories @@ -32,11 +39,13 @@ The system SHALL provide a pytest test `tests/unit/test_core_module_isolation.py The system SHALL exclude imports within `if TYPE_CHECKING:` blocks from static analysis violations. #### Scenario: Type hint import in TYPE_CHECKING block is allowed + - **WHEN** core code has `if TYPE_CHECKING: from specfact_cli.modules.backlog import BacklogAdapter` - **THEN** test SHALL pass - **AND** SHALL NOT flag as violation since it's only for type checking #### Scenario: Runtime import disguised as TYPE_CHECKING is caught + - **WHEN** code uses module import outside TYPE_CHECKING but has TYPE_CHECKING block elsewhere - **THEN** test SHALL still fail on the runtime import - **AND** SHALL distinguish between conditional type imports and runtime imports @@ -46,11 +55,13 @@ The system SHALL exclude imports within `if TYPE_CHECKING:` blocks from static a The system SHALL run `test_core_module_isolation.py` in `.github/workflows/tests.yml` and block PRs that violate core isolation. #### Scenario: CI runs isolation test on every PR + - **WHEN** PR is opened with core code changes - **THEN** GitHub Actions SHALL run `pytest tests/unit/test_core_module_isolation.py` - **AND** SHALL block merge if test fails #### Scenario: CI provides actionable error message on violation + - **WHEN** isolation test fails in CI - **THEN** GitHub Actions log SHALL show file path, line number, and import statement - **AND** SHALL guide developer to use registry pattern instead of direct import @@ -60,11 +71,13 @@ The system SHALL run `test_core_module_isolation.py` in `.github/workflows/tests The system SHALL format violation messages with file path, line number, and imported module name for easy debugging. #### Scenario: Violation message includes context + - **WHEN** violation is detected at src/specfact_cli/cli.py line 42 - **THEN** message SHALL be: "src/specfact_cli/cli.py:42 imports specfact_cli.modules.backlog.src.commands" - **AND** SHALL aggregate all violations before failing (not fail on first) #### Scenario: Multiple violations are reported together + - **WHEN** multiple core files import from modules - **THEN** test SHALL list all violations in a single failure message - **AND** SHALL show total count: "Found 3 core-to-module import violations" @@ -74,11 +87,13 @@ The system SHALL format violation messages with file path, line number, and impo The system SHALL ensure the static analysis test completes in under 2 seconds and requires no external dependencies beyond Python standard library. #### Scenario: Test parses AST efficiently + - **WHEN** test runs on full codebase - **THEN** it SHALL complete within 2 seconds - **AND** SHALL use ast.parse() from standard library (no external parsers) #### Scenario: Test core directories are configurable + - **WHEN** new core directories are added (e.g., contracts/) - **THEN** test SHALL have a CORE_DIRS constant at top of file - **AND** SHALL be easily updated by adding new Path to the list @@ -88,16 +103,19 @@ The system SHALL ensure the static analysis test completes in under 2 seconds an The system SHALL enforce that core CLI accesses modules only via CommandRegistry lazy loading, never via direct imports. #### Scenario: Core uses registry for module access + - **WHEN** core CLI needs to invoke a module command - **THEN** it SHALL call `CommandRegistry.get_typer(command_name)` - **AND** SHALL NOT import module code directly #### Scenario: Module code is loaded lazily on demand + - **WHEN** CommandRegistry.get_typer() is called - **THEN** module SHALL be loaded via importlib.util.module_from_spec - **AND** SHALL NOT be imported at CLI startup #### Scenario: Core-to-registry is allowed import + - **WHEN** core CLI imports from `specfact_cli.registry` - **THEN** static analysis test SHALL pass - **AND** SHALL distinguish between registry access (allowed) and module access (forbidden) @@ -107,12 +125,13 @@ The system SHALL enforce that core CLI accesses modules only via CommandRegistry The system SHALL document the core-module isolation principle in `docs/reference/module-contracts.md` for 3rd-party module developers. #### Scenario: Docs explain inversion-of-control architecture + - **WHEN** developer reads module-contracts.md - **THEN** docs SHALL explain that core never imports modules - **AND** SHALL illustrate registry pattern with code examples #### Scenario: Docs guide module developers on protocol implementation + - **WHEN** developer wants to create a marketplace module - **THEN** docs SHALL show how to implement ModuleIOContract - **AND** SHALL clarify that modules are discovered and loaded, not imported by core - diff --git a/openspec/specs/cross-repo-backlog-alignment/spec.md b/openspec/specs/cross-repo-backlog-alignment/spec.md index 27b72595..26b7f075 100644 --- a/openspec/specs/cross-repo-backlog-alignment/spec.md +++ b/openspec/specs/cross-repo-backlog-alignment/spec.md @@ -1,51 +1,64 @@ # cross-repo-backlog-alignment Specification ## Purpose + TBD - created by archiving change cross-repo-issue-realignment. Update Purpose after archive. + ## Requirements + ### Requirement: Active Change Ownership Must Be Classified Against The Canonical Repo Split + The system SHALL classify every active OpenSpec change and linked GitHub user story in `specfact-cli` against the canonical post-migration ownership model before further implementation work proceeds. #### Scenario: Active change is classified as core, modules, or split + - **WHEN** maintainers review active changes that still reference the pre-split monolithic structure - **THEN** each change is assigned one of: `core`, `modules`, or `split/rescope` - **AND** the decision is based on the canonical ownership specs rather than on stale proposal paths or legacy issue location. ### Requirement: Module-Owned Issues Must Use A Defined Reassignment Path + The system SHALL define a deterministic path for any GitHub issue that belongs in `specfact-cli-modules` instead of `specfact-cli`. #### Scenario: Native issue transfer is available and accepted + - **WHEN** a module-owned issue can be moved between the two repositories with acceptable metadata preservation - **THEN** the issue is transferred to `nold-ai/specfact-cli-modules` - **AND** the related OpenSpec artifacts and planning inventory are updated to reference the transferred issue in its new repository. #### Scenario: Native issue transfer is unavailable or unsuitable + - **WHEN** a module-owned issue cannot be moved cleanly between repositories - **THEN** the source issue in `specfact-cli` is closed with a comment pointing to the replacement issue in `specfact-cli-modules` - **AND** a replacement issue is created in `specfact-cli-modules` with updated scope aligned to the current architecture - **AND** the old and new issues cross-reference each other so planning history remains auditable. ### Requirement: Target Hierarchy Must Exist Before Module-Owned Stories Are Reassigned + The system SHALL establish the necessary Epic and Feature parents in `specfact-cli-modules` before module-owned user stories are transferred or recreated there. #### Scenario: Module-owned user story is prepared for reassignment + - **WHEN** a user story is classified as module-owned - **THEN** the target repository already has the Epic and Feature hierarchy needed to parent that story - **AND** the reassigned story is linked under the target Feature rather than being left as a flat issue. ### Requirement: Planning Inventories Must Be Updated In Both Repositories + The system SHALL keep planning metadata aligned across repositories when change ownership or GitHub issue ownership changes. #### Scenario: Change ownership is reassigned to modules repo + - **WHEN** a change or linked issue moves from `specfact-cli` planning to `specfact-cli-modules` - **THEN** `openspec/CHANGE_ORDER.md` in `specfact-cli` is updated to remove or annotate the old core-repo ownership - **AND** the corresponding planning inventory in `specfact-cli-modules` is updated with the new issue, Epic, Feature, and dependency references. ### Requirement: Rescoped Proposals Must State The Current Architecture And Repo Assignment + The system SHALL update affected active proposals before implementation resumes so they no longer describe obsolete monolithic paths or incorrect repository ownership. #### Scenario: Active proposal still references in-repo module implementation + - **WHEN** an active proposal describes implementation under `modules//` in `specfact-cli` for a module-owned capability - **THEN** the proposal is updated to either reflect its true core-only scope or to point to the owning implementation work in `specfact-cli-modules` - **AND** the proposal no longer implies that bundle-owned behavior will be implemented in the core repo. - diff --git a/openspec/specs/custom-registries/spec.md b/openspec/specs/custom-registries/spec.md index 8809d99c..eb244199 100644 --- a/openspec/specs/custom-registries/spec.md +++ b/openspec/specs/custom-registries/spec.md @@ -1,24 +1,30 @@ # custom-registries Specification ## Purpose + TBD - created by archiving change marketplace-02-advanced-marketplace-features. Update Purpose after archive. + ## Requirements + ### Requirement: Support multiple registries with priority ordering The system SHALL manage multiple registries with configurable priority and trust levels. #### Scenario: Add custom registry + - **WHEN** user runs `specfact module add-registry https://registry.company.com/index.json --id enterprise` - **THEN** system SHALL add registry to ~/.specfact/config/registries.yaml - **AND** SHALL assign next priority number - **AND** SHALL set trust level to "prompt" by default #### Scenario: List registries + - **WHEN** user runs `specfact module list-registries` - **THEN** system SHALL display all configured registries - **AND** SHALL show: id, url, priority, trust level #### Scenario: Remove registry + - **WHEN** user runs `specfact module remove-registry enterprise` - **THEN** system SHALL remove registry from config - **AND** SHALL NOT affect modules already installed from that registry @@ -28,6 +34,7 @@ The system SHALL manage multiple registries with configurable priority and trust The system SHALL search across all configured registries in priority order. #### Scenario: Search returns results from multiple registries + - **WHEN** user runs `specfact module search backlog` - **THEN** system SHALL fetch indexes from all registries - **AND** SHALL aggregate results @@ -38,11 +45,12 @@ The system SHALL search across all configured registries in priority order. The system SHALL enforce trust levels during module installation. #### Scenario: Install from trusted registry (always) + - **WHEN** installing module from trust=always registry - **THEN** system SHALL proceed without prompt #### Scenario: Install from untrusted registry (prompt) + - **WHEN** installing module from trust=prompt registry - **THEN** system SHALL display warning and registry info - **AND** SHALL prompt user for confirmation - diff --git a/openspec/specs/daily-standup/spec.md b/openspec/specs/daily-standup/spec.md index adbd922a..d04b9f4a 100644 --- a/openspec/specs/daily-standup/spec.md +++ b/openspec/specs/daily-standup/spec.md @@ -1,8 +1,11 @@ # daily-standup Specification ## Purpose + TBD - created by archiving change daily-standup-progress-support. Update Purpose after archive. + ## Requirements + ### Requirement: Standup view The system SHALL provide a standup or progress view that lists change proposals or backlog items (by assignee or filter) with last-updated and status, and optional one-line summary for yesterday/today/blockers. @@ -283,6 +286,7 @@ The system SHALL provide a prompt file (e.g. `resources/prompts/specfact.backlog The system SHALL support a `--summarize` flag on `specfact backlog daily` that produces a **prompt** (instructions plus applied filters and filtered standup output) suitable for use in an interactive slash command (e.g. `specfact.daily`) or copy-paste to Copilot, so an LLM can generate a meaningful **summary of the daily standup status**. The prompt content for item bodies and comments SHALL be provided as normalized Markdown text only (no raw HTML tags or entities), regardless of how the underlying provider stores or formats those fields. #### Scenario: --summarize outputs prompt with filters and data (Markdown-only content) + - **Given**: Backlog items in the current scope (same as standup: state, iteration/sprint, assignee, limit) and the user runs `specfact backlog daily --summarize` (stdout) or `--summarize-to ` (write to file) - **When**: The command runs with the same filters as the standup view - **Then**: The system outputs (to stdout or to the given path) a prompt that includes: (1) brief instruction that the following data is the current standup view and the LLM should generate a concise standup summary; (2) the applied filter context (adapter, state, sprint, assignee, limit); (3) per-item data including **body (description)** and **comments (annotations)** when available, plus ID, title, status, assignees, last updated, progress, blockers, optional value score, so the LLM can produce a **meaningful** summary @@ -555,4 +559,3 @@ The system SHALL allow posting standup comments directly from the interactive re - Interactive navigation includes a post action for the selected story. - Empty post input is rejected with a clear message. - Posting uses existing standup comment format and adapter capability checks. - diff --git a/openspec/specs/data-models/spec.md b/openspec/specs/data-models/spec.md index 0af9b5ca..774b85e5 100644 --- a/openspec/specs/data-models/spec.md +++ b/openspec/specs/data-models/spec.md @@ -1,8 +1,11 @@ # data-models Specification ## Purpose + TBD - created by archiving change add-change-tracking-datamodel. Update Purpose after archive. + ## Requirements + ### Requirement: Change Tracking Models The system SHALL provide tool-agnostic change tracking models to support delta spec tracking (ADDED/MODIFIED/REMOVED) and change proposals. @@ -193,4 +196,3 @@ The system SHALL ensure change tracking models are tool-agnostic and accessed vi - **THEN** same models work for Linear - **AND** Linear-specific metadata stored in `source_tracking` - **AND** no model changes required - diff --git a/openspec/specs/debug-logging/spec.md b/openspec/specs/debug-logging/spec.md index a6ed8be9..647fd7e9 100644 --- a/openspec/specs/debug-logging/spec.md +++ b/openspec/specs/debug-logging/spec.md @@ -1,8 +1,11 @@ # debug-logging Specification ## Purpose + TBD - created by archiving change add-debug-logs-specfact-home. Update Purpose after archive. + ## Requirements + ### Requirement: User-level debug log directory The system SHALL provide a user-level directory for debug logs when debug mode is enabled. @@ -156,23 +159,27 @@ The system SHALL keep raw internal runtime diagnostics out of normal command out - **AND** only explicitly formatted user-facing warnings remain visible when the user must take action. ### Requirement: Bridge logger used in all production source paths + Every production code path in `src/specfact_cli/` SHALL use `get_bridge_logger()` from `specfact_cli.common` for all diagnostic output, replacing any remaining `print()` builtin calls. #### Scenario: Adapter module writes diagnostic output via bridge logger + - **WHEN** an adapter module (e.g., `adapters/ado.py`, `adapters/github.py`) performs a network call or state change - **THEN** diagnostic messages are written via `logger = get_bridge_logger(__name__)` and `logger.debug(...)` / `logger.info(...)` - **AND** no `print()` call appears in the adapter module #### Scenario: Sync module writes diagnostic output via bridge logger + - **WHEN** `sync/bridge_sync.py` or `sync/spec_to_code.py` processes a file - **THEN** all progress and error messages are routed through `get_bridge_logger(__name__)` - **AND** no `print()` call appears in the sync module ### Requirement: Script-layer logging uses stdlib or Rich, not print() + Scripts in `scripts/` and `tools/` that run as standalone CLI programs SHALL use `logging.getLogger(__name__)` with a `StreamHandler` for progress output, or `rich.console.Console()` for formatted terminal output. The stdlib `print()` builtin SHALL NOT be used. #### Scenario: Standalone script writes progress via logging + - **WHEN** a script in `scripts/` needs to write a status message to stdout - **THEN** it calls `logging.getLogger(__name__).info(...)` or `console.print(...)` from a Rich Console instance - **AND** semgrep `print-in-src` reports zero findings for that script - diff --git a/openspec/specs/dependency-decoupling/spec.md b/openspec/specs/dependency-decoupling/spec.md index c988874b..65c1fabc 100644 --- a/openspec/specs/dependency-decoupling/spec.md +++ b/openspec/specs/dependency-decoupling/spec.md @@ -1,8 +1,11 @@ # dependency-decoupling Specification ## Purpose + TBD - created by archiving change module-migration-02-bundle-extraction. Update Purpose after archive. + ## Requirements + ### Requirement: No hardcoded imports of module-only specfact_cli code Bundles in specfact-cli-modules SHALL NOT import from `specfact_cli.*` submodules that are used exclusively by bundle code. Such code SHALL be migrated to the appropriate bundle or a shared package in specfact-cli-modules. @@ -29,4 +32,3 @@ Bundles in specfact-cli-modules SHALL NOT import from `specfact_cli.*` submodule - **THEN** a check SHALL run that scans bundle code for `from specfact_cli.* import` - **AND** the check SHALL fail if any import is not in the allowed (CORE) list - **AND** `ALLOWED_IMPORTS.md` (or equivalent) SHALL document the allowed set - diff --git a/openspec/specs/dependency-resolution/spec.md b/openspec/specs/dependency-resolution/spec.md index 97de2c0d..da037959 100644 --- a/openspec/specs/dependency-resolution/spec.md +++ b/openspec/specs/dependency-resolution/spec.md @@ -1,19 +1,24 @@ # dependency-resolution Specification ## Purpose + TBD - created by archiving change marketplace-02-advanced-marketplace-features. Update Purpose after archive. + ## Requirements + ### Requirement: Resolve pip dependencies across all modules The system SHALL aggregate pip_dependencies from all installed modules and resolve constraints using pip-compile or fallback resolver. #### Scenario: Dependencies resolved without conflicts + - **WHEN** module installation triggers dependency resolution - **THEN** system SHALL collect pip_dependencies from all modules - **AND** SHALL resolve constraints using pip-compile - **AND** SHALL return list of resolved package versions #### Scenario: Dependency conflict detected + - **WHEN** new module introduces conflicting pip dependency - **THEN** system SHALL detect conflict before installation - **AND** SHALL display error with conflicting packages and versions @@ -21,6 +26,7 @@ The system SHALL aggregate pip_dependencies from all installed modules and resol - **AND** SHALL NOT proceed with installation #### Scenario: Fallback to basic pip resolver + - **WHEN** pip-tools is not available - **THEN** system SHALL log warning "pip-tools not found, using basic resolver" - **AND** SHALL attempt resolution with pip's built-in resolver @@ -31,6 +37,7 @@ The system SHALL aggregate pip_dependencies from all installed modules and resol The system SHALL extend install command to resolve dependencies as pre-flight check. #### Scenario: Install with dependency resolution + - **WHEN** user runs `specfact module install X` - **THEN** system SHALL download module metadata - **AND** SHALL simulate: all_modules = current + X @@ -38,6 +45,7 @@ The system SHALL extend install command to resolve dependencies as pre-flight ch - **AND** SHALL proceed only if resolution succeeds #### Scenario: Skip dependency resolution with flag + - **WHEN** user runs `specfact module install X --skip-deps` - **THEN** system SHALL skip dependency resolution - **AND** SHALL install module and its pip_dependencies independently @@ -48,6 +56,7 @@ The system SHALL extend install command to resolve dependencies as pre-flight ch The system SHALL provide actionable error messages when dependency conflicts occur. #### Scenario: Conflict error message format + - **WHEN** dependency conflict is detected - **THEN** error SHALL include: conflicting packages, required versions, affected modules - **AND** SHALL suggest: uninstall conflicting module, use --force, or skip conflicting module @@ -62,11 +71,13 @@ SHALL handle both forms. #### Scenario: Registry entry declares a versioned bundle dependency - **GIVEN** a registry entry with: + ```json "bundle_dependencies": [ {"id": "nold-ai/specfact-project", "version": ">=0.41.0"} ] ``` + - **WHEN** the installer processes this entry - **THEN** the installer SHALL treat `nold-ai/specfact-project` as a required dependency with the constraint `>=0.41.0` @@ -82,6 +93,7 @@ SHALL handle both forms. During `specfact module install`, the system SHALL resolve all `bundle_dependencies` from both the registry index and the module's `module-package.yaml` manifest. For each dependency: + - If the dependency is not installed, the CLI SHALL prompt the user to install it - If the dependency is installed but its version does not satisfy the declared specifier, the CLI SHALL prompt the user to upgrade it @@ -210,10 +222,11 @@ resolution plan without performing any installs or upgrades. - **WHEN** user runs `specfact module install A --dry-run` - **THEN** the CLI SHALL print a dependency plan: + ``` Would install: nold-ai/specfact-project 0.41.2 (required by A >=0.41.0) nold-ai/A 0.42.0 ``` -- **AND** SHALL exit 0 without modifying any installed modules +- **AND** SHALL exit 0 without modifying any installed modules diff --git a/openspec/specs/devops-sync/spec.md b/openspec/specs/devops-sync/spec.md index b2e85c73..d38e7257 100644 --- a/openspec/specs/devops-sync/spec.md +++ b/openspec/specs/devops-sync/spec.md @@ -1,8 +1,11 @@ # devops-sync Specification ## Purpose + TBD - created by archiving change add-devops-backlog-tracking. Update Purpose after archive. + ## Requirements + ### Requirement: GitHub Issue Creation from Change Proposals The system SHALL create GitHub issues from OpenSpec change proposals automatically. @@ -1116,4 +1119,3 @@ The system SHALL support exporting coordination artifacts from dependency analys **Acceptance Criteria**: - `backlog analyze-deps` can export a "dependency review packet" (Markdown); coordination artifacts (dependency contract, ROAM seed, critical path narrative) are included when applicable. - diff --git a/openspec/specs/docs-aha-moment-entry/spec.md b/openspec/specs/docs-aha-moment-entry/spec.md index 0fcac2c5..13541719 100644 --- a/openspec/specs/docs-aha-moment-entry/spec.md +++ b/openspec/specs/docs-aha-moment-entry/spec.md @@ -1,8 +1,11 @@ # docs-aha-moment-entry Specification ## Purpose + TBD - created by archiving change docs-new-user-onboarding. Update Purpose after archive. + ## Requirements + ### Requirement: Homepage names `code review run` as the primary entry command The docs homepage SHALL explicitly name `specfact code review run` as the primary command for the @@ -82,4 +85,3 @@ users, without a "Limitations" warning that discourages its use. - **WHEN** a user wants to find Container or GitHub Action installation options - **THEN** a visible section heading or anchor link SHALL allow them to jump to those options without reading through the uvx or pip sections first - diff --git a/openspec/specs/docs-command-validation/spec.md b/openspec/specs/docs-command-validation/spec.md index 2ca6372a..91c9709d 100644 --- a/openspec/specs/docs-command-validation/spec.md +++ b/openspec/specs/docs-command-validation/spec.md @@ -1,8 +1,11 @@ # docs-command-validation Specification ## Purpose + TBD - created by archiving change docs-12-docs-validation-ci. Update Purpose after archive. + ## Requirements + ### Requirement: Docs command examples resolve to a valid CLI path Documentation under `docs/` SHALL include `specfact …` examples in fenced code blocks only when some prefix of the command tokens matches a command path that accepts `--help` in the current CLI (or is a bundle-only group that reports β€œnot installed” when bundles are absent). @@ -22,4 +25,3 @@ Content under `docs/migration/` and other explicitly listed illustrative pages M - **WHEN** `check-docs-commands` scans `docs/` - **THEN** it skips `docs/migration/**` and other configured exclusions - **AND** it does not fail on removed commands documented only for historical context - diff --git a/openspec/specs/docs-cross-site-link-check/spec.md b/openspec/specs/docs-cross-site-link-check/spec.md index 522074ae..52de0d5c 100644 --- a/openspec/specs/docs-cross-site-link-check/spec.md +++ b/openspec/specs/docs-cross-site-link-check/spec.md @@ -1,8 +1,11 @@ # docs-cross-site-link-check Specification ## Purpose + TBD - created by archiving change docs-12-docs-validation-ci. Update Purpose after archive. + ## Requirements + ### Requirement: Cross-site modules URLs are discoverable from Markdown The repository SHALL provide a script that extracts `https://modules.specfact.io/…` URLs from `docs/**/*.md`, performs HTTP HEAD/GET checks with redirects allowed, and reports source file context for failures. @@ -22,4 +25,3 @@ The handoff migration map SHALL be covered by opt-in HTTP tests that verify each - **WHEN** a maintainer sets `SPECFACT_RUN_HANDOFF_URL_CHECK=1` - **THEN** pytest runs the handoff map URL reachability test against production - **AND** the default CI run skips that test to avoid flaky or lagging deploy noise - diff --git a/openspec/specs/docs-review-gate/spec.md b/openspec/specs/docs-review-gate/spec.md index e7530f97..87c0fdea 100644 --- a/openspec/specs/docs-review-gate/spec.md +++ b/openspec/specs/docs-review-gate/spec.md @@ -1,8 +1,11 @@ # docs-review-gate Specification ## Purpose + TBD - created by archiving change docs-04-docs-review-gate-and-link-integrity. Update Purpose after archive. + ## Requirements + ### Requirement: Docs review validates published route integrity The docs review gate SHALL derive the published route for authored docs pages from Jekyll front matter and site defaults, and SHALL fail when an internal docs link points to a route that is not published by the current docs source tree. @@ -38,4 +41,3 @@ A dedicated docs review workflow SHALL run the docs review gate for pull request - **WHEN** a pull request changes only `docs/**` or Markdown files - **THEN** the dedicated docs review workflow runs the targeted docs review suite - **AND** docs validation does not wait for the full code-oriented PR orchestrator to complete - diff --git a/openspec/specs/docs-sync-algorithm/spec.md b/openspec/specs/docs-sync-algorithm/spec.md index 6328cc5e..55299da6 100644 --- a/openspec/specs/docs-sync-algorithm/spec.md +++ b/openspec/specs/docs-sync-algorithm/spec.md @@ -1,78 +1,98 @@ # docs-sync-algorithm Specification ## Purpose + TBD - created by archiving change ci-docs-sync-check. Update Purpose after archive. + ## Requirements + ### Requirement: Change Detection Algorithm + The system SHALL implement an algorithm to detect when source files change but tracked documentation doesn't. #### Scenario: Source change without doc update + - **WHEN** source files matching `tracks` patterns change - **AND** corresponding docs are not updated - **THEN** algorithm SHALL detect stale documentation #### Scenario: Source and doc both updated + - **WHEN** source files change - **AND** corresponding docs are also updated - **THEN** algorithm SHALL not detect stale documentation ### Requirement: Git Diff Integration + The system SHALL integrate with git to detect changed files between commits. #### Scenario: Git diff between base and head + - **WHEN** algorithm runs on PR - **THEN** it SHALL use `git diff --name-only ...` - **AND** correctly identify changed files #### Scenario: Multiple changed files + - **WHEN** multiple files change in PR - **THEN** algorithm SHALL process all changed files - **AND** identify all affected documentation ### Requirement: Glob Pattern Matching + The system SHALL support glob pattern matching for tracking relationships. #### Scenario: Single glob pattern match + - **WHEN** changed file matches single glob pattern in `tracks` - **THEN** corresponding doc SHALL be marked for sync check #### Scenario: Multiple glob pattern matches + - **WHEN** changed file matches multiple glob patterns - **THEN** all corresponding docs SHALL be marked for sync check ### Requirement: Stale Documentation Identification + The system SHALL correctly identify stale documentation. #### Scenario: Doc not in changed files + - **WHEN** source files change matching doc's `tracks` - **AND** doc file itself is not in changed files - **THEN** doc SHALL be marked as stale #### Scenario: Doc in changed files + - **WHEN** source files change matching doc's `tracks` - **AND** doc file is in changed files - **THEN** doc SHALL not be marked as stale ### Requirement: Exempt Documentation Handling + The system SHALL properly handle exempt documentation. #### Scenario: Exempt doc with source changes + - **WHEN** source files change matching exempt doc's `tracks` - **THEN** doc SHALL not be marked as stale #### Scenario: Non-exempt doc processing + - **WHEN** source files change matching non-exempt doc's `tracks` - **THEN** doc SHALL undergo normal sync check ### Requirement: Error Reporting + The system SHALL provide clear error reporting for stale documentation. #### Scenario: Single stale document + - **WHEN** one document is stale - **THEN** error output SHALL list that document clearly #### Scenario: Multiple stale documents + - **WHEN** multiple documents are stale - **THEN** error output SHALL list all stale documents - **AND** format SHALL be clear and readable - diff --git a/openspec/specs/docs-vibecoder-entry-path/spec.md b/openspec/specs/docs-vibecoder-entry-path/spec.md index 0f0e99ec..c1685bf9 100644 --- a/openspec/specs/docs-vibecoder-entry-path/spec.md +++ b/openspec/specs/docs-vibecoder-entry-path/spec.md @@ -1,8 +1,11 @@ # docs-vibecoder-entry-path Specification ## Purpose + TBD - created by archiving change docs-new-user-onboarding. Update Purpose after archive. + ## Requirements + ### Requirement: Vibe-coder entry path is discoverable and runnable in under 2 commands The documentation entry surface SHALL make it possible for a developer who has never used @@ -72,4 +75,3 @@ technical terms. expertise (e.g. "Point it at your code. Get a score and a list of what to fix.") - **AND** the first technical term they encounter SHALL be a command they can copy and run, not a concept they need to research first - diff --git a/openspec/specs/documentation-alignment/spec.md b/openspec/specs/documentation-alignment/spec.md index cf1469a4..14bd5019 100644 --- a/openspec/specs/documentation-alignment/spec.md +++ b/openspec/specs/documentation-alignment/spec.md @@ -1,13 +1,17 @@ # documentation-alignment Specification ## Purpose + TBD - created by archiving change arch-08-documentation-discrepancies-remediation. Update Purpose after archive. + ## Requirements + ### Requirement: Module system status in docs The published architecture documentation SHALL state that the module system is production-ready (e.g. since v0.27) and SHALL NOT describe it as "transitioning" or "experimental." #### Scenario: Reader checks module system status + - **GIVEN** the published architecture documentation (e.g. docs/reference/architecture.md, docs/architecture/module-system.md) - **WHEN** a reader looks for the current state of the module system - **THEN** the docs state production-ready status @@ -18,6 +22,7 @@ The published architecture documentation SHALL state that the module system is p The adapter documentation SHALL include the full BridgeAdapter interface: detect, import_artifact, export_artifact, load_change_tracking, save_change_tracking (or equivalent), with current behavior and contracts. #### Scenario: Developer implements BridgeAdapter + - **GIVEN** the adapter documentation - **WHEN** a developer implements or extends a BridgeAdapter - **THEN** the documented interface includes all methods above @@ -28,6 +33,7 @@ The adapter documentation SHALL include the full BridgeAdapter interface: detect The architecture overview SHALL describe the actual layers (Specification, Contract, Enforcement, and where relevant Adapter, Analysis, Module layers) so they match the codebase structure. #### Scenario: Reader learns layer structure + - **GIVEN** the architecture overview - **WHEN** a reader learns the high-level layer structure - **THEN** the docs describe actual layers present in code @@ -38,6 +44,7 @@ The architecture overview SHALL describe the actual layers (Specification, Contr The documentation for CI/CD and CoPilot modes SHALL clarify current mode detection and any limitations (e.g. mode-specific behavior as planned), and SHALL NOT imply full mode implementations that do not exist. #### Scenario: Reader checks mode implementation + - **GIVEN** the documentation for CI/CD and CoPilot modes - **WHEN** a reader checks what is implemented - **THEN** current detector behavior is stated @@ -48,6 +55,7 @@ The documentation for CI/CD and CoPilot modes SHALL clarify current mode detecti The architecture or module docs SHALL describe lazy loading, metadata caching, and the required module package structure (e.g. module-package.yaml, src//main.py) and naming conventions. #### Scenario: Developer needs registry or module layout details + - **GIVEN** the architecture or module docs - **WHEN** a developer needs implementation details for the command registry or module layout - **THEN** lazy loading and metadata caching are described @@ -58,6 +66,7 @@ The architecture or module docs SHALL describe lazy loading, metadata caching, a The ToolCapabilities model and adapter selection SHALL be documented; error handling patterns (custom exceptions, logging) SHALL be described in reference or adapter documentation. #### Scenario: Developer looks for capabilities or error handling + - **GIVEN** the reference or adapter documentation - **WHEN** a developer looks for adapter capabilities or error handling - **THEN** ToolCapabilities and adapter selection are documented @@ -68,6 +77,7 @@ The ToolCapabilities model and adapter selection SHALL be documented; error hand The documentation set SHALL use consistent terminology (e.g. Project Bundle, Plan Bundle) and SHALL standardize or remove version references that cause confusion. #### Scenario: Same concept referenced across docs + - **GIVEN** the documentation set - **WHEN** the same concept is referenced - **THEN** terminology is consistent @@ -78,6 +88,7 @@ The documentation set SHALL use consistent terminology (e.g. Project Bundle, Pla Any Mermaid or component diagram in the docs SHALL show only components that exist in the codebase or are clearly marked as planned; non-existent components (e.g. unimplemented "DevOps Adapters") SHALL be removed or relabeled. #### Scenario: Reader interprets diagram + - **GIVEN** any Mermaid or component diagram in the docs - **WHEN** a reader interprets the diagram - **THEN** only existing or clearly planned components are shown @@ -88,6 +99,7 @@ Any Mermaid or component diagram in the docs SHALL show only components that exi Any stated performance or timing in the docs SHALL reflect current benchmarks or SHALL be removed if outdated. #### Scenario: Docs publish performance claims + - **GIVEN** any stated performance or timing (e.g. "typical execution: < 10s") - **WHEN** the docs are published - **THEN** metrics reflect current benchmarks or are removed if outdated @@ -148,9 +160,11 @@ The command reference and migration guidance SHALL map old flat or pre-split syn - **AND** the docs do not present `project plan` as the replacement for removed flat commands in the post-split CLI ### Requirement: Markdown quality workflow auto-fixes low-risk issues before enforcement + The documentation workflow SHALL automatically fix low-risk Markdown issues during pre-commit checks before enforcing markdown lint failures for non-fixable or higher-risk issues. #### Scenario: Contributor stages Markdown changes with trivial spacing issues + - **WHEN** a contributor stages Markdown files and runs the repository pre-commit checks - **THEN** the workflow attempts safe markdown auto-fixes first using the configured markdown lint tooling - **AND** any auto-fixed Markdown files are re-staged automatically @@ -264,4 +278,3 @@ explicitly enough that maintainers can review the page against them. - what the user gets - how the user gets started - **AND** the page SHALL not bury those answers underneath topology or implementation details - diff --git a/openspec/specs/dogfood-self-review/spec.md b/openspec/specs/dogfood-self-review/spec.md index ed991848..1cfc601b 100644 --- a/openspec/specs/dogfood-self-review/spec.md +++ b/openspec/specs/dogfood-self-review/spec.md @@ -1,97 +1,119 @@ # dogfood-self-review Specification ## Purpose + TBD - created by archiving change code-review-zero-findings. Update Purpose after archive. + ## Requirements + ### Requirement: Self-review policy β€” specfact-cli runs specfact review on itself + The specfact-cli repository SHALL be subject to `specfact review` as a first-class CI gate, enforcing the same zero-finding standard we recommend to customers. #### Scenario: Review run on own repo produces zero findings + - **WHEN** `specfact review` is executed against the specfact-cli repository root - **THEN** `overall_verdict` is `PASS` - **AND** the findings array is empty - **AND** the process exits with code 0 #### Scenario: Review run failure blocks CI + - **WHEN** `specfact review` exits with code non-zero on a PR targeting `dev` or `main` - **THEN** the CI pipeline marks the PR check as failed - **AND** the PR cannot be merged until findings are resolved #### Scenario: Review result is machine-readable + - **WHEN** `specfact review --format json` is run in CI - **THEN** a JSON report is written to `.specfact/code-review.json` - **AND** the report schema_version is `1.0` - **AND** `overall_verdict`, `score`, `findings`, and `ci_exit_code` fields are present #### Scenario: Expanded clean-code categories stay at zero findings + - **GIVEN** the expanded clean-code pack is available from the review module - **WHEN** `specfact review` runs against the specfact-cli repository root with clean-code categories enabled - **THEN** categories `naming`, `kiss`, `yagni`, `dry`, and `solid` each report zero findings - **AND** the zero-finding proof is recorded in `TDD_EVIDENCE.md` ### Requirement: Type-safe codebase β€” zero basedpyright findings in strict mode + All public API class members and function signatures in `src/specfact_cli/` SHALL be explicitly typed so that `basedpyright` strict mode reports zero `reportUnknownMemberType`, `reportAttributeAccessIssue`, and `reportUnsupportedDunderAll` findings. #### Scenario: basedpyright strict mode passes on src/ + - **WHEN** `hatch run type-check` is executed - **THEN** basedpyright reports zero errors and zero warnings for files under `src/specfact_cli/` #### Scenario: Untyped class member introduced in PR fails CI + - **WHEN** a PR introduces a class member without a type annotation - **THEN** `hatch run type-check` exits non-zero - **AND** CI marks the type-check step as failed #### Scenario: TypedDict used for structured dict shapes + - **WHEN** a function accepts or returns a dict with a known schema - **THEN** a `TypedDict` or Pydantic model is used rather than `dict[str, Any]` - **AND** basedpyright infers the member types without `reportUnknownMemberType` ### Requirement: Print-free source β€” all production logging via bridge logger + No `print()` builtin calls SHALL appear in files under `src/specfact_cli/`, `scripts/`, or `tools/`, as detected by the semgrep `print-in-src` rule. #### Scenario: Logging call replaces print in adapter layer + - **WHEN** `get_bridge_logger()` is called in an adapter module (e.g., `adapters/ado.py`) - **THEN** structured log messages are routed to the debug log file when `--debug` is active - **AND** no `print()` call remains in the file - **AND** semgrep `print-in-src` reports zero findings for that file #### Scenario: Script-layer progress output uses Rich console or stdlib logging + - **WHEN** a script in `scripts/` or `tools/` needs to write progress to stdout - **THEN** it uses `rich.console.Console().print()` or `logging.getLogger(__name__)`, not the stdlib `print` builtin - **AND** semgrep `print-in-src` reports zero findings for that file ### Requirement: Full contract coverage β€” all public APIs carry icontract decorators + Every public function (non-underscore-prefixed) in `src/specfact_cli/` SHALL have at least one `@require` or `@ensure` decorator from icontract, and a `@beartype` decorator for runtime type enforcement. #### Scenario: Public function without @require fails contract_runner check + - **WHEN** `contract_runner` scans a file with a public function lacking `@require`/`@ensure` - **THEN** a `MISSING_ICONTRACT` finding is produced #### Scenario: Decorated public function produces no missing-contract finding + - **WHEN** a public function has both `@require` (or `@ensure`) and `@beartype` - **THEN** `contract_runner` produces zero `MISSING_ICONTRACT` findings for that function #### Scenario: Minimal meaningful contract per function + - **WHEN** a `@require` precondition is added to a public function - **THEN** the precondition checks a domain-meaningful invariant (e.g., path exists, non-empty string, valid enum) - **AND** the precondition is NOT a trivial `lambda x: x is not None` that merely restates the type #### Scenario: Utility contract exploration handles pathological strings gracefully + - **WHEN** CrossHair or unit tests exercise utility helpers with pathological string inputs such as control characters or malformed package names - **THEN** the helpers SHALL return a safe fallback value instead of raising unexpected exceptions - **AND** `hatch run contract-test` SHALL not report uncaught exceptions for those utility paths ### Requirement: Complexity budget β€” no function exceeds CC15 + No function in `src/specfact_cli/`, `scripts/`, or `tools/` SHALL have cyclomatic complexity >=16, as measured by radon. #### Scenario: High-complexity function split into helpers passes complexity check + - **WHEN** a function with CC>=16 is refactored into a top-level function and one or more private helpers - **THEN** `hatch run lint` (radon check) reports no CC>=16 findings for that function - **AND** each extracted helper has CC<10 #### Scenario: New code written during this change stays below threshold + - **WHEN** any new function is introduced during this change - **THEN** its cyclomatic complexity is <10 as measured by radon - **AND** no CC>=13 warning is raised for the new function - diff --git a/openspec/specs/entrypoint-onboarding/spec.md b/openspec/specs/entrypoint-onboarding/spec.md index df614d05..9d8728d1 100644 --- a/openspec/specs/entrypoint-onboarding/spec.md +++ b/openspec/specs/entrypoint-onboarding/spec.md @@ -1,8 +1,11 @@ # entrypoint-onboarding Specification ## Purpose + TBD - created by archiving change docs-14-first-contact-story-and-onboarding. Update Purpose after archive. + ## Requirements + ### Requirement: One primary fast-start path The central entry points SHALL provide one primary "start here now" path before branching into more @@ -77,4 +80,3 @@ The entry points SHALL explain that `docs.specfact.io` is the default starting p - **WHEN** a user outgrows the core landing guidance and needs workflow- or bundle-specific help - **THEN** the core docs SHALL provide a clear, explicit handoff to `modules.specfact.io` - **AND** the handoff SHALL explain what extra value the modules docs provide - diff --git a/openspec/specs/first-contact-story/spec.md b/openspec/specs/first-contact-story/spec.md index 89a61b20..6cfb4824 100644 --- a/openspec/specs/first-contact-story/spec.md +++ b/openspec/specs/first-contact-story/spec.md @@ -1,8 +1,11 @@ # first-contact-story Specification ## Purpose + TBD - created by archiving change docs-14-first-contact-story-and-onboarding. Update Purpose after archive. + ## Requirements + ### Requirement: Canonical first-contact product story The repository and documentation entry points SHALL present one canonical product story that answers @@ -104,4 +107,3 @@ Cross-repo traceability note: `modules.specfact.io` and the `nold-ai/specfact-cli-modules` `docs/index.md` SHALL either present the same first-contact story or provide an explicit handoff to the core docs. See `documentation-alignment/spec.md` for ownership and cross-site wording guidance. - diff --git a/openspec/specs/first-run-selection/spec.md b/openspec/specs/first-run-selection/spec.md index eef034ec..2e7281c0 100644 --- a/openspec/specs/first-run-selection/spec.md +++ b/openspec/specs/first-run-selection/spec.md @@ -1,8 +1,11 @@ # first-run-selection Specification ## Purpose + TBD - created by archiving change module-migration-01-categorize-and-group. Update Purpose after archive. + ## Requirements + ### Requirement: `specfact init` detects first-run and presents bundle selection On a fresh install where no bundles are installed, `specfact init` SHALL present an interactive @@ -134,4 +137,3 @@ The `specfact init` command SHALL delegate all bundle installation to the existi - **WHEN** init processes the selection - **THEN** the module installer SHALL automatically include `specfact-project` as a dependency - **AND** SHALL inform the user: "Installing specfact-project as required dependency of specfact-spec" - diff --git a/openspec/specs/format-abstraction/spec.md b/openspec/specs/format-abstraction/spec.md index 4ca21586..34bc5d15 100644 --- a/openspec/specs/format-abstraction/spec.md +++ b/openspec/specs/format-abstraction/spec.md @@ -1,8 +1,11 @@ # format-abstraction Specification ## Purpose + TBD - created by archiving change add-generic-backlog-abstraction. Update Purpose after archive. + ## Requirements + ### Requirement: Format Abstraction The system SHALL provide a `BacklogFormat` abstraction that handles serialization and deserialization of backlog items across different formats (Markdown, YAML, JSON). @@ -205,4 +208,3 @@ The system SHALL map work item types correctly across providers and frameworks. - **WHEN** the item is converted to `BacklogItem` - **THEN** the `work_item_type` field is set appropriately (User Story, Task, Bug, etc.) - **AND** no sprint/iteration information is required (Kanban doesn't use sprints) - diff --git a/openspec/specs/git-worktree-lifecycle/spec.md b/openspec/specs/git-worktree-lifecycle/spec.md index db7aad82..4226232a 100644 --- a/openspec/specs/git-worktree-lifecycle/spec.md +++ b/openspec/specs/git-worktree-lifecycle/spec.md @@ -1,8 +1,11 @@ # git-worktree-lifecycle Specification ## Purpose + TBD - created by archiving change workflow-01-git-worktree-management. Update Purpose after archive. + ## Requirements + ### Requirement: Worktree Branch Guardrails The system SHALL enforce branch policy when managing git worktrees. @@ -42,4 +45,3 @@ The system SHALL provide a cleanup command for local worktree lifecycle manageme - **THEN** the helper removes the mapped worktree path - **AND** runs local prune cleanup - **AND** reports completion steps. - diff --git a/openspec/specs/github-hierarchy-cache/spec.md b/openspec/specs/github-hierarchy-cache/spec.md new file mode 100644 index 00000000..fe9e3537 --- /dev/null +++ b/openspec/specs/github-hierarchy-cache/spec.md @@ -0,0 +1,52 @@ +# github-hierarchy-cache Specification + +## Purpose + +Epic and Feature issue metadata for this repository is cached locally so agents and contributors can resolve parent and +child relationships without repeated GitHub lookups. This spec defines deterministic sync, on-disk layout under +`.specfact/backlog/`, and cache-first governance rules (aligned with archived change +`openspec/changes/archive/2026-04-09-governance-02-github-hierarchy-cache`). + +## Requirements + +### Requirement: Repository hierarchy cache sync + +The repository SHALL provide a deterministic sync mechanism that retrieves GitHub Epic and Feature issues for the current repository and writes a local hierarchy cache under ignored `.specfact/backlog/`. + +#### Scenario: Generate hierarchy cache from GitHub metadata + +- **WHEN** the user runs the hierarchy cache sync script for the repository +- **THEN** the script retrieves GitHub issues whose Type is `Epic` or `Feature` +- **AND** writes a markdown cache under ignored `.specfact/backlog/` with each issue's number, title, URL, short summary, labels, and hierarchy relationships +- **AND** the output ordering is deterministic across repeated runs with unchanged source data + +#### Scenario: Represent hierarchy relationships in cache output + +- **WHEN** a synced Epic or Feature has parent or child hierarchy links +- **THEN** the markdown cache includes those relationships in normalized form +- **AND** missing relationships are rendered as explicit empty or none values rather than omitted ambiguously + +#### Scenario: Fast exit on unchanged hierarchy state + +- **WHEN** the script detects that the current Epic and Feature hierarchy fingerprint matches the last synced fingerprint +- **THEN** it exits successfully without regenerating the markdown cache +- **AND** it reports that no hierarchy update was required + +### Requirement: Repository governance must use cache-first hierarchy lookup + +Repository governance instructions SHALL direct contributors and agents to consult the local hierarchy cache before performing manual GitHub lookups for Epic or Feature parenting. + +#### Scenario: Cache-first governance guidance + +- **WHEN** a contributor reads `AGENTS.md` or `openspec/config.yaml` for GitHub issue setup guidance +- **THEN** the instructions tell them to consult the local hierarchy cache first +- **AND** the instructions define when the sync script must be rerun to refresh stale hierarchy metadata +- **AND** the instructions state that the cache is local ephemeral state and must not be committed + +#### Scenario: Session bootstrap refreshes missing or stale cache + +- **WHEN** an agent starts a governance-sensitive session that depends on GitHub hierarchy metadata +- **AND** the local hierarchy cache is missing or stale according to repository-defined freshness rules +- **THEN** the bootstrap guidance SHALL require rerunning the hierarchy cache sync script before continuing with issue-parenting or blocker-resolution work +- **AND** the compact governance flow SHALL treat the refresh as part of deterministic startup rather than an optional later reminder + diff --git a/openspec/specs/github-workflow/spec.md b/openspec/specs/github-workflow/spec.md index 9c27562c..73c167c0 100644 --- a/openspec/specs/github-workflow/spec.md +++ b/openspec/specs/github-workflow/spec.md @@ -1,75 +1,93 @@ # github-workflow Specification ## Purpose + TBD - created by archiving change ci-docs-sync-check. Update Purpose after archive. + ## Requirements + ### Requirement: Workflow File Structure + The system SHALL provide a GitHub Actions workflow file `.github/workflows/docs-sync.yml` with proper structure. #### Scenario: Valid workflow file + - **WHEN** workflow file is created - **THEN** it SHALL have valid YAML structure - **AND** follow GitHub Actions best practices #### Scenario: Workflow triggers + - **WHEN** workflow file is examined - **THEN** it SHALL trigger on pull_request events - **AND** target main and develop branches ### Requirement: Workflow Steps + The workflow SHALL include all necessary steps for docs sync checking. #### Scenario: Checkout step + - **WHEN** workflow runs - **THEN** first step SHALL checkout repository - **AND** use fetch-depth: 0 for full history #### Scenario: Python setup step + - **WHEN** workflow runs - **THEN** it SHALL setup Python environment - **AND** install PyYAML dependency #### Scenario: Docs sync check step + - **WHEN** workflow runs - **THEN** it SHALL execute docs sync script - **AND** pass base/head references correctly ### Requirement: Environment Configuration + The workflow SHALL properly configure the execution environment. #### Scenario: Python version + - **WHEN** workflow runs - **THEN** it SHALL use Python 3.12 - **AND** environment SHALL be properly configured #### Scenario: Dependency installation + - **WHEN** workflow runs - **THEN** it SHALL install required dependencies - **AND** handle installation errors appropriately ### Requirement: Error Handling + The workflow SHALL handle errors appropriately. #### Scenario: Script execution failure + - **WHEN** docs sync script fails - **THEN** workflow SHALL fail - **AND** provide clear error output #### Scenario: Workflow timeout + - **WHEN** workflow exceeds timeout - **THEN** it SHALL fail gracefully - **AND** provide timeout information ### Requirement: Output Formatting + The workflow SHALL provide well-formatted output. #### Scenario: Success output + - **WHEN** docs sync check passes - **THEN** output SHALL show success message - **AND** be clearly formatted #### Scenario: Failure output + - **WHEN** docs sync check fails - **THEN** output SHALL show error details - **AND** list stale documents clearly - diff --git a/openspec/specs/help-cache/spec.md b/openspec/specs/help-cache/spec.md index bcdae461..fc11b54c 100644 --- a/openspec/specs/help-cache/spec.md +++ b/openspec/specs/help-cache/spec.md @@ -1,8 +1,11 @@ # help-cache Specification ## Purpose + TBD - created by archiving change arch-01-cli-modular-command-registry. Update Purpose after archive. + ## Requirements + ### Requirement: Discovery on specfact init Writes Command Metadata to ~/.specfact When the user runs **specfact init**, the CLI SHALL run a discovery step that collects all registered commands' metadata (name, help, tier, optional subcommands) and SHALL write this metadata under `~/.specfact/registry/` (e.g. `commands.json` or `commands.yaml`), including a version or hash for cache invalidation. @@ -49,4 +52,3 @@ When the user runs **specfact init**, the CLI SHALL run a discovery step that co - Version or hash in cache file allows comparison with current runtime; if mismatch, treat cache as invalid and refresh on next init or root help - Running `specfact init` always refreshes cache for current version - diff --git a/openspec/specs/house-rules-skill/spec.md b/openspec/specs/house-rules-skill/spec.md index 66778780..c293a96c 100644 --- a/openspec/specs/house-rules-skill/spec.md +++ b/openspec/specs/house-rules-skill/spec.md @@ -1,33 +1,41 @@ # house-rules-skill Specification ## Purpose + TBD - created by archiving change code-review-07-house-rules-skill. Update Purpose after archive. + ## Requirements + ### Requirement: ai-integration-01 Compliant House Rules Skill File + The system SHALL provide `skills/specfact-code-review/SKILL.md` with valid ai-integration-01 YAML frontmatter, DO/DON'T rules, and an auto-managed TOP VIOLATIONS section, within a 35-line hard cap. #### Scenario: SKILL.md has valid ai-integration-01 YAML frontmatter + - **GIVEN** `skills/specfact-code-review/SKILL.md` exists - **WHEN** the frontmatter is parsed - **THEN** `name`, `description`, and `allowed-tools` fields are present and valid #### Scenario: Skill file is within 35 line cap + - **GIVEN** the SKILL.md in its default or updated state - **WHEN** line count is measured - **THEN** line count is at most 35 #### Scenario: TOP VIOLATIONS section is auto-managed and other sections preserved + - **GIVEN** SKILL.md contains the auto-managed TOP VIOLATIONS marker - **WHEN** `specfact code review rules update` runs - **THEN** only the TOP VIOLATIONS section is modified and DO/DON'T sections are unchanged #### Scenario: SKILL.md creation does not modify CLAUDE.md + - **GIVEN** `specfact code review rules init` is run - **WHEN** all files created or modified are inspected - **THEN** `CLAUDE.md` is not in the list of modified files #### Scenario: rules init creates default SKILL.md for new project + - **GIVEN** no `skills/specfact-code-review/SKILL.md` exists - **WHEN** `specfact code review rules init` is run - **THEN** SKILL.md is created with default DO/DON'T rules within 35 lines - diff --git a/openspec/specs/house-rules-updater/spec.md b/openspec/specs/house-rules-updater/spec.md index 71c3ee7c..8c979fcf 100644 --- a/openspec/specs/house-rules-updater/spec.md +++ b/openspec/specs/house-rules-updater/spec.md @@ -1,38 +1,47 @@ # house-rules-updater Specification ## Purpose + TBD - created by archiving change code-review-07-house-rules-skill. Update Purpose after archive. + ## Requirements + ### Requirement: House Rules Auto-Update Algorithm with Frequency Thresholds and Line Cap + The system SHALL implement an update algorithm that surfaces rules with >= 3 hits in last 20 runs, prunes rules with 0 hits for 10+ consecutive runs, enforces a 35-line hard cap, and increments the version header. #### Scenario: Rule appearing 3+ times in last 20 runs is surfaced + - **GIVEN** ledger data showing rule `C901` appeared 5 times in the last 20 runs - **WHEN** `update_house_rules(skill_path, ledger_data)` is called - **THEN** `C901` is present in the TOP VIOLATIONS section of the updated SKILL.md #### Scenario: Rule appearing fewer than 3 times is not surfaced + - **GIVEN** rule `T201` appeared only twice in the last 20 runs - **WHEN** `update_house_rules(...)` is called - **THEN** `T201` is NOT added to TOP VIOLATIONS #### Scenario: Rule with 0 hits for 10 consecutive runs is pruned + - **GIVEN** SKILL.md lists `W0702` in TOP VIOLATIONS and `W0702` has 0 hits in last 10 runs - **WHEN** `update_house_rules(...)` is called - **THEN** `W0702` is removed from TOP VIOLATIONS #### Scenario: Version header increments on each update + - **GIVEN** SKILL.md has `# House Rules β€” AI Coding Context (v3)` - **WHEN** `update_house_rules(...)` is called - **THEN** the version becomes `v4` and the `Updated:` timestamp reflects the current date #### Scenario: 35 line cap enforced by pruning lowest-frequency entries + - **GIVEN** updating would exceed the 35-line budget - **WHEN** `update_house_rules(...)` is called - **THEN** lowest-frequency entries are removed first and the result is at most 35 lines #### Scenario: DO and DON'T sections unchanged after update + - **GIVEN** DO and DON'T sections have specific content - **WHEN** `update_house_rules(...)` is called - **THEN** DO and DON'T sections remain byte-identical to their pre-update state - diff --git a/openspec/specs/implementation-status-docs/spec.md b/openspec/specs/implementation-status-docs/spec.md index 912ae79f..3ce2ae84 100644 --- a/openspec/specs/implementation-status-docs/spec.md +++ b/openspec/specs/implementation-status-docs/spec.md @@ -1,13 +1,17 @@ # implementation-status-docs Specification ## Purpose + TBD - created by archiving change arch-08-documentation-discrepancies-remediation. Update Purpose after archive. + ## Requirements + ### Requirement: Implemented vs planned clearly stated The implementation status documentation SHALL clearly mark each feature (e.g. architecture commands, protocol FSM, change tracking) as implemented or planned, with brief notes on scope where relevant. #### Scenario: Reader checks feature status + - **GIVEN** the implementation status documentation (e.g. docs/architecture/implementation-status.md) - **WHEN** a reader checks the status of a feature - **THEN** each feature is clearly marked as implemented or planned @@ -18,6 +22,7 @@ The implementation status documentation SHALL clearly mark each feature (e.g. ar For planned or partially implemented features, the implementation status doc SHALL link or reference the relevant OpenSpec change (e.g. architecture-01-solution-layer for architecture derive/validate/trace). #### Scenario: Reader finds spec for planned feature + - **GIVEN** a planned or partially implemented feature - **WHEN** the implementation status doc describes it - **THEN** it links or references the relevant OpenSpec change @@ -28,6 +33,7 @@ For planned or partially implemented features, the implementation status doc SHA Current limitations for change tracking and protocol/FSM behavior SHALL be stated (e.g. no FSM engine, partial adapter support for change tracking) so that expectations match reality. #### Scenario: Reader checks limitations + - **GIVEN** change tracking and protocol/FSM behavior - **WHEN** a user or contributor reads the implementation status - **THEN** current limitations are stated @@ -38,16 +44,18 @@ Current limitations for change tracking and protocol/FSM behavior SHALL be state The implementation status page SHALL be linked from the architecture README or reference architecture page so it can be found without searching. #### Scenario: User navigates architecture docs + - **GIVEN** the docs site - **WHEN** a user navigates architecture docs - **THEN** the implementation status page is linked - **AND** discoverable from the architecture index or README ### Requirement: Implementation-status docs describe core versus bundle ownership + Implementation-status and architecture status documentation SHALL explicitly describe which capabilities are owned by core runtime versus marketplace-installed bundles, and SHALL identify documentation that is still temporarily hosted in core despite belonging to bundle workflows. #### Scenario: Reader checks ownership in status docs + - **WHEN** a reader reviews implementation-status or architecture status pages - **THEN** the docs distinguish core lifecycle/runtime ownership from bundle workflow ownership - **AND** temporary docs-hosting exceptions are called out so documentation location does not imply incorrect runtime ownership - diff --git a/openspec/specs/init-ide-prompt-source-selection/spec.md b/openspec/specs/init-ide-prompt-source-selection/spec.md index 4d2d4459..28077d7e 100644 --- a/openspec/specs/init-ide-prompt-source-selection/spec.md +++ b/openspec/specs/init-ide-prompt-source-selection/spec.md @@ -1,13 +1,17 @@ # init-ide-prompt-source-selection Specification ## Purpose + TBD - created by archiving change init-ide-prompt-source-selection. Update Purpose after archive. + ## Requirements + ### Requirement: Init IDE Must Export All Prompt Sources By Default `specfact init ide` SHALL export all available prompt sources by default. #### Scenario: Default export includes core and installed modules across effective roots + - **WHEN** a user runs `specfact init ide` without restricting prompt sources - **THEN** prompt export includes core prompts - **AND** prompt export includes prompts from installed and enabled modules that provide prompt resources @@ -18,15 +22,18 @@ TBD - created by archiving change init-ide-prompt-source-selection. Update Purpo `specfact init ide` SHALL discover prompt and related module-owned resources from installed module roots and packaged resource directories. It SHALL not fetch module archives or treat the modules source repository as a runtime extraction source. #### Scenario: Installed project-scope bundle contributes prompt resources + - **WHEN** a repository has an installed module under `/.specfact/modules` - **THEN** `specfact init ide` can discover that module's packaged prompt resources for export in that repository. #### Scenario: Installed user-scope bundle contributes prompt resources + - **WHEN** a user has installed a module under `~/.specfact/modules` - **AND** no overriding project-scope copy shadows it - **THEN** `specfact init ide` can discover that module's packaged prompt resources for export. #### Scenario: Missing selected source does not trigger install work + - **WHEN** a selected prompt source is not installed or does not expose the required packaged resources - **THEN** `specfact init ide` fails or warns with actionable guidance - **AND** the guidance names the relevant scope and install/bootstrap command such as `specfact module init --scope project` or `specfact module install --scope user` @@ -37,6 +44,7 @@ TBD - created by archiving change init-ide-prompt-source-selection. Update Purpo Interactive `specfact init ide` SHALL allow users to choose prompt sources from installed options. #### Scenario: Interactive picker shows available sources + - **WHEN** `specfact init ide` runs in interactive mode - **THEN** it shows a multi-select source picker containing `core` and installed module ids with prompt resources - **AND** the selected sources determine which prompt resources are copied. @@ -46,11 +54,13 @@ Interactive `specfact init ide` SHALL allow users to choose prompt sources from Non-interactive `specfact init ide` SHALL accept a comma-separated prompt source selector. #### Scenario: Non-interactive selector accepts core and module ids + - **WHEN** a user runs `specfact init ide --prompts core,nold-ai/specfact-backlog` - **THEN** core prompts and the selected installed module prompts are copied - **AND** unrelated prompt sources are not copied. #### Scenario: Invalid or unavailable module source is rejected + - **WHEN** a user passes a prompt source token that is not `all`, not `core`, and not an installed module id with prompt resources - **THEN** the command fails with actionable guidance describing the invalid token and the available prompt sources. @@ -59,12 +69,13 @@ Non-interactive `specfact init ide` SHALL accept a comma-separated prompt source Exported prompts for VS Code / Copilot (under ``.github/prompts/``) and other multi-source IDE targets SHALL use a **flat** layout (no per-source subfolders) so editors and agents can discover ``specfact*.prompt.md`` (or equivalent) at the export root. #### Scenario: Core defers to modules on overlapping template basenames + - **WHEN** `core` and one or more installed modules expose the same source filename (e.g. ``specfact.01-import.md``) - **THEN** the prompt catalog SHALL list that basename only under the owning module source - **AND** `core` SHALL NOT duplicate that basename so exports are single-sourced. #### Scenario: Multiple module sources expose the same basename + - **WHEN** two installed modules expose the same template basename - **THEN** the merged export uses a deterministic last-wins rule by sorted source id (later id overwrites earlier) - **AND** the flat export contains exactly one file per output basename. - diff --git a/openspec/specs/init-module-discovery-alignment/spec.md b/openspec/specs/init-module-discovery-alignment/spec.md index 35cf611b..d804f8f8 100644 --- a/openspec/specs/init-module-discovery-alignment/spec.md +++ b/openspec/specs/init-module-discovery-alignment/spec.md @@ -1,8 +1,11 @@ # init-module-discovery-alignment Specification ## Purpose + TBD - created by archiving change backlog-core-01-dependency-analysis-commands. Update Purpose after archive. + ## Requirements + ### Requirement: Init uses same discovery roots as registry The system SHALL use the same module discovery roots for `specfact init` module state and list operations as are used for command registration (built-in package modules, repo-root `modules/` when present, and `SPECFACT_MODULES_ROOTS` when set). @@ -28,4 +31,3 @@ The system SHALL use the same module discovery roots for `specfact init` module **Then** the init command SHALL validate enable/disable against the full discovered package set (not built-in only) **And** state SHALL be persisted so the module's enabled flag is respected on next init and at command registration - diff --git a/openspec/specs/init-module-state/spec.md b/openspec/specs/init-module-state/spec.md index 4f4e967b..9907a0bc 100644 --- a/openspec/specs/init-module-state/spec.md +++ b/openspec/specs/init-module-state/spec.md @@ -1,8 +1,11 @@ # init-module-state Specification ## Purpose + TBD - created by archiving change arch-01-cli-modular-command-registry. Update Purpose after archive. + ## Requirements + ### Requirement: Init Discovers Modules and Stores State with Version and Enabled Flag When the user runs **specfact init**, the CLI SHALL discover all available module packages, SHALL treat each as **enabled by default**, and SHALL write a **state file** under `~/.specfact/registry/` (e.g. `modules.json`) that records for each module: identifier, version, and **enabled** (boolean). Subsequent runs of specfact init SHALL read this state file and SHALL respect previously set enabled/disabled values (user overrides). @@ -50,4 +53,3 @@ When the user runs **specfact init**, the CLI SHALL discover all available modul - --enable-module and --disable-module are supported (multiple allowed) - State file is updated after init so overrides persist - After init, if any module is disabled and that is due to user override (saved in state), print a message: e.g. "The following modules are disabled by your configuration: . Re-enable with specfact init --enable-module ." - diff --git a/openspec/specs/installed-runtime-module-discovery/spec.md b/openspec/specs/installed-runtime-module-discovery/spec.md index 771e7599..bcddd642 100644 --- a/openspec/specs/installed-runtime-module-discovery/spec.md +++ b/openspec/specs/installed-runtime-module-discovery/spec.md @@ -1,8 +1,11 @@ # installed-runtime-module-discovery Specification ## Purpose + TBD - created by archiving change backlog-core-04-installed-runtime-discovery-and-add-prompt. Update Purpose after archive. + ## Requirements + ### Requirement: Module Discovery Roots The system SHALL discover module packages consistently between development and installed runtime contexts when invoked from a repository checkout. @@ -21,4 +24,3 @@ The system SHALL discover module packages consistently between development and i - **WHEN** module discovery runs - **THEN** discovery roots remain limited to packaged modules and explicitly configured roots - **AND** no extra discovery errors are introduced. - diff --git a/openspec/specs/lazy-loading/spec.md b/openspec/specs/lazy-loading/spec.md index 7ec691a8..2572da0c 100644 --- a/openspec/specs/lazy-loading/spec.md +++ b/openspec/specs/lazy-loading/spec.md @@ -1,8 +1,11 @@ # lazy-loading Specification ## Purpose + TBD - created by archiving change arch-01-cli-modular-command-registry. Update Purpose after archive. + ## Requirements + ### Requirement: Only Invoked Command Module Loaded at Runtime The root CLI application SHALL NOT import command modules at top level. It SHALL build the Typer tree from the registry (or cached metadata for help) and load a command module only when that command is invoked (or when its help is requested). @@ -36,4 +39,3 @@ The root CLI application SHALL NOT import command modules at top level. It SHALL - No removal or renaming of commands - Help text for each command group unchanged from user perspective - Contract tests or CLI tests that assert key commands and --help exit 0 remain passing - diff --git a/openspec/specs/ledger-commands/spec.md b/openspec/specs/ledger-commands/spec.md index 9c17d40a..84e4ec27 100644 --- a/openspec/specs/ledger-commands/spec.md +++ b/openspec/specs/ledger-commands/spec.md @@ -1,33 +1,41 @@ # ledger-commands Specification ## Purpose + TBD - created by archiving change code-review-06-reward-ledger. Update Purpose after archive. + ## Requirements + ### Requirement: Ledger CLI Subcommands for Update, Status, and Reset + The system SHALL provide `specfact code review ledger update|status|reset` subcommands for managing the reward ledger from the terminal. #### Scenario: ledger update reads ReviewReport JSON from stdin + - **GIVEN** `specfact code review run --json` output piped to `specfact code review ledger update` - **WHEN** the command executes - **THEN** `LedgerClient.record_run` is called with the parsed `ReviewReport` and exit code is 0 #### Scenario: ledger update with invalid JSON exits with error + - **GIVEN** invalid JSON is provided on stdin - **WHEN** `specfact code review ledger update` runs - **THEN** an error message is printed to stderr and exit code is 1 #### Scenario: ledger status prints current state + - **GIVEN** the ledger has `coins=7.3`, `streak_pass=2`, `last_verdict="PASS"` - **WHEN** `specfact code review ledger status` runs - **THEN** output includes `7.30` coins, `2` pass streak, and `PASS` last verdict #### Scenario: ledger reset without --confirm refuses deletion + - **GIVEN** `specfact code review ledger reset` is run without `--confirm` - **WHEN** the command executes - **THEN** an error message asking for `--confirm` is printed and nothing is deleted #### Scenario: ledger reset with --confirm clears local ledger + - **GIVEN** `specfact code review ledger reset --confirm` is run - **WHEN** the command executes - **THEN** `~/.specfact/ledger.json` is cleared and exit code is 0 - diff --git a/openspec/specs/marketplace-publishing/spec.md b/openspec/specs/marketplace-publishing/spec.md index b5c9093a..d6825c84 100644 --- a/openspec/specs/marketplace-publishing/spec.md +++ b/openspec/specs/marketplace-publishing/spec.md @@ -1,8 +1,11 @@ # marketplace-publishing Specification ## Purpose + TBD - created by archiving change module-migration-02-bundle-extraction. Update Purpose after archive. + ## Requirements + ### Requirement: publish-module.py packages each bundle as a signed tarball The `scripts/publish-module.py` script SHALL package each bundle directory into a compressed tarball, compute its SHA-256 checksum, sign it with the project Ed25519 key, and deposit the artifact and signature into `specfact-cli-modules/registry/modules/` and `specfact-cli-modules/registry/signatures/`. @@ -123,4 +126,3 @@ Each bundle's version SHALL be set at publish time and SHALL follow semantic ver - **THEN** a patch increment (e.g., `0.29.0 β†’ 0.29.1`) SHALL be applied for fixes - **AND** a minor increment SHALL be applied when new sub-commands are added to the bundle - **AND** `publish-module.py` SHALL reject a publish if the version in `module-package.yaml` is not greater than the current `latest_version` in `index.json` - diff --git a/openspec/specs/module-aliasing/spec.md b/openspec/specs/module-aliasing/spec.md index ebdb7284..eb196a00 100644 --- a/openspec/specs/module-aliasing/spec.md +++ b/openspec/specs/module-aliasing/spec.md @@ -1,24 +1,30 @@ # module-aliasing Specification ## Purpose + TBD - created by archiving change marketplace-02-advanced-marketplace-features. Update Purpose after archive. + ## Requirements + ### Requirement: Alias system maps commands to namespaced modules The system SHALL provide alias commands to create, list, and remove command-to-module mappings. #### Scenario: Create alias + - **WHEN** user runs `specfact module alias backlog acme-corp/backlog-pro` - **THEN** system SHALL store mapping in ~/.specfact/registry/aliases.json - **AND** SHALL display success message - **AND** SHALL resolve "backlog" command to "acme-corp/backlog-pro" module #### Scenario: List aliases + - **WHEN** user runs `specfact module alias list` - **THEN** system SHALL display all configured aliases - **AND** SHALL show format: "alias -> namespaced-id" #### Scenario: Remove alias + - **WHEN** user runs `specfact module alias remove backlog` - **THEN** system SHALL delete alias from aliases.json - **AND** SHALL revert to default resolution (specfact/backlog) @@ -28,12 +34,13 @@ The system SHALL provide alias commands to create, list, and remove command-to-m The system SHALL resolve command names through alias system before falling back to defaults. #### Scenario: Aliased command resolved + - **WHEN** alias "backlog" maps to "acme-corp/backlog-pro" - **AND** user runs backlog command - **THEN** system SHALL load acme-corp/backlog-pro module #### Scenario: Alias warns when shadowing built-in + - **WHEN** user creates alias for built-in module name - **THEN** system SHALL warn "Alias will shadow built-in module" - **AND** SHALL require --force flag to proceed - diff --git a/openspec/specs/module-development-guide/spec.md b/openspec/specs/module-development-guide/spec.md index 93a40ae6..fb9edf86 100644 --- a/openspec/specs/module-development-guide/spec.md +++ b/openspec/specs/module-development-guide/spec.md @@ -1,51 +1,60 @@ # module-development-guide Specification ## Purpose + TBD - created by archiving change arch-08-documentation-discrepancies-remediation. Update Purpose after archive. + ## Requirements + ### Requirement: Required module structure documented The module development guide SHALL describe the required directory structure (e.g. modules//, module-package.yaml, src//__init__.py, main.py, commands) and file roles. #### Scenario: Developer creates new module -- **GIVEN** the module development guide -- **WHEN** a developer creates a new module -- **THEN** the guide describes the required directory structure -- **AND** file roles are explained + +- __GIVEN__ the module development guide +- __WHEN__ a developer creates a new module +- __THEN__ the guide describes the required directory structure +- __AND__ file roles are explained ### Requirement: Manifest and contract requirements documented The module development guide SHALL document the module-package.yaml schema (name, version, commands, dependencies, schema_extensions, service_bridges) and SHALL mention contract requirements (@icontract, @beartype) for public APIs. #### Scenario: Developer configures module -- **GIVEN** the module development guide -- **WHEN** a developer configures a module -- **THEN** the guide documents the module-package.yaml schema -- **AND** contract requirements for public APIs are mentioned + +- __GIVEN__ the module development guide +- __WHEN__ a developer configures a module +- __THEN__ the guide documents the module-package.yaml schema +- __AND__ contract requirements for public APIs are mentioned ### Requirement: Module guide discoverable The module development guide SHALL be reachable from the docs navigation (e.g. Guides or Reference) and from the architecture or module system documentation. #### Scenario: User looks for module development -- **GIVEN** the published docs (e.g. docs.specfact.io) -- **WHEN** a user looks for how to develop modules -- **THEN** the guide is reachable from the docs navigation -- **AND** from the architecture or module system documentation + +- __GIVEN__ the published docs (e.g. docs.specfact.io) +- __WHEN__ a user looks for how to develop modules +- __THEN__ the guide is reachable from the docs navigation +- __AND__ from the architecture or module system documentation ### Requirement: Module development docs reflect the dedicated modules repository model + The module development guide SHALL describe that official bundle implementation lives in `specfact-cli-modules`, while `specfact-cli` owns the lean runtime, registry, marketplace lifecycle, and shared contracts needed by installed bundles. #### Scenario: Developer reads module development docs after modularization -- **WHEN** a contributor reads the module development guide -- **THEN** the guide explains the current two-repository model -- **AND** it identifies which code and documentation concerns belong in `specfact-cli` versus `specfact-cli-modules` + +- __WHEN__ a contributor reads the module development guide +- __THEN__ the guide explains the current two-repository model +- __AND__ it identifies which code and documentation concerns belong in `specfact-cli` versus `specfact-cli-modules` ### Requirement: Directory and dependency docs reflect bundle boundaries + Module development, directory-structure, and dependency documentation SHALL describe the current bundle/package layout, canonical repository ownership, and bundle dependency relationships introduced by marketplace-installed official bundles. #### Scenario: Contributor checks structure and dependency guidance -- **WHEN** a contributor reads directory or dependency documentation related to modules -- **THEN** the docs show the current bundle/package boundaries and repository ownership -- **AND** dependency explanations match the marketplace-installed bundle model rather than the former in-repo bundled module layout +- __WHEN__ a contributor reads directory or dependency documentation related to modules +- __THEN__ the docs show the current bundle/package boundaries and repository ownership +- __AND__ dependency explanations match the marketplace-installed bundle model rather than the former in-repo bundled module layout diff --git a/openspec/specs/module-docs-ownership/spec.md b/openspec/specs/module-docs-ownership/spec.md index 5a806ae4..9efcab99 100644 --- a/openspec/specs/module-docs-ownership/spec.md +++ b/openspec/specs/module-docs-ownership/spec.md @@ -1,8 +1,11 @@ # module-docs-ownership Specification ## Purpose + TBD - created by archiving change docs-01-core-modules-docs-alignment. Update Purpose after archive. + ## Requirements + ### Requirement: Core docs declare current and target docs ownership boundaries The documentation SHALL state which documentation concerns remain owned by `specfact-cli` core, which concerns belong to marketplace-installed module bundles, and that module-specific deep docs are canonically owned by `specfact-cli-modules` once published there. @@ -32,4 +35,3 @@ The core documentation set SHALL include a short reference page that explains th - **WHEN** a contributor needs to add or verify a cross-site link - **THEN** they can open `docs/reference/documentation-url-contract.md` in this repository for obligations and a link to the full contract on the modules site - diff --git a/openspec/specs/module-grouping/spec.md b/openspec/specs/module-grouping/spec.md index 998e56e4..d3058538 100644 --- a/openspec/specs/module-grouping/spec.md +++ b/openspec/specs/module-grouping/spec.md @@ -1,8 +1,11 @@ # module-grouping Specification ## Purpose + TBD - created by archiving change module-migration-01-categorize-and-group. Update Purpose after archive. + ## Requirements + ### Requirement: Module-package.yaml declares category metadata Every `module-package.yaml` file SHALL declare four new fields: `category`, `bundle`, `bundle_group_command`, and `bundle_sub_command`. @@ -79,4 +82,3 @@ The registry SHALL validate the four metadata fields on load and reject manifest - **WHEN** the registry attempts to load the module - **THEN** the registry SHALL raise a `ModuleManifestError` - **AND** SHALL include the expected and actual values in the error message - diff --git a/openspec/specs/module-installation/spec.md b/openspec/specs/module-installation/spec.md index b0f5a6e2..9e647f46 100644 --- a/openspec/specs/module-installation/spec.md +++ b/openspec/specs/module-installation/spec.md @@ -1,13 +1,17 @@ # module-installation Specification ## Purpose + TBD - created by archiving change marketplace-01-central-module-registry. Update Purpose after archive. + ## Requirements + ### Requirement: Install command downloads and installs modules The system SHALL provide `specfact module install ` command that downloads, verifies, and installs modules from the registry. #### Scenario: Install module from marketplace + - **WHEN** user runs `specfact module install specfact/backlog` - **THEN** system SHALL fetch registry index - **AND** SHALL download module tarball @@ -17,11 +21,13 @@ The system SHALL provide `specfact module install ` command that down - **AND** SHALL display success message #### Scenario: Install specific version + - **WHEN** user runs `specfact module install specfact/backlog --version 0.29.0` - **THEN** system SHALL install specified version - **AND** SHALL verify core_compatibility with current CLI version #### Scenario: Install module already installed + - **WHEN** user installs module that is already installed - **THEN** system SHALL display message "Module already installed (version X)" - **AND** SHALL suggest using upgrade command @@ -31,6 +37,7 @@ The system SHALL provide `specfact module install ` command that down The system SHALL provide `specfact module uninstall ` command that removes modules from marketplace path. #### Scenario: Uninstall marketplace module + - **WHEN** user runs `specfact module uninstall backlog` - **THEN** system SHALL check if module is from marketplace - **AND** SHALL remove ~/.specfact/marketplace-modules/backlog/ directory @@ -38,6 +45,7 @@ The system SHALL provide `specfact module uninstall ` command that - **AND** SHALL display success message #### Scenario: Attempt to uninstall built-in module + - **WHEN** user attempts to uninstall built-in module - **THEN** system SHALL display error "Cannot uninstall built-in module" - **AND** SHALL NOT modify module @@ -47,6 +55,7 @@ The system SHALL provide `specfact module uninstall ` command that The system SHALL provide `specfact module search ` command that searches registry index by name, description, or tags. #### Scenario: Search modules by keyword + - **WHEN** user runs `specfact module search backlog` - **THEN** system SHALL fetch registry index - **AND** SHALL filter modules matching query in name, description, or tags @@ -57,11 +66,13 @@ The system SHALL provide `specfact module search ` command that searches The system SHALL provide `specfact module list` command that displays modules from all sources with source indicators. #### Scenario: List all modules + - **WHEN** user runs `specfact module list` - **THEN** system SHALL show modules from built-in, marketplace, and custom paths - **AND** SHALL indicate source (built-in/marketplace/custom) for each module #### Scenario: List marketplace modules only + - **WHEN** user runs `specfact module list --source marketplace` - **THEN** system SHALL show only marketplace-installed modules @@ -165,6 +176,7 @@ bumps may contain breaking changes. The system SHALL reject archive members that escape the intended extraction root. #### Scenario: Installer blocks path traversal entries + - **WHEN** a downloaded marketplace tarball contains absolute paths or `..` traversal - **THEN** install SHALL fail before extraction - **AND** SHALL raise a validation error indicating unsafe archive content @@ -174,12 +186,14 @@ The system SHALL reject archive members that escape the intended extraction root The system SHALL extend install command to resolve pip dependencies across all modules before installation. #### Scenario: Install with dependency resolution + - **WHEN** user installs module with pip_dependencies - **THEN** system SHALL resolve dependencies with existing modules - **AND** SHALL fail if conflicts detected - **AND** SHALL install resolved dependencies if resolution succeeds #### Scenario: Force install bypasses dependency resolution + - **WHEN** user runs install with --force flag - **THEN** system SHALL skip dependency resolution - **AND** SHALL log warning about potential conflicts @@ -238,4 +252,3 @@ behaviour and the UX of standard package managers. - **THEN** the system SHALL report that A is not found - **AND** SHALL still attempt to uninstall B - **AND** SHALL exit non-zero if any module failed or was not found - diff --git a/openspec/specs/module-io-contract/spec.md b/openspec/specs/module-io-contract/spec.md index 9e918291..0f07c161 100644 --- a/openspec/specs/module-io-contract/spec.md +++ b/openspec/specs/module-io-contract/spec.md @@ -1,25 +1,32 @@ # module-io-contract Specification ## Purpose + TBD - created by archiving change arch-04-core-contracts-interfaces. Update Purpose after archive. + ## Requirements + ### Requirement: ModuleIOContract protocol defines four core operations The system SHALL provide a `ModuleIOContract` Protocol in `src/specfact_cli/contracts/module_interface.py` that defines four required operations all modules must implement for interacting with ProjectBundle. #### Scenario: Protocol defines import_to_bundle operation + - **WHEN** a module implements ModuleIOContract - **THEN** it MUST provide `import_to_bundle(source: Path, config: dict) -> ProjectBundle` method that converts external format to ProjectBundle #### Scenario: Protocol defines export_from_bundle operation + - **WHEN** a module implements ModuleIOContract - **THEN** it MUST provide `export_from_bundle(bundle: ProjectBundle, target: Path, config: dict) -> None` method that converts ProjectBundle to external format #### Scenario: Protocol defines sync_with_bundle operation + - **WHEN** a module implements ModuleIOContract - **THEN** it MUST provide `sync_with_bundle(bundle: ProjectBundle, external_source: str, config: dict) -> ProjectBundle` method for bidirectional synchronization #### Scenario: Protocol defines validate_bundle operation + - **WHEN** a module implements ModuleIOContract - **THEN** it MUST provide `validate_bundle(bundle: ProjectBundle, rules: dict) -> ValidationReport` method for module-specific validation @@ -28,11 +35,13 @@ The system SHALL provide a `ModuleIOContract` Protocol in `src/specfact_cli/cont The system SHALL use `typing.Protocol` for ModuleIOContract to enable structural subtyping without requiring explicit inheritance. #### Scenario: Module without explicit inheritance satisfies protocol + - **WHEN** a module class implements all four protocol methods with correct signatures - **THEN** basedpyright type checker SHALL recognize it as implementing ModuleIOContract - **AND** no explicit inheritance or registration is required #### Scenario: Module with partial implementation is type-checked + - **WHEN** a module class implements only some protocol methods - **THEN** basedpyright SHALL report protocol violations during type checking - **AND** module registration SHALL detect missing methods via hasattr() checks @@ -42,16 +51,19 @@ The system SHALL use `typing.Protocol` for ModuleIOContract to enable structural The system SHALL enforce that all ModuleIOContract methods accept or return ProjectBundle as the data exchange format. #### Scenario: Import operation returns ProjectBundle + - **WHEN** import_to_bundle is called with valid external source - **THEN** it MUST return a ProjectBundle instance - **AND** the returned bundle SHALL have all required fields populated #### Scenario: Export operation accepts ProjectBundle + - **WHEN** export_from_bundle is called with ProjectBundle - **THEN** it MUST accept ProjectBundle as input - **AND** SHALL NOT require any other data structure for core export logic #### Scenario: Sync operation uses ProjectBundle bidirectionally + - **WHEN** sync_with_bundle is called - **THEN** it MUST accept ProjectBundle as input - **AND** MUST return ProjectBundle as output @@ -62,16 +74,19 @@ The system SHALL enforce that all ModuleIOContract methods accept or return Proj The system SHALL require all ModuleIOContract implementations to use `@icontract` and `@beartype` decorators for runtime validation. #### Scenario: Import method has preconditions + - **WHEN** import_to_bundle is implemented - **THEN** it MUST have `@require` decorator validating source path exists - **AND** MUST have `@beartype` decorator for type checking #### Scenario: Export method has postconditions + - **WHEN** export_from_bundle is implemented - **THEN** it MUST have `@ensure` decorator validating target file was created - **AND** MUST have `@beartype` decorator for type checking #### Scenario: Validate method returns ValidationReport + - **WHEN** validate_bundle is implemented - **THEN** it MUST return ValidationReport instance - **AND** MUST have `@ensure` decorator validating report structure @@ -81,16 +96,19 @@ The system SHALL require all ModuleIOContract implementations to use `@icontract The system SHALL allow modules to implement subsets of ModuleIOContract operations based on their functionality. #### Scenario: Import-only module omits export methods + - **WHEN** a module only supports importing from external systems - **THEN** it MAY implement only import_to_bundle and validate_bundle - **AND** module registration SHALL detect and log supported operations #### Scenario: Sync-only module implements full bidirectional operations + - **WHEN** a module supports bidirectional sync - **THEN** it MUST implement all four operations - **AND** sync_with_bundle SHALL use import_to_bundle and export_from_bundle internally #### Scenario: Validation-only module omits IO operations + - **WHEN** a module only validates bundles without external IO - **THEN** it MAY implement only validate_bundle - **AND** SHALL NOT be required to implement import/export/sync operations @@ -100,14 +118,16 @@ The system SHALL allow modules to implement subsets of ModuleIOContract operatio The system SHALL provide a `ValidationReport` Pydantic model for structured validation results. #### Scenario: ValidationReport has status field + - **WHEN** validate_bundle returns ValidationReport - **THEN** report MUST have `status` field with values: "passed", "failed", "warnings" #### Scenario: ValidationReport has violations list + - **WHEN** validation finds issues - **THEN** report MUST have `violations` list of dicts with keys: severity, message, location #### Scenario: ValidationReport has summary field + - **WHEN** validation completes - **THEN** report MUST have `summary` field with counts: total_checks, passed, failed, warnings - diff --git a/openspec/specs/module-lifecycle-management/spec.md b/openspec/specs/module-lifecycle-management/spec.md index 8588c915..7c0f4537 100644 --- a/openspec/specs/module-lifecycle-management/spec.md +++ b/openspec/specs/module-lifecycle-management/spec.md @@ -1,8 +1,11 @@ # module-lifecycle-management Specification ## Purpose + TBD - created by archiving change arch-03-module-lifecycle-management. Update Purpose after archive. + ## Requirements + ### Requirement: Shared helper extraction from cross-module command imports The system SHALL provide shared bundle conversion and constitution helper utilities under core `specfact_cli.utils` so modules do not import private non-`app` symbols from other modules' `src.commands`. @@ -188,21 +191,25 @@ The test suite SHALL fail when any module imports non-`app` symbols from another The system SHALL extend registration-time validation to check if module implements ModuleIOContract and log protocol compliance status. #### Scenario: Registration checks for protocol implementation + - **WHEN** module package is registered - **THEN** system SHALL inspect module for ModuleIOContract implementation - **AND** SHALL use hasattr() to check for import_to_bundle, export_from_bundle, sync_with_bundle, validate_bundle methods #### Scenario: Full protocol implementation is logged + - **WHEN** module implements all four ModuleIOContract methods - **THEN** registration SHALL log at INFO level: "Module X: ModuleIOContract fully implemented" - **AND** SHALL store protocol_operations: ["import", "export", "sync", "validate"] in metadata #### Scenario: Partial protocol implementation is logged with operations + - **WHEN** module implements only import_to_bundle and validate_bundle - **THEN** registration SHALL log at INFO level: "Module X: ModuleIOContract partial (import, validate)" - **AND** SHALL store protocol_operations: ["import", "validate"] in metadata #### Scenario: No protocol implementation logs legacy mode + - **WHEN** module does not implement any ModuleIOContract methods - **THEN** registration SHALL log at WARNING level: "Module X: No ModuleIOContract (legacy mode)" - **AND** SHALL store protocol_operations: [] in metadata @@ -213,17 +220,20 @@ The system SHALL extend registration-time validation to check if module implemen The system SHALL extend registration validation to check ProjectBundle schema version compatibility if module declares schema_version in manifest. #### Scenario: Compatible schema version allows registration + - **WHEN** module declares schema_version: "1" and ProjectBundle.schema_version is "1" - **THEN** registration SHALL succeed - **AND** SHALL log: "Module X: Schema version 1 (compatible)" #### Scenario: Incompatible schema version skips registration + - **WHEN** module declares schema_version: "2" and ProjectBundle.schema_version is "1" - **THEN** registration SHALL skip module - **AND** SHALL log at WARNING level: "Module X: Schema version 2 required, but current is 1 (skipped)" - **AND** skipped module SHALL be listed in registration summary #### Scenario: Missing schema version assumes compatibility + - **WHEN** module omits schema_version from manifest - **THEN** registration SHALL assume current ProjectBundle schema - **AND** SHALL log at DEBUG level: "Module X: No schema version declared (assuming current)" @@ -234,11 +244,13 @@ The system SHALL extend registration validation to check ProjectBundle schema ve The system SHALL extend registration summary output to include protocol compliance statistics. #### Scenario: Summary counts protocol-compliant modules + - **WHEN** registration completes - **THEN** summary SHALL include counts: "Protocol-compliant: 4/5 modules" - **AND** SHALL list modules by status: Full (3), Partial (1), Legacy (1) #### Scenario: Summary warns about legacy modules + - **WHEN** registration finds modules without ModuleIOContract - **THEN** summary SHALL include warning: "1 module(s) in legacy mode (no ModuleIOContract)" - **AND** SHALL recommend updating to ModuleIOContract for marketplace compatibility @@ -324,27 +336,32 @@ The system SHALL degrade gracefully when one module fails trust checks. The system SHALL extend module registration to load schema_extensions from manifests, validate namespace uniqueness, and populate the global extension registry. #### Scenario: Registration loads schema_extensions from manifest + - **WHEN** module registration loads module-package.yaml - **THEN** system SHALL parse schema_extensions section if present - **AND** SHALL extract target models, field names, types, descriptions #### Scenario: Registration validates extension namespace uniqueness + - **WHEN** module declares schema extension with field name - **THEN** system SHALL check global extension registry for conflicts - **AND** SHALL reject registration if `module.field` already declared by another module - **AND** SHALL log error with conflicting module name #### Scenario: Registration populates global extension registry + - **WHEN** module registration succeeds with schema_extensions - **THEN** system SHALL add extensions to global registry - **AND** registry SHALL map module_name β†’ extensions metadata #### Scenario: Registration logs registered extensions + - **WHEN** module with schema_extensions completes registration - **THEN** system SHALL log: "Module X registered N schema extensions for [Feature, ProjectBundle]" - **AND** SHALL log at debug level the specific fields registered #### Scenario: Registration skips invalid extension declarations + - **WHEN** module declares extension with malformed field name (e.g., contains dots) - **THEN** system SHALL log warning - **AND** SHALL skip that extension @@ -355,16 +372,19 @@ The system SHALL extend module registration to load schema_extensions from manif The system SHALL extend registration to handle modules from built-in, marketplace, and custom sources with appropriate lifecycle rules. #### Scenario: Marketplace modules can be uninstalled + - **WHEN** module from marketplace is registered - **THEN** system SHALL mark it as uninstallable - **AND** SHALL allow removal via uninstall command #### Scenario: Built-in modules cannot be uninstalled + - **WHEN** module from built-in source is registered - **THEN** system SHALL mark it as non-uninstallable - **AND** SHALL prevent removal via uninstall command #### Scenario: Registration validates namespace for marketplace modules + - **WHEN** marketplace module is registered - **THEN** system SHALL validate id uses "namespace/name" format - **AND** SHALL log warning if flat name used @@ -374,11 +394,13 @@ The system SHALL extend registration to handle modules from built-in, marketplac The system SHALL keep existing init-based lifecycle flags functional while introducing `specfact module` as the canonical lifecycle command surface. #### Scenario: init lifecycle flags remain functional + - **WHEN** user runs `specfact init --list-modules` or `--enable-module/--disable-module` - **THEN** system SHALL preserve current lifecycle behavior and state updates - **AND** SHALL provide deprecation guidance toward `specfact module` commands #### Scenario: module command is canonical lifecycle surface + - **WHEN** user runs `specfact module list` or lifecycle operations - **THEN** system SHALL provide equivalent lifecycle management capabilities - **AND** documentation SHALL reference `specfact module` as primary UX @@ -388,14 +410,15 @@ The system SHALL keep existing init-based lifecycle flags functional while intro The system SHALL validate namespace format during module registration for marketplace-sourced modules. #### Scenario: Marketplace module must use namespace format + - **WHEN** module from marketplace is registered - **THEN** id SHALL match format "namespace/name" - **AND** namespace SHALL be alphanumeric with hyphens - **AND** name SHALL be alphanumeric with hyphens #### Scenario: Namespace collision detected + - **WHEN** registering module with id that conflicts with existing module - **THEN** system SHALL log error "Module namespace collision: {id}" - **AND** SHALL prevent registration - **AND** SHALL suggest using alias system for disambiguation - diff --git a/openspec/specs/module-marketplace-registry/spec.md b/openspec/specs/module-marketplace-registry/spec.md index 23c87504..6878c7b3 100644 --- a/openspec/specs/module-marketplace-registry/spec.md +++ b/openspec/specs/module-marketplace-registry/spec.md @@ -1,24 +1,30 @@ # module-marketplace-registry Specification ## Purpose + TBD - created by archiving change marketplace-01-central-module-registry. Update Purpose after archive. + ## Requirements + ### Requirement: Registry index schema with module metadata The system SHALL define an index.json schema for the central registry containing module metadata including ID, namespace, version, download URLs, and checksums. #### Scenario: Index includes module with full metadata + - **WHEN** registry index.json is parsed - **THEN** it SHALL include schema_version field - **AND** SHALL include modules array with module entries - **AND** each module SHALL have: id, namespace, name, description, latest_version, core_compatibility, download_url, checksum_sha256 #### Scenario: Module ID uses namespace format + - **WHEN** module is listed in registry - **THEN** id SHALL use format "namespace/name" (e.g., "specfact/backlog") - **AND** namespace SHALL match separate namespace field #### Scenario: Core compatibility uses PEP 440 specifier + - **WHEN** module declares core_compatibility - **THEN** it SHALL use PEP 440 specifier format (e.g., ">=0.28.0,<1.0.0") - **AND** SHALL be validated during module installation @@ -28,18 +34,21 @@ The system SHALL define an index.json schema for the central registry containing The system SHALL implement a registry client that fetches index.json from the GitHub repository. #### Scenario: Client fetches registry index + - **WHEN** client calls fetch_registry_index() - **THEN** it SHALL request index.json from GitHub raw content URL - **AND** SHALL parse JSON response - **AND** SHALL return dict with schema_version and modules #### Scenario: Network unavailable during fetch + - **WHEN** client attempts to fetch index but network is unavailable - **THEN** it SHALL log warning "Registry unavailable, using offline mode" - **AND** SHALL NOT raise exception - **AND** SHALL return None or empty index #### Scenario: Invalid JSON in registry index + - **WHEN** registry index contains invalid JSON - **THEN** client SHALL log error with parse details - **AND** SHALL raise ValueError with message "Invalid registry index format" @@ -49,18 +58,21 @@ The system SHALL implement a registry client that fetches index.json from the Gi The system SHALL download module tarballs from registry URLs and verify checksums before extraction. #### Scenario: Download module tarball + - **WHEN** download_module() is called with module_id and version - **THEN** system SHALL look up module in registry index - **AND** SHALL download tarball from download_url to temp directory - **AND** SHALL verify checksum matches checksum_sha256 from index #### Scenario: Checksum mismatch detected + - **WHEN** downloaded tarball checksum does not match index - **THEN** system SHALL delete downloaded file - **AND** SHALL raise SecurityError with message "Checksum mismatch for module X" - **AND** SHALL NOT proceed with installation #### Scenario: Module not found in registry + - **WHEN** download_module() is called with non-existent module_id - **THEN** system SHALL raise ValueError with message "Module 'X' not found in registry" - **AND** SHALL suggest using `specfact module search` to find available modules @@ -70,13 +82,14 @@ The system SHALL download module tarballs from registry URLs and verify checksum The system SHALL support offline operation with graceful degradation when registry is unavailable. #### Scenario: Registry fetch fails gracefully + - **WHEN** registry fetch fails due to network issues - **THEN** system SHALL log warning - **AND** SHALL continue with built-in modules only - **AND** SHALL NOT block CLI functionality #### Scenario: Install command fails offline + - **WHEN** user runs install command but registry unavailable - **THEN** system SHALL display error "Cannot install from marketplace (offline)" - **AND** SHALL suggest installing from local tarball (future feature) - diff --git a/openspec/specs/module-owned-ide-prompts/spec.md b/openspec/specs/module-owned-ide-prompts/spec.md index 739bc3d5..7a33e6b5 100644 --- a/openspec/specs/module-owned-ide-prompts/spec.md +++ b/openspec/specs/module-owned-ide-prompts/spec.md @@ -1,44 +1,55 @@ # module-owned-ide-prompts Specification ## Purpose + TBD - created by archiving change packaging-02-cross-platform-runtime-and-module-resources. Update Purpose after archive. + ## Requirements + ### Requirement: IDE prompt export SHALL use installed module resources + `specfact init ide` SHALL discover prompt templates from installed module packages and their packaged resource directories. The export flow SHALL not depend on workflow prompt files stored under the core CLI package for bundle-owned commands. #### Scenario: Installed bundle contributes prompt resources + - **WHEN** an installed module exposes packaged prompt resources for IDE export - **THEN** `specfact init ide` discovers that module's prompt directory from the installed module location - **AND** copies the prompt files from that module-owned resource path into the selected IDE folder #### Scenario: Core package does not masquerade as owner of bundle prompts + - **WHEN** workflow prompts exist only for bundle/module-owned commands - **THEN** the export catalog excludes equivalent core-owned fallback prompt files - **AND** prompt provenance remains attributable to the owning module ### Requirement: Missing prompt assets SHALL fail clearly + If a selected or installed module is expected to provide prompt resources but no packaged prompt directory is available, `specfact init ide` SHALL report an actionable error or warning that identifies the owning module and the missing resource path. #### Scenario: Selected module has no packaged prompt directory + - **WHEN** `specfact init ide` evaluates an installed module that should contribute prompts but its packaged prompt resource directory is absent - **THEN** the command reports which module is incomplete - **AND** the message explains that prompt resources must ship with the owning module package #### Scenario: Prompt discovery feeds later source selection + - **WHEN** the prompt export catalog is built for a repository with multiple installed modules - **THEN** the discovered prompt sources are available for later interactive or non-interactive source selection features - **AND** the catalog preserves module-level provenance for each exported prompt ### Requirement: Core init flows SHALL use installed module-owned template resources + When a setup or install flow needs a non-prompt resource that is owned by an extracted bundle, the core CLI SHALL resolve that asset from the installed bundle package instead of from a core-owned fallback directory. #### Scenario: Backlog field mapping templates resolve from installed backlog bundle + - **WHEN** a core init or setup flow needs backlog field mapping templates - **THEN** the CLI resolves those templates from the installed backlog bundle resource path - **AND** the flow does not require a canonical source copy under the core CLI repository #### Scenario: Missing module-owned template asset fails clearly + - **WHEN** a required installed bundle resource path for a module-owned template is absent - **THEN** the CLI reports which bundle-owned asset is missing - **AND** the message directs the user toward installing or updating the owning bundle - diff --git a/openspec/specs/module-package-separation/spec.md b/openspec/specs/module-package-separation/spec.md index 38b8239c..87287790 100644 --- a/openspec/specs/module-package-separation/spec.md +++ b/openspec/specs/module-package-separation/spec.md @@ -1,8 +1,11 @@ # module-package-separation Specification ## Purpose + TBD - created by archiving change arch-02-module-package-separation. Update Purpose after archive. + ## Requirements + ### Requirement: Module-local command implementation The system SHALL store each CLI command implementation inside its owning module package at `src/specfact_cli/modules//src/commands.py`. @@ -64,4 +67,3 @@ The system SHALL keep `module_dependencies` accurate in each module package mani **Then** the imported module is declared under `module_dependencies` **And** if no cross-module imports exist, `module_dependencies` remains empty - diff --git a/openspec/specs/module-packages/spec.md b/openspec/specs/module-packages/spec.md index 5a530832..47e140b7 100644 --- a/openspec/specs/module-packages/spec.md +++ b/openspec/specs/module-packages/spec.md @@ -1,8 +1,11 @@ # module-packages Specification ## Purpose + TBD - created by archiving change arch-01-cli-modular-command-registry. Update Purpose after archive. + ## Requirements + ### Requirement: Logical Packages by Feature with Dedicated Folder Structure The CLI SHALL group functionality into **logical module packages** by feature (e.g. "backlog refine", "backlog daily", "validate sidecar"). Each package SHALL live in a dedicated folder under a modules root and SHALL include its own **metadata**, **src**, **resources** (prompts, templates), and **tests**. Resources that are used only by one feature SHALL belong to that package; shared resources remain in core or a shared package. @@ -63,16 +66,19 @@ The codebase SHALL distinguish **core** (bootstrapping, CommandRegistry, init sc The system SHALL extend `ModulePackageMetadata` to include a `schema_version` field indicating which ProjectBundle schema version the module is compatible with. #### Scenario: Metadata declares schema compatibility + - **WHEN** module-package.yaml is loaded - **THEN** it MAY include `schema_version: "1"` field - **AND** module registration SHALL validate compatibility with ProjectBundle.schema_version #### Scenario: Missing schema_version defaults to current + - **WHEN** module-package.yaml omits schema_version - **THEN** registration SHALL assume current ProjectBundle schema version - **AND** SHALL log warning recommending explicit declaration #### Scenario: Incompatible schema_version blocks registration + - **WHEN** module declares schema_version: "2" but ProjectBundle is version "1" - **THEN** registration SHALL skip module with warning - **AND** SHALL log: "Module X requires schema version 2, but current is 1" @@ -82,21 +88,25 @@ The system SHALL extend `ModulePackageMetadata` to include a `schema_version` fi The system SHALL extend module discovery to check if module implements ModuleIOContract protocol and log supported operations. #### Scenario: Discovery detects protocol implementation + - **WHEN** module package is discovered and loaded - **THEN** registry SHALL check if module class implements ModuleIOContract - **AND** SHALL use hasattr() to detect which operations are supported #### Scenario: Module with protocol is logged as compliant + - **WHEN** module implements all four ModuleIOContract methods - **THEN** registration SHALL log: "Module X implements ModuleIOContract (full)" - **AND** SHALL store supported operations in module metadata #### Scenario: Module without protocol is logged as legacy + - **WHEN** module does not implement ModuleIOContract - **THEN** registration SHALL log warning: "Module X does not implement ModuleIOContract (legacy mode)" - **AND** SHALL still register module for backward compatibility #### Scenario: Module with partial protocol is logged with operations + - **WHEN** module implements import_to_bundle and validate_bundle only - **THEN** registration SHALL log: "Module X implements ModuleIOContract (partial: import, validate)" - **AND** SHALL allow partial implementation @@ -106,11 +116,13 @@ The system SHALL extend module discovery to check if module implements ModuleIOC The system SHALL update `src/specfact_cli/models/module_package.py` to include schema_version and protocol_compliance fields. #### Scenario: ModulePackageMetadata has schema_version field + - **WHEN** ModulePackageMetadata is instantiated - **THEN** it SHALL have optional `schema_version: str | None` field - **AND** default value SHALL be None (implying current schema) #### Scenario: ModulePackageMetadata tracks protocol operations + - **WHEN** module is discovered - **THEN** metadata SHALL have `protocol_operations: list[str]` field - **AND** SHALL contain names of implemented operations: ["import", "export", "sync", "validate"] @@ -193,22 +205,26 @@ The system SHALL support versioned dependency declarations for both module and p The system SHALL extend `ModulePackageMetadata` to include optional `schema_extensions` field declaring fields the module adds to core models. #### Scenario: Manifest schema includes schema_extensions + - **WHEN** module-package.yaml is parsed - **THEN** it MAY include `schema_extensions` array - **AND** each entry SHALL specify: target model name, field definitions with type/description #### Scenario: Schema extension for Feature model + - **WHEN** module declares schema_extensions for Feature - **THEN** manifest SHALL list fields being added - **AND** each field SHALL include type hint and description - **AND** module namespace is implicit from module name #### Scenario: Schema extension for ProjectBundle model + - **WHEN** module declares schema_extensions for ProjectBundle - **THEN** manifest SHALL list fields being added - **AND** each field SHALL include type hint and description #### Scenario: Module without schema_extensions remains valid + - **WHEN** module-package.yaml omits schema_extensions - **THEN** module SHALL load successfully - **AND** no extensions registered for that module @@ -218,12 +234,13 @@ The system SHALL extend `ModulePackageMetadata` to include optional `schema_exte The system SHALL extend module discovery to scan built-in, marketplace, and custom paths with source tracking. #### Scenario: Discovery function returns source information + - **WHEN** discover_package_metadata() finds a module - **THEN** it SHALL include source field in metadata - **AND** source SHALL be "builtin", "marketplace", or "custom" #### Scenario: Registry stores module source + - **WHEN** module is registered - **THEN** registry SHALL persist source information - **AND** SHALL be queryable via module list command - diff --git a/openspec/specs/module-publishing/spec.md b/openspec/specs/module-publishing/spec.md index 1c23afd6..cf517070 100644 --- a/openspec/specs/module-publishing/spec.md +++ b/openspec/specs/module-publishing/spec.md @@ -1,19 +1,24 @@ # module-publishing Specification ## Purpose + TBD - created by archiving change marketplace-02-advanced-marketplace-features. Update Purpose after archive. + ## Requirements + ### Requirement: Publishing script validates module structure The system SHALL provide scripts/publish-module.py that validates module before publishing. #### Scenario: Validate module structure + - **WHEN** publish script runs on module directory - **THEN** it SHALL verify module-package.yaml exists and is valid - **AND** SHALL verify namespace format for marketplace modules - **AND** SHALL verify all required files present #### Scenario: Create module tarball + - **WHEN** validation passes - **THEN** script SHALL create tarball with format: {module-name}-{version}.tar.gz - **AND** SHALL include only necessary files (exclude tests, .git, etc.) @@ -23,10 +28,10 @@ The system SHALL provide scripts/publish-module.py that validates module before The system SHALL provide .github/workflows/publish-modules.yml that automates publishing. #### Scenario: Publish on release tag + - **WHEN** git tag matches pattern `{module}-v{version}` is pushed - **THEN** workflow SHALL run publish-module.py for that module - **AND** SHALL generate checksum - **AND** SHALL sign tarball (if signing configured) - **AND** SHALL update registry index.json - **AND** SHALL create pull request to registry repo - diff --git a/openspec/specs/module-removal-gate/spec.md b/openspec/specs/module-removal-gate/spec.md index e5652445..e54bec16 100644 --- a/openspec/specs/module-removal-gate/spec.md +++ b/openspec/specs/module-removal-gate/spec.md @@ -1,8 +1,11 @@ # module-removal-gate Specification ## Purpose + TBD - created by archiving change module-migration-03-core-slimming. Update Purpose after archive. + ## Requirements + ### Requirement: A verification gate script confirms bundle availability before any module deletion A script SHALL exist at `scripts/verify-bundle-published.py` that, given a list of module names, checks that the corresponding bundle is published in the marketplace registry, carries a valid Ed25519 signature, and is installable (the download URL resolves and the tarball passes integrity verification). @@ -108,4 +111,3 @@ The gate is not specific to this change. It SHALL be reusable for any future rem - **WHEN** `python scripts/verify-bundle-published.py --modules migrate` is run - **THEN** the script SHALL check that `specfact-project` (the bundle containing `migrate`) is published and verified - **AND** SHALL exit 0 if the check passes, 1 if it fails - diff --git a/openspec/specs/module-security/spec.md b/openspec/specs/module-security/spec.md index 3ca81741..2e360d0e 100644 --- a/openspec/specs/module-security/spec.md +++ b/openspec/specs/module-security/spec.md @@ -1,8 +1,11 @@ # module-security Specification ## Purpose + TBD - created by archiving change arch-06-enhanced-manifest-security. Update Purpose after archive. + ## Requirements + ### Requirement: Module artifacts SHALL be verified for integrity before installation The system SHALL verify module artifact checksums before extraction or registration. @@ -50,4 +53,3 @@ The system SHALL require explicit allow-unsigned policy override when strict tru - **WHEN** user sets allow-unsigned override - **THEN** installer MAY continue after checksum validation - **AND** system SHALL emit warning/audit logs. - diff --git a/openspec/specs/multi-location-discovery/spec.md b/openspec/specs/multi-location-discovery/spec.md index 4e11a27e..fff59bb1 100644 --- a/openspec/specs/multi-location-discovery/spec.md +++ b/openspec/specs/multi-location-discovery/spec.md @@ -1,24 +1,30 @@ # multi-location-discovery Specification ## Purpose + TBD - created by archiving change marketplace-01-central-module-registry. Update Purpose after archive. + ## Requirements + ### Requirement: Discover modules from multiple paths The system SHALL discover modules from built-in, marketplace, and custom paths in priority order. #### Scenario: Discovery scans all three locations + - **WHEN** module discovery runs - **THEN** system SHALL scan {site-packages}/specfact_cli/modules/ - **AND** SHALL scan ~/.specfact/marketplace-modules/ if exists - **AND** SHALL scan ~/.specfact/custom-modules/ if exists #### Scenario: Built-in modules take priority + - **WHEN** module "backlog" exists in both built-in and marketplace - **THEN** system SHALL use built-in version - **AND** SHALL log warning about shadowed marketplace module #### Scenario: Marketplace modules discovered when no built-in + - **WHEN** module exists in marketplace but not built-in - **THEN** system SHALL discover and register marketplace module @@ -27,11 +33,13 @@ The system SHALL discover modules from built-in, marketplace, and custom paths i The system SHALL track the source (built-in/marketplace/custom) for each discovered module. #### Scenario: Module metadata includes source + - **WHEN** module is discovered - **THEN** system SHALL record source in module metadata - **AND** source SHALL be one of: "builtin", "marketplace", "custom" #### Scenario: List command shows module source + - **WHEN** user runs `specfact module list` - **THEN** each module SHALL display source indicator - **AND** built-in modules SHALL be marked as "[built-in]" @@ -41,12 +49,13 @@ The system SHALL track the source (built-in/marketplace/custom) for each discove The system SHALL handle missing marketplace or custom paths without errors. #### Scenario: Marketplace path does not exist + - **WHEN** ~/.specfact/marketplace-modules/ does not exist - **THEN** discovery SHALL continue with built-in modules only - **AND** SHALL NOT log warning (normal state) #### Scenario: Custom path does not exist + - **WHEN** ~/.specfact/custom-modules/ does not exist - **THEN** discovery SHALL continue normally - **AND** SHALL NOT raise exception - diff --git a/openspec/specs/official-bundle-tier/spec.md b/openspec/specs/official-bundle-tier/spec.md index 575f3d49..02056cfe 100644 --- a/openspec/specs/official-bundle-tier/spec.md +++ b/openspec/specs/official-bundle-tier/spec.md @@ -1,8 +1,11 @@ # official-bundle-tier Specification ## Purpose + TBD - created by archiving change module-migration-02-bundle-extraction. Update Purpose after archive. + ## Requirements + ### Requirement: Official-tier bundles declare tier and publisher in module-package.yaml and index.json Every official bundle manifest SHALL declare `tier: official` and `publisher: nold-ai`. @@ -113,4 +116,3 @@ Users SHALL be able to distinguish official-tier bundles from community-tier mod - **GIVEN** a user installs an official bundle - **WHEN** installation completes successfully - **THEN** the CLI output SHALL include a confirmation line indicating official-tier verification passed (e.g., "Verified: official (nold-ai) β€” SHA-256 and Ed25519 signature OK") - diff --git a/openspec/specs/package-distribution/spec.md b/openspec/specs/package-distribution/spec.md index e5428fd6..fd0ed46c 100644 --- a/openspec/specs/package-distribution/spec.md +++ b/openspec/specs/package-distribution/spec.md @@ -1,21 +1,25 @@ # package-distribution Specification ## Purpose + TBD - created by archiving change packaging-01-wheel-package-inclusion. Update Purpose after archive. + ## Requirements + ### Requirement: Released wheel includes core CLI package The published wheel MUST include the importable `specfact_cli` Python package required by declared console scripts. #### Scenario: Wheel contains core CLI module + - **GIVEN** a wheel is built from the repository release configuration - **WHEN** its contents are inspected - **THEN** it includes `specfact_cli/cli.py` - **AND** it includes `specfact_cli/__init__.py` #### Scenario: Console scripts target importable CLI entrypoint + - **GIVEN** the built distribution metadata - **WHEN** console script entrypoints are inspected - **THEN** both `specfact` and `specfact-cli` resolve to `specfact_cli.cli:cli_main` - **AND** importing `specfact_cli.cli` from the built artifact succeeds - diff --git a/openspec/specs/patch-mode/spec.md b/openspec/specs/patch-mode/spec.md index 45891bb7..d41b21d5 100644 --- a/openspec/specs/patch-mode/spec.md +++ b/openspec/specs/patch-mode/spec.md @@ -1,8 +1,11 @@ # patch-mode Specification ## Purpose + TBD - created by archiving change patch-mode-01-preview-apply. Update Purpose after archive. + ## Requirements + ### Requirement: Patch generation (backlog, OpenSpec, config) The system SHALL support generating unified diffs for: backlog issue body updates (AC, missing fields), OpenSpec proposal/spec updates, config updates (policy, mapping templates). Default behavior SHALL be generate-only (no apply, no write). @@ -44,4 +47,3 @@ The system SHALL provide `specfact patch apply --write` (or equivalent) that upd - **THEN** upstream write path executes only after confirmation - **AND** repeated invocation with the same operation key does not create duplicate writes/comments - **AND** failures in write orchestration surface clear non-zero error outcomes. - diff --git a/openspec/specs/policy-engine/spec.md b/openspec/specs/policy-engine/spec.md index ebdafaf1..7ad74c53 100644 --- a/openspec/specs/policy-engine/spec.md +++ b/openspec/specs/policy-engine/spec.md @@ -1,8 +1,11 @@ # policy-engine Specification ## Purpose + TBD - created by archiving change policy-engine-01-unified-framework. Update Purpose after archive. + ## Requirements + ### Requirement: Policy validate (deterministic, hard failures) The system SHALL provide `specfact policy validate` that runs policy rules deterministically and reports hard failures (rule id, severity, evidence pointer, recommended action). It SHALL run without network access when using snapshots. @@ -276,4 +279,3 @@ The system SHALL provide actionable format/documentation hints when `specfact po **Then**: The error includes the parse/validation failure reason **And**: The output includes a hint to the policy config format documentation. - diff --git a/openspec/specs/portable-review-adoption/spec.md b/openspec/specs/portable-review-adoption/spec.md index 495d0539..e6c7c98e 100644 --- a/openspec/specs/portable-review-adoption/spec.md +++ b/openspec/specs/portable-review-adoption/spec.md @@ -1,31 +1,38 @@ # portable-review-adoption Specification ## Purpose + TBD - created by archiving change code-review-09-f4-automation-upgrade. Update Purpose after archive. + ## Requirements + ### Requirement: Portable Review-Gate Adoption Guidance + The system SHALL document how to add the same `specfact code review run` pre-commit gate to other projects, including optional house-rules workflow usage and the default local JSON ledger behavior with optional configured backend support. #### Scenario: Documentation shows a reusable pre-commit configuration + - **GIVEN** a developer wants to add code review gating to another project - **WHEN** they read the code-review module documentation - **THEN** they can copy a concrete pre-commit configuration that runs `specfact code review run` before commit success #### Scenario: Documentation explains optional house-rules integration + - **GIVEN** a project maintains a `house_rules` skill file - **WHEN** the developer follows the adoption guidance - **THEN** the documentation explains how to use that guidance in the review workflow without making it mandatory #### Scenario: Documentation explains JSON-first ledger behavior + - **GIVEN** a developer uses the review gate on a local or offline project - **WHEN** they read the adoption guidance - **THEN** the documentation states that the ledger works with local JSON storage by default and may use Supabase or another backend only when configured #### Scenario: Documentation explains commit-blocking semantics + - **GIVEN** a developer adopts the review gate in another repository - **WHEN** they read the guidance - **THEN** they understand that only blocking review verdicts fail the commit while advisory verdicts remain commit-green - diff --git a/openspec/specs/pre-commit-review-gate/spec.md b/openspec/specs/pre-commit-review-gate/spec.md index 15c686c2..b6324e36 100644 --- a/openspec/specs/pre-commit-review-gate/spec.md +++ b/openspec/specs/pre-commit-review-gate/spec.md @@ -1,35 +1,43 @@ # pre-commit-review-gate Specification ## Purpose + TBD - created by archiving change code-review-09-f4-automation-upgrade. Update Purpose after archive. + ## Requirements + ### Requirement: Repository Pre-Commit Review Gate + The system SHALL integrate `specfact code review run` into this repository's pre-commit workflow so commits are blocked when the review verdict is `FAIL` and allowed to proceed when the verdict is `PASS` or `PASS_WITH_ADVISORY`. #### Scenario: Pre-commit passes when review verdict is PASS + - **GIVEN** staged repository files produce a `PASS` verdict - **WHEN** the repository pre-commit workflow runs the review gate - **THEN** the hook exits successfully and the commit may proceed #### Scenario: Pre-commit passes when review verdict is PASS_WITH_ADVISORY + - **GIVEN** staged repository files produce a `PASS_WITH_ADVISORY` verdict - **WHEN** the repository pre-commit workflow runs the review gate - **THEN** the hook exits successfully and the commit may proceed #### Scenario: Pre-commit blocks commit when review verdict is FAIL + - **GIVEN** staged repository files produce a `FAIL` verdict - **WHEN** the repository pre-commit workflow runs the review gate - **THEN** the hook exits non-zero and the commit is blocked #### Scenario: Review gate only targets relevant staged files + - **GIVEN** a commit contains staged files and non-code staged files - **WHEN** the repository pre-commit workflow runs the review gate - **THEN** the command reviews only the relevant staged source files instead of the full repository #### Scenario: Missing review command surfaces actionable setup guidance + - **GIVEN** the local environment cannot run `specfact code review run` - **WHEN** the repository pre-commit workflow runs the review gate - **THEN** the hook exits non-zero with setup guidance instead of failing silently - diff --git a/openspec/specs/profile-presets/spec.md b/openspec/specs/profile-presets/spec.md index c698beaa..1f32f192 100644 --- a/openspec/specs/profile-presets/spec.md +++ b/openspec/specs/profile-presets/spec.md @@ -1,8 +1,11 @@ # profile-presets Specification ## Purpose + TBD - created by archiving change module-migration-03-core-slimming. Update Purpose after archive. + ## Requirements + ### Requirement: `specfact init` enforces bundle selection on a fresh install On a fresh install with no bundles installed, `specfact init` SHALL NOT complete workspace initialisation until the user has selected and installed at least one bundle (or the user explicitly confirms the core-only install). @@ -130,4 +133,3 @@ The `--install all` shorthand, introduced by `first-run-selection` (module-migra - **WHEN** the pipeline runs after the core slimming upgrade - **THEN** all 21 commands SHALL be available after the init step completes - **AND** the pipeline SHALL not require any changes to continue functioning - diff --git a/openspec/specs/project-codebase-ownership/spec.md b/openspec/specs/project-codebase-ownership/spec.md index 6543d29e..ae8c1ba5 100644 --- a/openspec/specs/project-codebase-ownership/spec.md +++ b/openspec/specs/project-codebase-ownership/spec.md @@ -1,18 +1,23 @@ # project-codebase-ownership Specification ## Purpose + TBD - created by archiving change module-migration-11-project-codebase-ownership-realignment. Update Purpose after archive. + ## Requirements + ### Requirement: Codebase Commands Own Code-First Brownfield Workflows The system SHALL treat commands whose primary input is a source codebase or runtime code evidence as `code` category commands. #### Scenario: Brownfield import is code-owned + - **WHEN** the user runs the canonical code-first brownfield import workflow - **THEN** the workflow resolves from the `specfact code ...` command surface - **AND** `specfact code import` is treated as the canonical codebase-owned entrypoint rather than a project-owned path in the target command model. #### Scenario: Compatibility alias is transitional only + - **GIVEN** a temporary compatibility alias exists for a pre-realignment path such as `specfact project import from-code` - **WHEN** the command is invoked during the migration window - **THEN** the system routes to the code-owned implementation @@ -23,6 +28,7 @@ The system SHALL treat commands whose primary input is a source codebase or runt The system SHALL reserve the `project` category for commands whose primary subject is the SpecFact project bundle/workspace and its editable artifacts. #### Scenario: Project surface manages bundle artifacts + - **WHEN** a command primarily selects, reviews, edits, imports, exports, migrates, or otherwise manages SpecFact project bundle artifacts - **THEN** that command belongs to the `specfact project ...` surface - **AND** the command is not classified as codebase-owned solely because the artifact may later be synchronized with source code. @@ -32,6 +38,7 @@ The system SHALL reserve the `project` category for commands whose primary subje Subsystems that implement code-first brownfield analysis SHALL have one documented canonical bundle owner. #### Scenario: Analysis subsystems align with codebase ownership + - **WHEN** bundle ownership is resolved for brownfield analysis internals - **THEN** `analyzers`, `comparators`, brownfield-oriented `parsers`, and related import-analysis helpers are assigned to the codebase owner unless an explicit documented exception exists - **AND** migration plans, runtime registration, and docs do not describe contradictory owners for the same subsystem family. @@ -41,14 +48,15 @@ Subsystems that implement code-first brownfield analysis SHALL have one document Pending OpenSpec changes that touch command surface, docs, prompts, migration cleanup, or issue planning SHALL align with the canonical post-migration ownership model instead of reintroducing monolithic `specfact-cli` module ownership by implication. #### Scenario: Active change does not finalize conflicting import ownership + - **GIVEN** an active pending change updates grouped command paths or release-facing docs - **WHEN** that change references brownfield import ownership - **THEN** it references the canonical owner defined by this change - **AND** it does not re-establish a conflicting public command path or subsystem owner by implication. #### Scenario: Active proposal does not preserve obsolete in-repo module paths + - **GIVEN** an active proposal still describes implementation under `modules//` in `specfact-cli` for a capability now owned by a bundle in `specfact-cli-modules` - **WHEN** maintainers reconcile the proposal against the current architecture - **THEN** the proposal is updated to the correct repository and bundle ownership model - **AND** any remaining core-repo scope is reduced to the shared runtime, contract, or integration surface that core still owns. - diff --git a/openspec/specs/prompt-resource-sync/spec.md b/openspec/specs/prompt-resource-sync/spec.md index 514e4b3d..49aaa24a 100644 --- a/openspec/specs/prompt-resource-sync/spec.md +++ b/openspec/specs/prompt-resource-sync/spec.md @@ -1,8 +1,11 @@ # prompt-resource-sync Specification ## Purpose + TBD - created by archiving change backlog-core-05-user-modules-bootstrap. Update Purpose after archive. + ## Requirements + ### Requirement: Prompt Resource Detection and Project Target Copy The system SHALL consistently detect bundled prompt resources and copy them to IDE-specific project target paths during IDE initialization. @@ -20,4 +23,3 @@ The system SHALL consistently detect bundled prompt resources and copy them to I - **WHEN** `specfact init ide` copies templates for a selected IDE - **THEN** prompt files are created in the expected project target folder for that IDE - **AND** backlog-related prompts (including `specfact.backlog-add`) are included. - diff --git a/openspec/specs/pylint-runner/spec.md b/openspec/specs/pylint-runner/spec.md index d60638a9..cccb1ee1 100644 --- a/openspec/specs/pylint-runner/spec.md +++ b/openspec/specs/pylint-runner/spec.md @@ -1,28 +1,35 @@ # pylint-runner Specification ## Purpose + TBD - created by archiving change code-review-03-type-governance-runners. Update Purpose after archive. + ## Requirements + ### Requirement: pylint Architecture Smell Extraction + The system SHALL invoke pylint on provided files and map message IDs to violation categories: `W0702`, `W0703` β†’ architecture (bare-except, broad-except). #### Scenario: Bare except maps to architecture category + - **GIVEN** pylint output with message id `W0702` - **WHEN** `run_pylint(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `category="architecture"` and `tool="pylint"` #### Scenario: W0703 broad-except maps to architecture + - **GIVEN** pylint output with message id `W0703` - **WHEN** `run_pylint(files=[...])` is called - **THEN** finding has `category="architecture"` #### Scenario: Only changed files are filtered + - **GIVEN** a file list `[file_a.py]` - **WHEN** `run_pylint(files=[file_a.py])` is called - **THEN** only findings for `file_a.py` are returned #### Scenario: pylint parse error produces tool_error + - **GIVEN** pylint returns unparseable output - **WHEN** `run_pylint(files=[...])` is called - **THEN** one `ReviewFinding` with `category="tool_error"` is returned - diff --git a/openspec/specs/radon-runner/spec.md b/openspec/specs/radon-runner/spec.md index 2cdfce64..08509eb3 100644 --- a/openspec/specs/radon-runner/spec.md +++ b/openspec/specs/radon-runner/spec.md @@ -1,28 +1,35 @@ # radon-runner Specification ## Purpose + TBD - created by archiving change code-review-02-ruff-radon-runners. Update Purpose after archive. + ## Requirements + ### Requirement: Radon Cyclomatic Complexity Extraction + The system SHALL invoke `radon cc -j` on provided files and produce severity-tiered findings for functions exceeding complexity thresholds (12-15 β†’ warning, >15 β†’ error). #### Scenario: Function with complexity 13 produces warning + - **GIVEN** radon output shows a function with cyclomatic complexity 13 - **WHEN** `run_radon(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `severity="warning"` and `category="clean_code"` #### Scenario: Function with complexity 16 produces error + - **GIVEN** radon output shows a function with cyclomatic complexity 16 - **WHEN** `run_radon(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `severity="error"` and `category="clean_code"` #### Scenario: Function with complexity 12 or below produces no finding + - **GIVEN** all functions have complexity <= 12 - **WHEN** `run_radon(files=[...])` is called - **THEN** no findings are returned #### Scenario: Radon parse error produces tool_error finding + - **GIVEN** radon returns unparseable output - **WHEN** `run_radon(files=[...])` is called - **THEN** one `ReviewFinding` with `category="tool_error"` is returned - diff --git a/openspec/specs/readme-first-contact/spec.md b/openspec/specs/readme-first-contact/spec.md index f8d08177..9d8d28fa 100644 --- a/openspec/specs/readme-first-contact/spec.md +++ b/openspec/specs/readme-first-contact/spec.md @@ -1,8 +1,11 @@ # readme-first-contact Specification ## Purpose + TBD - created by archiving change readme-star-conversion-01. Update Purpose after archive. + ## Requirements + ### Requirement: README hero leads with developer outcome and runnable proof The repository README SHALL lead with a developer-facing value proposition and a runnable quickstart @@ -66,4 +69,3 @@ reader into the full documentation set first. of the document - **AND** the README SHALL include a brief "How SpecFact is built" or equivalent trust section that explains the repo's OpenSpec -> TDD -> quality-gate workflow in plain language - diff --git a/openspec/specs/review-cli-contracts/spec.md b/openspec/specs/review-cli-contracts/spec.md index bc357e23..40c2d624 100644 --- a/openspec/specs/review-cli-contracts/spec.md +++ b/openspec/specs/review-cli-contracts/spec.md @@ -1,46 +1,57 @@ # review-cli-contracts Specification ## Purpose + TBD - created by archiving change code-review-08-review-run-integration. Update Purpose after archive. + ## Requirements + ### Requirement: cli-val-01 Scenario YAML Files for All Three Command Groups + The system SHALL provide cli-val-01 compliant scenario YAML files for `specfact code review run`, `ledger`, and `rules` command groups. #### Scenario: review-run scenarios cover happy path and anti-patterns + - **GIVEN** `tests/cli-contracts/specfact-code-review-run.scenarios.yaml` exists - **WHEN** parsed against the cli-val-01 schema - **THEN** it contains at least one happy-path scenario (exit 0 on clean file) and one anti-pattern scenario #### Scenario: ledger scenarios cover pipe flow, status, and reset guard + - **GIVEN** `tests/cli-contracts/specfact-code-review-ledger.scenarios.yaml` exists - **WHEN** parsed - **THEN** it covers `ledger update` happy path, `ledger update` invalid JSON anti-pattern, `ledger status` happy path, and `ledger reset` missing --confirm anti-pattern #### Scenario: rules scenarios cover all three subcommands + - **GIVEN** `tests/cli-contracts/specfact-code-review-rules.scenarios.yaml` exists - **WHEN** parsed - **THEN** it covers `rules show`, `rules update`, and `rules init` happy paths plus error cases #### Scenario: All scenario files conform to cli-val-01 schema + - **GIVEN** all three scenario YAML files - **WHEN** validated against the cli-val-01 behavior contract schema - **THEN** no validation errors are reported ### Requirement: Review CLI commands carry icontract and beartype decorators + All public command functions in the review module (`specfact code review run`, `ledger`, `rules`) SHALL have `@require` / `@ensure` decorators (icontract) and `@beartype` on their signatures, consistent with the project-wide contract-first standard. #### Scenario: review run command has precondition on repo_path + - **WHEN** `specfact code review run` is invoked with an invalid `repo_path` - **THEN** an icontract `ViolationError` is raised before any tool runner is invoked - **AND** the error message references the violated precondition #### Scenario: review CLI contracts are consistent with typed signatures + - **WHEN** `hatch run contract-test` is executed after type annotations are applied to the review CLI module - **THEN** `contract_runner` reports zero `MISSING_ICONTRACT` findings for review command functions - **AND** `basedpyright` reports zero type errors for the review CLI module #### Scenario: Contract validation scenarios cover review run with CI flag + - **GIVEN** `tests/cli-contracts/specfact-code-review-run.scenarios.yaml` exists - **WHEN** a scenario exercising `review run --ci` with a clean target is added - **THEN** the scenario validates exit code 0 and presence of `.specfact/code-review.json` - diff --git a/openspec/specs/review-finding-model/spec.md b/openspec/specs/review-finding-model/spec.md index 9a5bd2d7..6d6b7052 100644 --- a/openspec/specs/review-finding-model/spec.md +++ b/openspec/specs/review-finding-model/spec.md @@ -1,24 +1,30 @@ # review-finding-model Specification ## Purpose + TBD - created by archiving change code-review-01-module-scaffold. Update Purpose after archive. + ## Requirements + ### Requirement: ReviewFinding Pydantic Model + The system SHALL provide a `ReviewFinding` Pydantic BaseModel with validated `category`, `severity`, `tool`, `rule`, `file`, `line`, `message`, and `fixable` fields. #### Scenario: Valid ReviewFinding creates successfully + - **GIVEN** a dict with all required fields: category, severity, tool, rule, file, line, message - **WHEN** `ReviewFinding(**data)` is called - **THEN** a valid instance is returned with all fields populated - **AND** `fixable` defaults to `False` if not provided #### Scenario: Invalid severity is rejected + - **GIVEN** a dict with `severity="critical"` (not in the valid set) - **WHEN** `ReviewFinding(**data)` is called - **THEN** a `ValidationError` is raised #### Scenario: Valid severity and category values accepted + - **GIVEN** severity values `error`, `warning`, `info` and category values `clean_code`, `security`, `type_safety`, `contracts`, `testing`, `style`, `architecture`, `tool_error` - **WHEN** each is used in a `ReviewFinding` - **THEN** all are accepted without validation error - diff --git a/openspec/specs/review-report-model/spec.md b/openspec/specs/review-report-model/spec.md index e949ac86..a94fa50b 100644 --- a/openspec/specs/review-report-model/spec.md +++ b/openspec/specs/review-report-model/spec.md @@ -1,34 +1,42 @@ # review-report-model Specification ## Purpose + TBD - created by archiving change code-review-01-module-scaffold. Update Purpose after archive. + ## Requirements + ### Requirement: ReviewReport Governance-01 Compatible Envelope + The system SHALL provide a `ReviewReport` Pydantic model carrying standard governance-01 fields (`schema_version`, `run_id`, `timestamp`, `overall_verdict`, `ci_exit_code`) and review-specific extensions (`score`, `reward_delta`, `findings`, `summary`). #### Scenario: PASS verdict maps to governance-01 PASS + - **GIVEN** a run with `score=85` - **WHEN** `ReviewReport` is constructed - **THEN** `overall_verdict` equals `"PASS"` and `ci_exit_code` equals `0` - **AND** `reward_delta` equals `5` (85 - 80) #### Scenario: WARN verdict maps to PASS_WITH_ADVISORY + - **GIVEN** a run with `score=60` - **WHEN** `ReviewReport` is constructed - **THEN** `overall_verdict` equals `"PASS_WITH_ADVISORY"` and `ci_exit_code` equals `0` #### Scenario: BLOCK verdict maps to FAIL + - **GIVEN** a run with `score=45` - **WHEN** `ReviewReport` is constructed - **THEN** `overall_verdict` equals `"FAIL"` and `ci_exit_code` equals `1` #### Scenario: Blocking error forces FAIL regardless of score + - **GIVEN** a run with `score=75` but a finding with `severity=error` and `fixable=False` - **WHEN** `ReviewReport` is constructed - **THEN** `overall_verdict` equals `"FAIL"` and `ci_exit_code` equals `1` #### Scenario: Standard governance fields always present + - **GIVEN** any `ReviewReport` instance - **WHEN** its fields are inspected - **THEN** `schema_version`, `run_id`, `timestamp`, `overall_verdict`, `ci_exit_code` are all non-null - diff --git a/openspec/specs/review-run-command/spec.md b/openspec/specs/review-run-command/spec.md index bfb28c56..2d7ef2bd 100644 --- a/openspec/specs/review-run-command/spec.md +++ b/openspec/specs/review-run-command/spec.md @@ -1,12 +1,17 @@ # review-run-command Specification ## Purpose + TBD - created by archiving change code-review-08-review-run-integration. Update Purpose after archive. + ## Requirements + ### Requirement: End-to-End specfact code review run Command + The `specfact code review run` workflow SHALL support the dogfood self-review proof for the SpecFact CLI repository and emit a governed zero-finding report when remediation is complete. #### Scenario: Dogfood self-review on SpecFact CLI reaches zero tracked findings + - **GIVEN** the SpecFact CLI repository under the `code-review-zero-findings` remediation branch - **AND** the dogfood self-review tests in `tests/unit/specfact_cli/test_dogfood_self_review.py` - **WHEN** `specfact code review run --scope full --json --out ` is executed in an environment where the `code` bundle is installed @@ -14,4 +19,3 @@ The `specfact code review run` workflow SHALL support the dogfood self-review pr - **AND** the report contains zero findings with rules `reportUnknownMemberType`, `print-in-src`, and `MISSING_ICONTRACT` - **AND** the report contains zero `clean_code` findings with rules `CC16` or higher - **AND** the report contains zero findings in category `tool_error` - diff --git a/openspec/specs/review-scorer/spec.md b/openspec/specs/review-scorer/spec.md index aaa813e1..6fa3cb21 100644 --- a/openspec/specs/review-scorer/spec.md +++ b/openspec/specs/review-scorer/spec.md @@ -1,33 +1,41 @@ # review-scorer Specification ## Purpose + TBD - created by archiving change code-review-01-module-scaffold. Update Purpose after archive. + ## Requirements + ### Requirement: Review Scoring Algorithm + The system SHALL compute `score` (0-120) and `reward_delta = score - 80` from findings and bonus conditions. Base score is 100; deductions: blocking error=-15, fixable error=-5, warning=-2, info=-1; bonuses: +5 each for five conditions. #### Scenario: Single blocking error deducts 15 + - **GIVEN** one finding with `severity=error`, `fixable=False` - **WHEN** score is computed with no bonuses - **THEN** `score` equals `85` and `reward_delta` equals `5` #### Scenario: Auto-fixable error deducts 5 + - **GIVEN** one finding with `severity=error`, `fixable=True` - **WHEN** score is computed - **THEN** `score` equals `95` and `reward_delta` equals `15` #### Scenario: Warnings deduct 2 each + - **GIVEN** three findings with `severity=warning` - **WHEN** score is computed - **THEN** `score` equals `94` #### Scenario: Verdict thresholds applied correctly + - **GIVEN** scores of 85, 60, and 45 - **WHEN** verdict is determined - **THEN** score 85 β†’ `"PASS"`, score 60 β†’ `"PASS_WITH_ADVISORY"`, score 45 β†’ `"FAIL"` #### Scenario: Score capped at 120 + - **GIVEN** conditions that would produce a score exceeding 120 - **WHEN** score is computed - **THEN** `score` equals `120` - diff --git a/openspec/specs/reward-ledger/spec.md b/openspec/specs/reward-ledger/spec.md index 7abf9dd4..a2b37da6 100644 --- a/openspec/specs/reward-ledger/spec.md +++ b/openspec/specs/reward-ledger/spec.md @@ -1,41 +1,50 @@ # reward-ledger Specification ## Purpose + TBD - created by archiving change code-review-06-reward-ledger. Update Purpose after archive. + ## Requirements + ### Requirement: Supabase Reward Ledger with Offline JSON Fallback + The system SHALL persist review run results to local `~/.specfact/ledger.json` by default for local and offline workflows, and MAY additionally write to Supabase `ai_sync.review_runs` plus `ai_sync.reward_ledger` when backend configuration is present. #### Scenario: Record run writes to local JSON by default + - **GIVEN** a `ReviewReport` with `score=85`, `reward_delta=5`, `verdict="PASS"` - **WHEN** `LedgerClient.record_run(report)` is called in a local default setup - **THEN** the run is appended to `~/.specfact/ledger.json` and no backend configuration is required #### Scenario: Record run stores data in Supabase when configured + - **GIVEN** a `ReviewReport` with `score=85`, `reward_delta=5`, `verdict="PASS"` and Supabase is configured and reachable - **WHEN** `LedgerClient.record_run(report)` is called - **THEN** a row is inserted into `ai_sync.review_runs` and `ai_sync.reward_ledger` is updated with `coins += 0.5` and `streak_pass` incremented #### Scenario: Record run falls back to local JSON when backend unavailable + - **GIVEN** Supabase configuration is missing or unreachable - **WHEN** `LedgerClient.record_run(report)` is called - **THEN** the run is appended to `~/.specfact/ledger.json` and no exception is raised #### Scenario: Streak pass bonus applied at streak >= 5 + - **GIVEN** the agent has `streak_pass=4` and a new PASS run occurs - **WHEN** the ledger updates - **THEN** `streak_pass` becomes `5` and an additional `+0.5` coins is added #### Scenario: Streak block penalty applied at streak >= 3 + - **GIVEN** the agent has `streak_block=2` and a new BLOCK run occurs - **WHEN** the ledger updates - **THEN** `streak_block` becomes `3` and `-1.0` coins is deducted #### Scenario: Ledger status returns correct state + - **GIVEN** `cumulative_coins=12.5`, `streak_pass=3`, `last_verdict="PASS"` - **WHEN** `LedgerClient.get_status()` is called - **THEN** the returned dict includes `coins=12.5`, `streak_pass=3`, `last_verdict="PASS"` - diff --git a/openspec/specs/ruff-runner/spec.md b/openspec/specs/ruff-runner/spec.md index bc29bee2..0ce145cd 100644 --- a/openspec/specs/ruff-runner/spec.md +++ b/openspec/specs/ruff-runner/spec.md @@ -1,28 +1,35 @@ # ruff-runner Specification ## Purpose + TBD - created by archiving change code-review-02-ruff-radon-runners. Update Purpose after archive. + ## Requirements + ### Requirement: Ruff Finding Extraction Mapped to ReviewFinding + The system SHALL invoke `ruff check --output-format json` on provided files and map rule prefixes to categories: `S*` β†’ security, `C9*` β†’ clean_code, `E/F/I*` β†’ style. #### Scenario: Bandit S-rules map to security category + - **GIVEN** ruff output contains a finding with rule `S603` - **WHEN** `run_ruff(files=[...])` is called - **THEN** the returned `ReviewFinding` has `category="security"` and `tool="ruff"` #### Scenario: C90 complexity rules map to clean_code + - **GIVEN** ruff output contains a finding with rule `C901` - **WHEN** `run_ruff(files=[...])` is called - **THEN** the finding has `category="clean_code"` #### Scenario: Only provided files are scanned + - **GIVEN** a file list `[file_a.py]` - **WHEN** `run_ruff(files=[file_a.py])` is called - **THEN** ruff is invoked with only `file_a.py` and no other files appear in findings #### Scenario: Ruff parse error or unavailability produces tool_error finding + - **GIVEN** ruff returns unparseable output or is not installed - **WHEN** `run_ruff(files=[...])` is called - **THEN** one `ReviewFinding` with `category="tool_error"` is returned and no exception propagates - diff --git a/openspec/specs/rules-commands/spec.md b/openspec/specs/rules-commands/spec.md index 526884ad..2bd525af 100644 --- a/openspec/specs/rules-commands/spec.md +++ b/openspec/specs/rules-commands/spec.md @@ -1,28 +1,35 @@ # rules-commands Specification ## Purpose + TBD - created by archiving change code-review-07-house-rules-skill. Update Purpose after archive. + ## Requirements + ### Requirement: House Rules CLI Subcommands for Show, Update, and Init + The system SHALL provide `specfact code review rules show|update|init` subcommands for managing the house rules skill file. #### Scenario: rules show prints current SKILL.md content + - **GIVEN** `skills/specfact-code-review/SKILL.md` exists - **WHEN** `specfact code review rules show` is run - **THEN** the full content is printed to stdout and exit code is 0 #### Scenario: rules show with missing SKILL.md prints helpful error + - **GIVEN** no SKILL.md exists at the expected path - **WHEN** `specfact code review rules show` is run - **THEN** an error message suggesting `rules init` is printed and exit code is 1 #### Scenario: rules update re-derives TOP VIOLATIONS from ledger + - **GIVEN** the ledger has 20 runs with violation `C901` appearing 5 times - **WHEN** `specfact code review rules update` is run - **THEN** `C901` appears in TOP VIOLATIONS and the version header is incremented #### Scenario: rules init creates default skill for new project + - **GIVEN** no SKILL.md exists - **WHEN** `specfact code review rules init` is run - **THEN** SKILL.md is created with default content and exit code is 0 - diff --git a/openspec/specs/runtime-portability/spec.md b/openspec/specs/runtime-portability/spec.md index 980122de..8e8f3725 100644 --- a/openspec/specs/runtime-portability/spec.md +++ b/openspec/specs/runtime-portability/spec.md @@ -1,31 +1,39 @@ # runtime-portability Specification ## Purpose + TBD - created by archiving change packaging-02-cross-platform-runtime-and-module-resources. Update Purpose after archive. + ## Requirements + ### Requirement: CLI output SHALL degrade safely on non-UTF-8 terminals + The SpecFact CLI SHALL render help, startup, and other common command output without raising encoding exceptions on supported Windows, Linux, and macOS terminals. When the active output stream cannot encode configured Unicode glyphs, the CLI SHALL switch to an ASCII-safe fallback for affected symbols instead of crashing. #### Scenario: Windows help rendering on a legacy code page + - **WHEN** a user runs a help or startup command in a terminal whose output encoding cannot represent the configured Unicode icons - **THEN** the CLI completes successfully - **AND** the rendered output uses encoding-safe fallback symbols for the unsupported glyphs #### Scenario: UTF-8 terminal preserves rich symbols + - **WHEN** a user runs the same help or startup command in a UTF-8-capable terminal - **THEN** the CLI completes successfully - **AND** the configured rich Unicode symbols remain enabled ### Requirement: Runtime mismatch diagnostics SHALL be actionable + When automation or programmatic invocation reaches a SpecFact installation whose runtime, module path, or compiled dependencies are incompatible with the calling environment, the CLI SHALL fail with a compatibility error that identifies the failing component and the resolved installation context. #### Scenario: External interpreter cannot load installed SpecFact module runtime + - **WHEN** a caller invokes backlog automation from a different interpreter environment than the one hosting the installed SpecFact stack - **THEN** the CLI fails with a compatibility error instead of a raw low-level import traceback - **AND** the error reports the unresolved module or compiled dependency - **AND** the error explains which interpreter or installation boundary must be used #### Scenario: Compatible installation resolves without manual path injection + - **WHEN** a caller invokes a supported SpecFact workflow from the interpreter that hosts the installed SpecFact runtime and module resources - **THEN** the CLI resolves the required runtime and module paths without requiring manual `.specfact/modules/...` injection - diff --git a/openspec/specs/schema-extension-system/spec.md b/openspec/specs/schema-extension-system/spec.md index 21d4c228..f25a7951 100644 --- a/openspec/specs/schema-extension-system/spec.md +++ b/openspec/specs/schema-extension-system/spec.md @@ -1,25 +1,31 @@ # schema-extension-system Specification ## Purpose + TBD - created by archiving change arch-07-schema-extension-system. Update Purpose after archive. + ## Requirements + ### Requirement: Core models provide extensions field The system SHALL add an `extensions` field to Feature and ProjectBundle models to store module-specific metadata as a dictionary with namespace-prefixed keys. #### Scenario: Feature model includes extensions field + - **WHEN** Feature model is instantiated - **THEN** it SHALL include `extensions: dict[str, Any]` field - **AND** extensions SHALL default to empty dict if not provided - **AND** extensions SHALL serialize/deserialize with YAML and JSON #### Scenario: ProjectBundle model includes extensions field + - **WHEN** ProjectBundle model is instantiated - **THEN** it SHALL include `extensions: dict[str, Any]` field - **AND** extensions SHALL default to empty dict if not provided - **AND** extensions SHALL serialize/deserialize with YAML and JSON #### Scenario: Backward compatibility with bundles without extensions + - **WHEN** existing bundle without extensions field is loaded - **THEN** extensions SHALL default to empty dict - **AND** bundle SHALL remain valid @@ -30,22 +36,26 @@ The system SHALL add an `extensions` field to Feature and ProjectBundle models t The system SHALL provide `get_extension()` and `set_extension()` methods on Feature and ProjectBundle models that enforce namespace-prefixed field access. #### Scenario: Get extension with namespace prefix + - **WHEN** code calls `feature.get_extension("backlog", "ado_work_item_id")` - **THEN** system SHALL look up `extensions["backlog.ado_work_item_id"]` - **AND** SHALL return the value if present - **AND** SHALL return None if not present (or provided default) #### Scenario: Set extension with namespace prefix + - **WHEN** code calls `feature.set_extension("backlog", "ado_work_item_id", "123456")` - **THEN** system SHALL store value at `extensions["backlog.ado_work_item_id"]` - **AND** SHALL enforce namespace format (module.field) #### Scenario: Invalid namespace format is rejected + - **WHEN** code calls `set_extension("backlog.submodule", "field", "value")` - **THEN** system SHALL raise ValueError with message "Invalid module name format" - **AND** SHALL require single-level namespace (no dots in module_name) #### Scenario: Get extension with default value + - **WHEN** code calls `feature.get_extension("backlog", "missing_field", default="default_value")` - **THEN** system SHALL return "default_value" if field not present - **AND** SHALL NOT modify extensions dict @@ -55,16 +65,19 @@ The system SHALL provide `get_extension()` and `set_extension()` methods on Feat The system SHALL extend module manifest schema to allow modules to declare schema extensions in `module-package.yaml`. #### Scenario: Manifest declares Feature extensions + - **WHEN** module-package.yaml includes schema_extensions section - **THEN** it MAY declare extensions for Feature model - **AND** each extension SHALL specify: target (Feature), field name, type hint, description #### Scenario: Manifest declares ProjectBundle extensions + - **WHEN** module-package.yaml includes schema_extensions section - **THEN** it MAY declare extensions for ProjectBundle model - **AND** each extension SHALL specify: target (ProjectBundle), field name, type hint, description #### Scenario: Extension field metadata is documented + - **WHEN** module declares schema extension - **THEN** manifest SHALL include human-readable description - **AND** description SHALL explain purpose and usage @@ -75,18 +88,21 @@ The system SHALL extend module manifest schema to allow modules to declare schem The system SHALL validate that no two modules declare conflicting extension field names during module registration. #### Scenario: Duplicate extension field is detected + - **WHEN** module A declares extension "backlog.ado_work_item_id" - **AND** module B also declares extension "backlog.ado_work_item_id" - **THEN** registration SHALL fail for second module - **AND** SHALL log error: "Extension field collision: backlog.ado_work_item_id already declared by module A" #### Scenario: Different modules use unique namespaces + - **WHEN** module backlog declares "backlog.ado_work_item_id" - **AND** module sync declares "sync.last_sync_timestamp" - **THEN** both registrations SHALL succeed - **AND** no collision detected #### Scenario: Same module declares multiple fields + - **WHEN** module backlog declares "backlog.ado_work_item_id" and "backlog.jira_issue_key" - **THEN** both extensions SHALL register successfully - **AND** namespace "backlog" is owned by backlog module @@ -96,11 +112,13 @@ The system SHALL validate that no two modules declare conflicting extension fiel The system SHALL maintain a global extension registry mapping module names to their declared schema extensions for debugging and documentation. #### Scenario: Registry populated at module registration + - **WHEN** module registration loads schema_extensions from manifest - **THEN** extensions SHALL be added to global registry - **AND** registry SHALL map: module_name β†’ list of (target, field, type, description) #### Scenario: Registry is queryable for debugging + - **WHEN** developer needs to inspect registered extensions - **THEN** registry SHALL provide method to list all extensions - **AND** SHALL show which module declared each extension @@ -111,12 +129,14 @@ The system SHALL maintain a global extension registry mapping module names to th The system SHALL use @icontract decorators to enforce namespace format and type safety for extension operations. #### Scenario: get_extension enforces namespace format + - **WHEN** get_extension() is called - **THEN** @require SHALL validate module_name matches pattern `[a-z][a-z0-9_-]*` - **AND** @require SHALL validate field matches pattern `[a-z][a-z0-9_]*` - **AND** SHALL use @beartype for type checking #### Scenario: set_extension enforces namespace format + - **WHEN** set_extension() is called - **THEN** @require SHALL validate module_name and field patterns - **AND** @ensure SHALL verify value was stored at correct key @@ -127,14 +147,15 @@ The system SHALL use @icontract decorators to enforce namespace format and type The system SHALL ensure extension functionality does not break existing code that does not use extensions. #### Scenario: Core operations work without extensions + - **WHEN** bundle is created without any extension usage - **THEN** all core operations SHALL function normally - **AND** extensions field SHALL be empty dict - **AND** no performance impact #### Scenario: Modules without schema_extensions work normally + - **WHEN** module manifest omits schema_extensions section - **THEN** module SHALL register successfully - **AND** module SHALL function normally - **AND** SHALL NOT have any extensions registered - diff --git a/openspec/specs/semgrep-runner/spec.md b/openspec/specs/semgrep-runner/spec.md index 3bc43563..4f28347d 100644 --- a/openspec/specs/semgrep-runner/spec.md +++ b/openspec/specs/semgrep-runner/spec.md @@ -1,28 +1,35 @@ # semgrep-runner Specification ## Purpose + TBD - created by archiving change code-review-05-semgrep-clean-code-rules. Update Purpose after archive. + ## Requirements + ### Requirement: Project-Specific Semgrep Rule Execution and Finding Extraction + The system SHALL invoke semgrep with the project-specific clean_code ruleset and map findings to `List[ReviewFinding]`, filtered to the provided file list. #### Scenario: Semgrep finding maps to ReviewFinding + - **GIVEN** semgrep output with a match on the `get-modify-same-method` rule - **WHEN** `run_semgrep(files=[...])` is called - **THEN** a `ReviewFinding` is returned with `tool="semgrep"` and `category="clean_code"` #### Scenario: Non-provided files filtered out + - **GIVEN** semgrep finds matches in `file_a.py` and `file_b.py`, only `file_a.py` provided - **WHEN** `run_semgrep(files=[file_a.py])` is called - **THEN** only findings from `file_a.py` are returned #### Scenario: Semgrep unavailable produces tool_error + - **GIVEN** semgrep binary is unavailable - **WHEN** `run_semgrep(files=[...])` is called - **THEN** one `ReviewFinding` with `category="tool_error"` is returned and no exception propagates #### Scenario: Clean file produces no findings + - **GIVEN** a file that matches none of the 5 custom rules - **WHEN** `run_semgrep(files=[file])` is called - **THEN** an empty list is returned - diff --git a/openspec/specs/sidecar-validation/spec.md b/openspec/specs/sidecar-validation/spec.md index 9c676c00..86535543 100644 --- a/openspec/specs/sidecar-validation/spec.md +++ b/openspec/specs/sidecar-validation/spec.md @@ -1,8 +1,11 @@ # sidecar-validation Specification ## Purpose + TBD - created by archiving change integrate-sidecar-validation. Update Purpose after archive. + ## Requirements + ### Requirement: Sidecar Validation Command The system SHALL provide a CLI command to run sidecar validation workflow. @@ -609,4 +612,3 @@ The sidecar validation SHALL work end-to-end with Flask applications. - Routes are extracted correctly - Contracts are populated - Harness is generated - diff --git a/openspec/specs/speckit-extension-catalog/spec.md b/openspec/specs/speckit-extension-catalog/spec.md index 2501060e..904322bd 100644 --- a/openspec/specs/speckit-extension-catalog/spec.md +++ b/openspec/specs/speckit-extension-catalog/spec.md @@ -1,8 +1,11 @@ # speckit-extension-catalog Specification ## Purpose + TBD - created by archiving change speckit-02-v04-adapter-alignment. Update Purpose after archive. + ## Requirements + ### Requirement: Extension catalog detection The system SHALL detect spec-kit extension catalogs in a target repository by scanning for `extensions/catalog.community.json` and `extensions/catalog.core.json` files relative to the repository root or `.specify/` directory. @@ -69,4 +72,3 @@ The system SHALL respect `.extensionignore` files when reporting active extensio - **GIVEN** a repository with extensions but no `.extensionignore` file - **WHEN** `SpecKitScanner.scan_extensions()` is called - **THEN** all extensions from the catalogs are included in the result - diff --git a/openspec/specs/speckit-version-detection/spec.md b/openspec/specs/speckit-version-detection/spec.md index b2fe8e68..514b90a0 100644 --- a/openspec/specs/speckit-version-detection/spec.md +++ b/openspec/specs/speckit-version-detection/spec.md @@ -1,8 +1,11 @@ # speckit-version-detection Specification ## Purpose + TBD - created by archiving change speckit-02-v04-adapter-alignment. Update Purpose after archive. + ## Requirements + ### Requirement: CLI-based version detection The system SHALL attempt to detect the installed spec-kit version by invoking the `specify` CLI when available on PATH. @@ -80,4 +83,3 @@ The system SHALL integrate version detection into the existing `SpecKitAdapter.g - **AND** if CLI returns `None`, falls back to heuristic detection - **AND** populates `ToolCapabilities.version` with the result - **AND** populates `ToolCapabilities.detected_version_source` with the detection method used - diff --git a/openspec/specs/template-detection/spec.md b/openspec/specs/template-detection/spec.md index 536e66f4..b0131c3a 100644 --- a/openspec/specs/template-detection/spec.md +++ b/openspec/specs/template-detection/spec.md @@ -1,8 +1,11 @@ # template-detection Specification ## Purpose + TBD - created by archiving change add-template-driven-backlog-refinement. Update Purpose after archive. + ## Requirements + ### Requirement: Template Detection Engine The system SHALL detect which template (if any) a backlog item matches, returning confidence scores and missing fields. @@ -132,4 +135,3 @@ The system SHALL support filtering backlog items by iteration, sprint, and relea - **WHEN** a backlog item is created from GitHub with milestone "Sprint 1" - **THEN** the system extracts sprint "Sprint 1" into normalized field, preserving original milestone data in provider_fields - diff --git a/openspec/specs/test-migration-cleanup/spec.md b/openspec/specs/test-migration-cleanup/spec.md index e08ed862..88c682bc 100644 --- a/openspec/specs/test-migration-cleanup/spec.md +++ b/openspec/specs/test-migration-cleanup/spec.md @@ -1,8 +1,11 @@ # test-migration-cleanup Specification ## Purpose + TBD - created by archiving change module-migration-07-test-migration-cleanup. Update Purpose after archive. + ## Requirements + ### Requirement: Post-Migration Test Topology Alignment The test suite SHALL align with the category-group command topology and removed in-core module paths after module migration. @@ -42,4 +45,3 @@ The test suite SHALL align with the category-group command topology and removed - **GIVEN** tests that assert removed flat command topology as active behavior - **WHEN** no supported runtime path exists for that assertion - **THEN** those tests are removed or replaced with assertions against the supported grouped/runtime command surface. - diff --git a/openspec/specs/test-suite-stabilization/spec.md b/openspec/specs/test-suite-stabilization/spec.md index bc0855d8..ce0a31e0 100644 --- a/openspec/specs/test-suite-stabilization/spec.md +++ b/openspec/specs/test-suite-stabilization/spec.md @@ -1,8 +1,11 @@ # test-suite-stabilization Specification ## Purpose + TBD - created by archiving change module-migration-08-release-suite-stabilization. Update Purpose after archive. + ## Requirements + ### Requirement: Post-Migration Release Suite Stability The `specfact-cli` repository SHALL keep only tests that match the lean-core runtime and supported grouped command surface after module migration. @@ -30,4 +33,3 @@ The `specfact-cli` repository SHALL keep only tests that match the lean-core run - **GIVEN** the release branch validation suites run in non-interactive CI - **WHEN** signing and installer-related tests execute - **THEN** they use deterministic local fixtures and fail only on real behavior defects rather than environment-coupled artifacts. - diff --git a/openspec/specs/test-tdd-gate/spec.md b/openspec/specs/test-tdd-gate/spec.md index beab6482..1d8b1ea9 100644 --- a/openspec/specs/test-tdd-gate/spec.md +++ b/openspec/specs/test-tdd-gate/spec.md @@ -1,34 +1,42 @@ # test-tdd-gate Specification ## Purpose + TBD - created by archiving change code-review-04-contract-test-runners. Update Purpose after archive. + ## Requirements + ### Requirement: TDD Gate Enforcing Test File Existence and Coverage Threshold + The system SHALL block the review if any changed `src/` file has no corresponding test file, or if tests fail, or if coverage is below 80%. #### Scenario: Changed src file with no test file produces BLOCK + - **GIVEN** a changed file `src/specfact_code_review/run/scorer.py` with no corresponding test file - **WHEN** the TDD gate runs - **THEN** a `ReviewFinding` with `rule="TEST_FILE_MISSING"`, `severity="error"`, `category="testing"` is returned - **AND** the overall verdict is forced to BLOCK #### Scenario: Passing tests with coverage >= 80% produces no finding + - **GIVEN** a changed file with a corresponding test file and 85% coverage - **WHEN** the TDD gate runs - **THEN** no testing finding is returned #### Scenario: Test failure produces BLOCK finding + - **GIVEN** a changed file with failing tests - **WHEN** the TDD gate runs - **THEN** a `ReviewFinding` with `severity="error"` and `category="testing"` is returned #### Scenario: Coverage below 80% produces warning + - **GIVEN** passing tests with 65% coverage - **WHEN** the TDD gate runs - **THEN** a `ReviewFinding` with `severity="warning"` and `category="testing"` is returned #### Scenario: --no-tests flag skips TDD gate + - **GIVEN** `--no-tests` is passed to `specfact code review run` - **WHEN** the runner executes - **THEN** no TDD gate check is performed and no testing findings are returned - diff --git a/openspec/specs/trustworthy-green-checks/spec.md b/openspec/specs/trustworthy-green-checks/spec.md index d2ef352a..27e20755 100644 --- a/openspec/specs/trustworthy-green-checks/spec.md +++ b/openspec/specs/trustworthy-green-checks/spec.md @@ -1,8 +1,11 @@ # trustworthy-green-checks Specification ## Purpose + TBD - created by archiving change ci-02-trustworthy-green-checks. Update Purpose after archive. + ## Requirements + ### Requirement: Required CI jobs fail on required tool failures Workflow jobs classified as required merge gates SHALL exit non-zero when their underlying required tool fails, and SHALL NOT suppress the failure behind warn-only shell patterns or broad continue-on-error handling. @@ -106,4 +109,3 @@ and automated review outputs. - **WHEN** a contributor reads the documented pre-commit, CI, or PR-review guidance - **THEN** the documentation names which checks are merge-blocking required gates - **AND** the documentation names which outputs remain advisory warnings or review assistance - diff --git a/openspec/specs/user-module-root/spec.md b/openspec/specs/user-module-root/spec.md index ad541706..cd548ca9 100644 --- a/openspec/specs/user-module-root/spec.md +++ b/openspec/specs/user-module-root/spec.md @@ -1,8 +1,11 @@ # user-module-root Specification ## Purpose + TBD - created by archiving change backlog-core-05-user-modules-bootstrap. Update Purpose after archive. + ## Requirements + ### Requirement: Canonical User Module Root The system SHALL use a canonical per-user module root at `/.specfact/modules` for installed module artifacts and discovery. diff --git a/pyproject.toml b/pyproject.toml index 3665ae77..29ca95df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -232,7 +232,10 @@ yaml-check-all = "bash scripts/yaml-tools.sh check-all {args}" check-docs-commands = "python scripts/check-docs-commands.py" check-cross-site-links = "python scripts/check-cross-site-links.py" doc-frontmatter-check = "python scripts/check_doc_frontmatter.py" -docs-validate = "python scripts/check-docs-commands.py && python scripts/check-cross-site-links.py --warn-only && python scripts/check_doc_frontmatter.py" +validate-agent-rule-signals = "python scripts/validate_agent_rule_applies_when.py" +check-version-sources = "python scripts/check_version_sources.py" +release = "python scripts/check_version_sources.py" +docs-validate = "python scripts/check-docs-commands.py && python scripts/check-cross-site-links.py --warn-only && python scripts/check_doc_frontmatter.py && python scripts/validate_agent_rule_applies_when.py" # Legacy entry (kept for compatibility); prefer `workflows-lint` above lint-workflows = "bash scripts/run_actionlint.sh {args}" diff --git a/scripts/check_doc_frontmatter.py b/scripts/check_doc_frontmatter.py index 98174eae..5f3651b4 100755 --- a/scripts/check_doc_frontmatter.py +++ b/scripts/check_doc_frontmatter.py @@ -11,7 +11,7 @@ import subprocess import sys from pathlib import Path -from typing import Any +from typing import Any, cast import typer import yaml @@ -33,6 +33,7 @@ TRACKED_DOC_DIRS = ("docs",) REQUIRED_ROOT_DOCS: tuple[str, ...] = ("USAGE-FAQ.md",) +AGENT_RULES_DIR = "docs/agent-rules/" EXEMPT_FILES = frozenset( { "docs/LICENSE.md", @@ -49,6 +50,18 @@ _OWNER_GLOB_METACHARS = frozenset("*?[]{}") REQUIRED_KEYS = ("title", "doc_owner", "tracks", "last_reviewed", "exempt", "exempt_reason") +AGENT_RULE_REQUIRED_KEYS = ( + *REQUIRED_KEYS, + "id", + "always_load", + "applies_when", + "priority", + "blocking", + "user_interaction_required", + "stop_conditions", + "depends_on", +) +RULE_ID_RE = re.compile(r"^[a-z0-9][a-z0-9-]*$") class DocFrontmatter(BaseModel): @@ -122,6 +135,51 @@ def _owner_and_exempt_rules(self) -> DocFrontmatter: DocFrontmatter.model_rebuild(_types_namespace={"datetime": datetime}) +class AgentRuleFrontmatter(DocFrontmatter): + """Validated frontmatter for deterministic governance rule documents.""" + + id: str = Field(..., description="Stable rule identifier") + always_load: bool = Field(..., description="Whether the rule must load for every applicable bootstrap") + applies_when: list[str] = Field(..., min_length=1, description="Task signals that require this rule") + priority: int = Field(..., ge=0, description="Deterministic loading order") + blocking: bool = Field(..., description="Whether the rule can block progress") + user_interaction_required: bool = Field(..., description="Whether the rule requires user clarification") + stop_conditions: list[str] = Field(..., min_length=1, description="Blocking conditions for the rule") + depends_on: list[str] = Field(..., description="Other rule ids that must load first") + + @field_validator("id", mode="before") + @classmethod + def _normalize_rule_id(cls, value: object) -> str: + if not isinstance(value, str): + raise ValueError("`id` must be a string") + rule_id = value.strip() + if not RULE_ID_RE.match(rule_id): + raise ValueError("`id` must be kebab-case") + return rule_id + + @field_validator("applies_when", "stop_conditions", "depends_on", mode="before") + @classmethod + def _string_list_fields(cls, value: object, info: ValidationInfo) -> list[str]: + if not isinstance(value, list) or not all(isinstance(x, str) for x in value): + raise ValueError(f"`{info.field_name}` must be a list of strings") + string_values = cast(list[str], value) + normalized = [x.strip() for x in string_values] + if info.field_name != "depends_on" and any(not x for x in normalized): + raise ValueError(f"`{info.field_name}` entries must be non-empty") + if info.field_name == "depends_on": + return [x for x in normalized if x] + return normalized + + @model_validator(mode="after") + def _always_load_requires_bootstrap_signal(self) -> AgentRuleFrontmatter: + if self.always_load and not {"session-bootstrap", "implementation", "all"} & set(self.applies_when): + raise ValueError("always-load rules must apply to bootstrap, implementation, or all tasks") + return self + + +AgentRuleFrontmatter.model_rebuild(_types_namespace={"datetime": datetime}) + + def _format_doc_frontmatter_errors(exc: ValidationError) -> list[str]: """Turn Pydantic errors into the same style as legacy manual validation.""" out: list[str] = [] @@ -149,6 +207,31 @@ def _enforced_path() -> Path: return _root() / "docs" / ".doc-frontmatter-enforced" +def _rel_posix(path: Path) -> str: + try: + return path.resolve().relative_to(_root().resolve()).as_posix() + except ValueError: + return str(path).replace("\\", "/") + + +def _agent_rules_path_slug(rel_under_rules: str) -> str: + """Build URL-style slug from path under ``docs/agent-rules/`` (matches existing rule ids).""" + tail = rel_under_rules[:-3] if rel_under_rules.endswith(".md") else rel_under_rules + parts = tail.split("/") + cleaned = [re.sub(r"^\d+-", "", segment) for segment in parts] + return "-".join(cleaned).lower().replace("_", "-") + + +def _agent_rules_canonical_id(rel_under_rules: str) -> str: + return f"agent-rules-{_agent_rules_path_slug(rel_under_rules)}" + + +def _agent_rules_default_permalink(slug: str) -> str: + if slug == "index": + return "/contributing/agent-rules/" + return f"/contributing/agent-rules/{slug}/" + + def _path_is_existing_file(path: Path) -> bool: return path.is_file() @@ -269,6 +352,24 @@ def _resolve_owner_impl(owner: str, root_key: str) -> bool: return _find_owner_under_source_roots(owner, base_root, owner_single_segment) +_PLAIN_YAML_SCALAR = re.compile(r"^[A-Za-z0-9_.\-/]+$") + + +@beartype +def _yaml_plain_or_quoted_scalar(value: str) -> str: + """Format a string as a YAML scalar (plain when unambiguous, else double-quoted).""" + if _PLAIN_YAML_SCALAR.fullmatch(value) and not value.startswith(("-", ":", "?", "[")): + return value + escaped = value.replace("\\", "\\\\").replace('"', '\\"') + return f'"{escaped}"' + + +@beartype +def _yaml_flow_inline(value: object) -> str: + """Serialize a Python value as an inline (flow-style) YAML fragment.""" + return yaml.dump(value, default_flow_style=True, allow_unicode=True, width=1000).strip() + + @beartype @require(lambda patterns: isinstance(patterns, list), "Patterns must be list") @ensure(lambda result: isinstance(result, bool), "Must return bool") @@ -301,6 +402,75 @@ def validate_glob_patterns(patterns: list[str]) -> bool: @ensure(lambda result: isinstance(result, str), "Must return string") def suggest_frontmatter(path: Path) -> str: """Return a suggested frontmatter block for a document.""" + rel = _rel_posix(path) + if rel.startswith(AGENT_RULES_DIR): + rel_under = rel[len(AGENT_RULES_DIR) :] + rule_slug = _agent_rules_path_slug(rel_under) + canonical_id = _agent_rules_canonical_id(rel_under) + default_permalink = _agent_rules_default_permalink(rule_slug) + layout_val = "default" + permalink_val = default_permalink + description_val: str | None = None + keywords_val: list[str] | None = None + audience_val: list[str] | None = None + expertise_val: list[str] | None = None + if path.is_file(): + try: + existing = parse_frontmatter(path) + except OSError: + pass + else: + lv = existing.get("layout") + if isinstance(lv, str) and lv.strip(): + layout_val = lv.strip() + pv = existing.get("permalink") + if isinstance(pv, str) and pv.strip(): + permalink_val = pv.strip() + dv = existing.get("description") + if isinstance(dv, str) and dv.strip(): + description_val = dv.strip() + kv = existing.get("keywords") + if isinstance(kv, list) and all(isinstance(x, str) for x in kv): + keywords_val = list(kv) + av = existing.get("audience") + if isinstance(av, list) and all(isinstance(x, str) for x in av): + audience_val = list(av) + ev = existing.get("expertise_level") + if isinstance(ev, list) and all(isinstance(x, str) for x in ev): + expertise_val = list(ev) + title_guess = path.stem.replace("-", " ").title().replace('"', '\\"') + optional_lines = "" + if description_val is not None: + optional_lines += f"description: {_yaml_flow_inline(description_val)}\n" + if keywords_val is not None: + optional_lines += f"keywords: {_yaml_flow_inline(keywords_val)}\n" + if audience_val is not None: + optional_lines += f"audience: {_yaml_flow_inline(audience_val)}\n" + if expertise_val is not None: + optional_lines += f"expertise_level: {_yaml_flow_inline(expertise_val)}\n" + return f"""--- +layout: {_yaml_plain_or_quoted_scalar(layout_val)} +title: "{title_guess}" +permalink: {_yaml_plain_or_quoted_scalar(permalink_val)} +{optional_lines}id: {canonical_id} +doc_owner: specfact-cli +tracks: + - AGENTS.md + - docs/agent-rules/** +last_reviewed: {datetime.date.today().isoformat()} +exempt: false +exempt_reason: "" +always_load: false +applies_when: + - detailed-reference +priority: 50 +blocking: false +user_interaction_required: false +stop_conditions: + - none +depends_on: [] +--- +""" return f"""--- title: "{path.stem}" doc_owner: specfact-cli @@ -314,13 +484,6 @@ def suggest_frontmatter(path: Path) -> str: """ -def _rel_posix(path: Path) -> str: - try: - return path.resolve().relative_to(_root().resolve()).as_posix() - except ValueError: - return str(path).replace("\\", "/") - - def _load_enforced_patterns() -> list[str] | None: """Return path patterns from docs/.doc-frontmatter-enforced, or None if file missing.""" path = _enforced_path() @@ -438,6 +601,23 @@ def _validate_record(fm: dict[str, Any]) -> list[str]: return [] +def _is_agent_rule_doc(path: Path) -> bool: + return _rel_posix(path).startswith(AGENT_RULES_DIR) + + +def _validate_record_for_path(path: Path, fm: dict[str, Any]) -> list[str]: + required_keys = AGENT_RULE_REQUIRED_KEYS if _is_agent_rule_doc(path) else REQUIRED_KEYS + missing = [f"missing `{key}`" for key in required_keys if key not in fm] + if missing: + return missing + model = AgentRuleFrontmatter if _is_agent_rule_doc(path) else DocFrontmatter + try: + model.model_validate(fm) + except ValidationError as exc: + return _format_doc_frontmatter_errors(exc) + return [] + + def _discover_paths_to_check(all_docs: bool) -> list[Path] | None: """Return files to validate, or None when the run should exit 0 early (skip).""" if all_docs: @@ -476,7 +656,7 @@ def _collect_failures( except (OSError, yaml.YAMLError) as exc: failures.append(f" βœ— YAML parse error: {rel}: {exc}") continue - errs = _validate_record(fm) + errs = _validate_record_for_path(file_path, fm) if errs: msg = f" βœ— INVALID frontmatter: {rel}\n - " + "\n - ".join(errs) if fix_hint: diff --git a/scripts/check_version_sources.py b/scripts/check_version_sources.py new file mode 100644 index 00000000..473fcb0b --- /dev/null +++ b/scripts/check_version_sources.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +"""Ensure release version strings match across canonical source files.""" + +from __future__ import annotations + +import re +import sys +from pathlib import Path + + +def _repo_root() -> Path: + return Path(__file__).resolve().parents[1] + + +def _read_version_pyproject(text: str) -> str | None: + match = re.search(r'(?m)^version\s*=\s*["\']([^"\']+)["\']', text) + return match.group(1) if match else None + + +def _read_version_setup(text: str) -> str | None: + match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', text) + return match.group(1) if match else None + + +def _read_version_init(text: str) -> str | None: + match = re.search(r'(?m)^__version__\s*=\s*["\']([^"\']+)["\']', text) + return match.group(1) if match else None + + +def main() -> int: + root = _repo_root() + paths = { + "pyproject.toml": root / "pyproject.toml", + "setup.py": root / "setup.py", + "src/__init__.py": root / "src" / "__init__.py", + "src/specfact_cli/__init__.py": root / "src" / "specfact_cli" / "__init__.py", + } + versions: dict[str, str] = {} + readers = { + "pyproject.toml": _read_version_pyproject, + "setup.py": _read_version_setup, + "src/__init__.py": _read_version_init, + "src/specfact_cli/__init__.py": _read_version_init, + } + for label, path in paths.items(): + if not path.is_file(): + sys.stderr.write(f"check_version_sources: missing file {path.relative_to(root)}\n") + return 2 + text = path.read_text(encoding="utf-8") + ver = readers[label](text) + if not ver: + sys.stderr.write(f"check_version_sources: could not parse version in {label}\n") + return 2 + versions[label] = ver + + unique = sorted(set(versions.values())) + if len(unique) != 1: + sys.stderr.write( + "check_version_sources: version mismatch across pyproject.toml, setup.py, " + "src/__init__.py, src/specfact_cli/__init__.py:\n" + ) + for label, ver in sorted(versions.items()): + sys.stderr.write(f" {label}: {ver}\n") + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/pre-commit-smart-checks.sh b/scripts/pre-commit-smart-checks.sh index 1606b8f3..f7a01e5a 100755 --- a/scripts/pre-commit-smart-checks.sh +++ b/scripts/pre-commit-smart-checks.sh @@ -61,6 +61,34 @@ ${md_files} EOF } +run_version_sources_check_if_needed() { + local files + files=$(staged_files) + local version_paths=("pyproject.toml" "setup.py" "src/__init__.py" "src/specfact_cli/__init__.py") + local hit=0 + local f + for f in $files; do + local p + for p in "${version_paths[@]}"; do + if [ "$f" = "$p" ]; then + hit=1 + break + fi + done + done + if [ "$hit" -eq 0 ]; then + return 0 + fi + info "πŸ“Œ Version file(s) staged β€” verifying synchronized versions" + if hatch run python scripts/check_version_sources.py; then + success "βœ… Version sources are synchronized" + else + error "❌ Version mismatch across pyproject.toml, setup.py, src/__init__.py, src/specfact_cli/__init__.py" + warn "πŸ’‘ Run: hatch run check-version-sources (or python scripts/check_version_sources.py)" + exit 1 + fi +} + run_module_signature_verification() { info "πŸ” Verifying bundled module signatures/version bumps" if hatch run ./scripts/verify-modules-signature.py --require-signature --enforce-version-bump; then @@ -213,7 +241,7 @@ run_code_review_gate() { check_safe_change() { local files files=$(staged_files) - local version_files=("pyproject.toml" "setup.py" "src/__init__.py") + local version_files=("pyproject.toml" "setup.py" "src/__init__.py" "src/specfact_cli/__init__.py") local changelog_files=("CHANGELOG.md") local test_infrastructure_files=( "tools/smart_test_coverage.py" @@ -263,6 +291,7 @@ warn "πŸ” Running pre-commit checks (YAML/workflows + smart tests)" # Always enforce module signature/version policy before commit run_module_signature_verification +run_version_sources_check_if_needed run_format_safety # Always run lint checks when relevant files changed @@ -271,7 +300,7 @@ run_markdown_lint_if_needed run_yaml_lint_if_needed run_actionlint_if_needed -# If only safe changes, skip tests after lint passes +# If only safe changes, skip tests after lint passes (version files already verified above) if check_safe_change; then success "βœ… Safe change detected - skipping test run" info "πŸ’‘ Only version numbers, docs/test infra, or YAML/workflows changed" diff --git a/scripts/sync_github_hierarchy_cache.py b/scripts/sync_github_hierarchy_cache.py new file mode 100755 index 00000000..8a6eccd7 --- /dev/null +++ b/scripts/sync_github_hierarchy_cache.py @@ -0,0 +1,607 @@ +#!/usr/bin/env python3 +"""Sync GitHub Epic/Feature hierarchy into a local OpenSpec cache.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import subprocess +import sys +from collections.abc import Mapping +from dataclasses import dataclass +from datetime import UTC, datetime +from pathlib import Path +from typing import Any + +from beartype import beartype +from icontract import ensure, require + + +DEFAULT_REPO_OWNER = "nold-ai" +_SCRIPT_DIR = Path(__file__).resolve().parent + + +@beartype +@ensure( + lambda result: result is None or bool(str(result).strip()), + "parsed repository name must be non-blank when present", +) +def parse_repo_name_from_remote_url(url: str) -> str | None: + """Return the repository name segment from a Git remote URL, if parseable.""" + stripped = url.strip() + if not stripped: + return None + if stripped.startswith("git@"): + _, _, rest = stripped.partition(":") + path = rest + elif "://" in stripped: + host_and_path = stripped.split("://", 1)[1] + if "/" not in host_and_path: + return None + path = host_and_path.split("/", 1)[1] + else: + path = stripped + path = path.rstrip("/") + if path.endswith(".git"): + path = path[:-4] + segments = [segment for segment in path.split("/") if segment] + if not segments: + return None + return segments[-1] + + +@beartype +def _default_repo_name_from_git(script_dir: Path) -> str | None: + """Resolve the GitHub repository name from ``origin`` (works in worktrees).""" + try: + completed = subprocess.run( + ["git", "-C", str(script_dir), "config", "--get", "remote.origin.url"], + check=False, + capture_output=True, + text=True, + ) + except (FileNotFoundError, OSError): + return None + if completed.returncode != 0: + return None + return parse_repo_name_from_remote_url(completed.stdout) + + +_DEFAULT_REPO_NAME_FALLBACK = Path(__file__).resolve().parents[1].name +DEFAULT_REPO_NAME = _default_repo_name_from_git(_SCRIPT_DIR) or _DEFAULT_REPO_NAME_FALLBACK +DEFAULT_OUTPUT_PATH = Path(".specfact") / "backlog" / "github_hierarchy_cache.md" +DEFAULT_STATE_PATH = Path(".specfact") / "backlog" / "github_hierarchy_cache_state.json" +SUPPORTED_ISSUE_TYPES = frozenset({"Epic", "Feature"}) +SUPPORTED_ISSUE_TYPES_ORDER: tuple[str, ...] = ("Epic", "Feature") +_SUMMARY_SKIP_LINES = {"why", "scope", "summary", "changes", "capabilities", "impact"} +_GH_GRAPHQL_TIMEOUT_SEC = 120 + + +@beartype +def _build_hierarchy_issues_query(*, include_body: bool) -> str: + """Return the shared GitHub GraphQL query, optionally including issue body text.""" + body_field = " bodyText\n" if include_body else "" + return f""" +query($owner: String!, $name: String!, $after: String) {{ + repository(owner: $owner, name: $name) {{ + issues(first: 100, after: $after, states: [OPEN, CLOSED], orderBy: {{field: CREATED_AT, direction: ASC}}) {{ + pageInfo {{ hasNextPage endCursor }} + nodes {{ + number + title + url + updatedAt +{body_field} issueType {{ name }} + labels(first: 100) {{ nodes {{ name }} }} + parent {{ number title url }} + subIssues(first: 100) {{ nodes {{ number title url issueType {{ name }} }} }} + }} + }} + }} +}} +""".strip() + + +@dataclass(frozen=True) +class IssueLink: + """Compact link to a related issue.""" + + number: int + title: str + url: str + + +@dataclass(frozen=True) +class HierarchyIssue: + """Normalized hierarchy issue used for cache rendering.""" + + number: int + title: str + url: str + issue_type: str + labels: list[str] + summary: str + updated_at: str + parent: IssueLink | None + children: list[IssueLink] + + +@dataclass(frozen=True) +class SyncResult: + """Outcome of a cache sync attempt.""" + + changed: bool + issue_count: int + fingerprint: str + output_path: Path + + +@beartype +def _extract_summary(body_text: str) -> str: + """Return a compact summary line for markdown output.""" + normalized = body_text.replace("\\n", "\n") + for line in normalized.splitlines(): + cleaned = line.strip() + if not cleaned: + continue + if cleaned.startswith("#"): + cleaned = cleaned.lstrip("#").strip() + if cleaned.lower().rstrip(":") in _SUMMARY_SKIP_LINES: + continue + if cleaned: + return cleaned[:200] + return "No summary provided." + + +@beartype +def _parse_issue_link(node: Mapping[str, Any] | None) -> IssueLink | None: + """Convert a GraphQL link node to IssueLink.""" + if not node: + return None + return IssueLink( + number=int(node["number"]), + title=str(node["title"]), + url=str(node["url"]), + ) + + +@beartype +def _mapping_value(node: Mapping[str, Any], key: str) -> Mapping[str, Any] | None: + """Return a nested mapping value when present.""" + value = node.get(key) + return value if isinstance(value, Mapping) else None + + +@beartype +def _mapping_nodes(container: Mapping[str, Any] | None) -> list[Mapping[str, Any]]: + """Return a filtered list of mapping nodes from a GraphQL connection.""" + if container is None: + return [] + + raw_nodes = container.get("nodes") + if not isinstance(raw_nodes, list): + return [] + + return [item for item in raw_nodes if isinstance(item, Mapping)] + + +@beartype +def _label_names(label_nodes: list[Mapping[str, Any]]) -> list[str]: + """Extract sorted label names from GraphQL label nodes.""" + names: list[str] = [] + for item in label_nodes: + name = item.get("name") + if name: + names.append(str(name)) + return sorted(names, key=str.lower) + + +@beartype +def _subissue_type_name(item: Mapping[str, Any]) -> str | None: + """Return sub-issue type name when present.""" + issue_type_node = _mapping_value(item, "issueType") + if issue_type_node and issue_type_node.get("name"): + return str(issue_type_node["name"]) + return None + + +@beartype +def _child_links(subissue_nodes: list[Mapping[str, Any]]) -> list[IssueLink]: + """Extract sorted child issue links from GraphQL subissue nodes (Epic/Feature only).""" + children = [ + IssueLink(number=int(item["number"]), title=str(item["title"]), url=str(item["url"])) + for item in subissue_nodes + if item.get("number") is not None and _subissue_type_name(item) in SUPPORTED_ISSUE_TYPES + ] + children.sort(key=lambda item: item.number) + return children + + +@beartype +def _parse_issue_node(node: Mapping[str, Any], *, include_body: bool) -> HierarchyIssue | None: + """Convert a GraphQL issue node to HierarchyIssue when supported.""" + issue_type_node = _mapping_value(node, "issueType") + issue_type_name = str(issue_type_node["name"]) if issue_type_node and issue_type_node.get("name") else None + if issue_type_name not in SUPPORTED_ISSUE_TYPES: + return None + + summary = _extract_summary(str(node.get("bodyText", ""))) if include_body else "" + return HierarchyIssue( + number=int(node["number"]), + title=str(node["title"]), + url=str(node["url"]), + issue_type=str(issue_type_name), + labels=_label_names(_mapping_nodes(_mapping_value(node, "labels"))), + summary=summary, + updated_at=str(node["updatedAt"]), + parent=_parse_issue_link(_mapping_value(node, "parent")), + children=_child_links(_mapping_nodes(_mapping_value(node, "subIssues"))), + ) + + +@beartype +def _run_graphql_query(query: str, *, repo_owner: str, repo_name: str, after: str | None) -> Mapping[str, Any]: + """Run a GitHub GraphQL query through `gh`.""" + command = [ + "gh", + "api", + "graphql", + "-f", + f"query={query}", + "-F", + f"owner={repo_owner}", + "-F", + f"name={repo_name}", + ] + if after is not None: + command.extend(["-F", f"after={after}"]) + + try: + completed = subprocess.run( + command, + check=False, + capture_output=True, + text=True, + timeout=_GH_GRAPHQL_TIMEOUT_SEC, + ) + except subprocess.TimeoutExpired as exc: + detail = f"GitHub GraphQL subprocess timed out after {_GH_GRAPHQL_TIMEOUT_SEC}s" + out = (exc.stdout or "").strip() + err = (exc.stderr or "").strip() + if out or err: + detail = f"{detail}; stdout={out!r}; stderr={err!r}" + raise RuntimeError(detail) from exc + + if completed.returncode != 0: + raise RuntimeError(completed.stderr.strip() or completed.stdout.strip() or "GitHub GraphQL query failed") + + payload = json.loads(completed.stdout) + if "errors" in payload: + raise RuntimeError(json.dumps(payload["errors"], indent=2)) + return payload + + +@beartype +def _is_not_blank(value: str) -> bool: + """Return whether a required CLI string value is non-blank.""" + return bool(value.strip()) + + +@beartype +def _all_supported_issue_types(result: list[HierarchyIssue]) -> bool: + """Return whether every issue has a supported issue type.""" + return all(issue.issue_type in SUPPORTED_ISSUE_TYPES for issue in result) + + +@beartype +def _require_repo_owner_for_fetch(*, repo_owner: str, repo_name: str, fingerprint_only: bool) -> bool: + _ = (repo_name, fingerprint_only) + return _is_not_blank(repo_owner) + + +@beartype +def _require_repo_name_for_fetch(*, repo_owner: str, repo_name: str, fingerprint_only: bool) -> bool: + _ = (repo_owner, fingerprint_only) + return _is_not_blank(repo_name) + + +@beartype +@require(_require_repo_owner_for_fetch, "repo_owner must not be blank") +@require(_require_repo_name_for_fetch, "repo_name must not be blank") +@ensure(_all_supported_issue_types, "Only Epic and Feature issues should be returned") +def fetch_hierarchy_issues(*, repo_owner: str, repo_name: str, fingerprint_only: bool) -> list[HierarchyIssue]: + """Fetch Epic and Feature issues from GitHub for the given repository.""" + query = _build_hierarchy_issues_query(include_body=not fingerprint_only) + issues: list[HierarchyIssue] = [] + after: str | None = None + + while True: + payload = _run_graphql_query(query, repo_owner=repo_owner, repo_name=repo_name, after=after) + repository = payload.get("data", {}).get("repository", {}) + issue_connection = repository.get("issues", {}) + nodes = issue_connection.get("nodes", []) + for node in nodes: + if not isinstance(node, Mapping): + continue + parsed = _parse_issue_node(node, include_body=not fingerprint_only) + if parsed is not None: + issues.append(parsed) + page_info = issue_connection.get("pageInfo", {}) + if not page_info.get("hasNextPage"): + break + after = page_info.get("endCursor") + + return issues + + +@beartype +@ensure(lambda result: len(result) == 64, "Fingerprint must be a SHA-256 hex digest") +def compute_hierarchy_fingerprint(issues: list[HierarchyIssue]) -> str: + """Compute a deterministic fingerprint for hierarchy state.""" + canonical_rows: list[dict[str, Any]] = [] + for issue in sorted(issues, key=lambda item: (item.issue_type, item.number)): + canonical_rows.append( + { + "number": issue.number, + "title": issue.title, + "issue_type": issue.issue_type, + "updated_at": issue.updated_at, + "labels": sorted(issue.labels, key=str.lower), + "parent_number": issue.parent.number if issue.parent else None, + "child_numbers": [child.number for child in sorted(issue.children, key=lambda item: item.number)], + } + ) + + canonical_json = json.dumps(canonical_rows, sort_keys=True, separators=(",", ":")) + return hashlib.sha256(canonical_json.encode("utf-8")).hexdigest() + + +@beartype +def _group_issues_by_type(issues: list[HierarchyIssue]) -> dict[str, list[HierarchyIssue]]: + """Return issues grouped by supported type in deterministic order.""" + return { + issue_type: sorted((item for item in issues if item.issue_type == issue_type), key=lambda item: item.number) + for issue_type in SUPPORTED_ISSUE_TYPES_ORDER + } + + +@beartype +def _render_issue_block(issue: HierarchyIssue) -> list[str]: + """Render one issue block for the hierarchy cache.""" + parent_text = "none" + if issue.parent is not None: + parent_text = f"#{issue.parent.number} {issue.parent.title}" + + child_text = "none" + if issue.children: + child_text = ", ".join(f"#{child.number} {child.title}" for child in issue.children) + + label_text = ", ".join(sorted(issue.labels, key=str.lower)) if issue.labels else "none" + return [ + f"### #{issue.number} {issue.title}", + f"- URL: {issue.url}", + f"- Parent: {parent_text}", + f"- Children: {child_text}", + f"- Labels: {label_text}", + f"- Summary: {issue.summary or 'No summary provided.'}", + "", + ] + + +@beartype +def _render_issue_section(*, title: str, issues: list[HierarchyIssue]) -> list[str]: + """Render one section of grouped issues.""" + lines = [f"## {title}", ""] + if not issues: + lines.extend(["_None_", ""]) + return lines + + for issue in issues: + lines.extend(_render_issue_block(issue)) + return lines + + +@beartype +def _require_repo_full_name_for_render( + *, repo_full_name: str, issues: list[HierarchyIssue], generated_at: str, fingerprint: str +) -> bool: + _ = (issues, generated_at, fingerprint) + return _is_not_blank(repo_full_name) + + +@beartype +def _require_generated_at_for_render( + *, repo_full_name: str, issues: list[HierarchyIssue], generated_at: str, fingerprint: str +) -> bool: + _ = (repo_full_name, issues, fingerprint) + return _is_not_blank(generated_at) + + +@beartype +def _require_fingerprint_for_render( + *, repo_full_name: str, issues: list[HierarchyIssue], generated_at: str, fingerprint: str +) -> bool: + _ = (repo_full_name, issues, generated_at) + return _is_not_blank(fingerprint) + + +@beartype +@require(_require_repo_full_name_for_render, "repo_full_name must not be blank") +@require(_require_generated_at_for_render, "generated_at must not be blank") +@require(_require_fingerprint_for_render, "fingerprint must not be blank") +def render_cache_markdown( + *, + repo_full_name: str, + issues: list[HierarchyIssue], + generated_at: str, + fingerprint: str, +) -> str: + """Render deterministic markdown for the hierarchy cache.""" + grouped = _group_issues_by_type(issues) + + lines = [ + "# GitHub Hierarchy Cache", + "", + f"- Repository: `{repo_full_name}`", + f"- Generated At: `{generated_at}`", + f"- Fingerprint: `{fingerprint}`", + f"- Included Issue Types: `{', '.join(sorted(SUPPORTED_ISSUE_TYPES))}`", + "", + ( + "Use this file as the first lookup source for parent Epic or Feature relationships " + "during OpenSpec and GitHub issue setup." + ), + "", + ] + + for section_name, issue_type in (("Epics", "Epic"), ("Features", "Feature")): + lines.extend(_render_issue_section(title=section_name, issues=grouped[issue_type])) + + return "\n".join(lines).rstrip() + "\n" + + +@beartype +def _load_state(state_path: Path) -> Mapping[str, Any]: + """Load state JSON if it exists; otherwise return empty mapping.""" + if not state_path.exists(): + return {} + try: + loaded = json.loads(state_path.read_text(encoding="utf-8")) + except json.JSONDecodeError: + return {} + return loaded if isinstance(loaded, Mapping) else {} + + +@beartype +def _write_state( + *, state_path: Path, repo_full_name: str, fingerprint: str, issue_count: int, generated_at: str +) -> None: + """Persist machine-readable sync state.""" + state_path.parent.mkdir(parents=True, exist_ok=True) + payload = { + "repo": repo_full_name, + "fingerprint": fingerprint, + "issue_count": issue_count, + "generated_at": generated_at, + } + state_path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +@beartype +def _require_repo_owner_for_sync( + *, repo_owner: str, repo_name: str, output_path: Path, state_path: Path, force: bool = False +) -> bool: + _ = (repo_name, output_path, state_path, force) + return _is_not_blank(repo_owner) + + +@beartype +def _require_repo_name_for_sync( + *, repo_owner: str, repo_name: str, output_path: Path, state_path: Path, force: bool = False +) -> bool: + _ = (repo_owner, output_path, state_path, force) + return _is_not_blank(repo_name) + + +@beartype +@require(_require_repo_owner_for_sync, "repo_owner must not be blank") +@require(_require_repo_name_for_sync, "repo_name must not be blank") +def sync_cache( + *, + repo_owner: str, + repo_name: str, + output_path: Path, + state_path: Path, + force: bool = False, +) -> SyncResult: + """Sync the local hierarchy cache from GitHub.""" + state = _load_state(state_path) + detailed_issues = fetch_hierarchy_issues( + repo_owner=repo_owner, + repo_name=repo_name, + fingerprint_only=False, + ) + fingerprint = compute_hierarchy_fingerprint(detailed_issues) + repo_full_name = f"{repo_owner}/{repo_name}" + + if ( + not force + and state.get("repo") == repo_full_name + and state.get("fingerprint") == fingerprint + and output_path.exists() + ): + return SyncResult( + changed=False, + issue_count=len(detailed_issues), + fingerprint=fingerprint, + output_path=output_path, + ) + + generated_at = datetime.now(tz=UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z") + output_path.parent.mkdir(parents=True, exist_ok=True) + output_path.write_text( + render_cache_markdown( + repo_full_name=repo_full_name, + issues=detailed_issues, + generated_at=generated_at, + fingerprint=fingerprint, + ), + encoding="utf-8", + ) + _write_state( + state_path=state_path, + repo_full_name=repo_full_name, + fingerprint=fingerprint, + issue_count=len(detailed_issues), + generated_at=generated_at, + ) + return SyncResult( + changed=True, + issue_count=len(detailed_issues), + fingerprint=fingerprint, + output_path=output_path, + ) + + +@beartype +def _build_parser() -> argparse.ArgumentParser: + """Create CLI argument parser.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--repo-owner", default=DEFAULT_REPO_OWNER, help="GitHub repo owner") + parser.add_argument("--repo-name", default=DEFAULT_REPO_NAME, help="GitHub repo name") + parser.add_argument("--output", default=str(DEFAULT_OUTPUT_PATH), help="Markdown cache output path") + parser.add_argument("--state-file", default=str(DEFAULT_STATE_PATH), help="Fingerprint state file path") + parser.add_argument("--force", action="store_true", help="Rewrite cache even when fingerprint is unchanged") + return parser + + +@beartype +@ensure(lambda result: result >= 0, "exit code must be non-negative") +def main(argv: list[str] | None = None) -> int: + """Run the hierarchy cache sync.""" + parser = _build_parser() + args = parser.parse_args(argv) + try: + result = sync_cache( + repo_owner=args.repo_owner, + repo_name=args.repo_name, + output_path=Path(args.output), + state_path=Path(args.state_file), + force=bool(args.force), + ) + except RuntimeError as exc: + sys.stderr.write(f"GitHub hierarchy cache sync failed: {exc}\n") + return 1 + except OSError as exc: + sys.stderr.write(f"GitHub hierarchy cache sync failed: {exc}\n") + return 1 + if result.changed: + sys.stdout.write(f"Updated GitHub hierarchy cache with {result.issue_count} issues at {result.output_path}\n") + else: + sys.stdout.write(f"GitHub hierarchy cache unchanged ({result.issue_count} issues).\n") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/validate_agent_rule_applies_when.py b/scripts/validate_agent_rule_applies_when.py new file mode 100644 index 00000000..343691cc --- /dev/null +++ b/scripts/validate_agent_rule_applies_when.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +"""Validate docs/agent-rules/*.md frontmatter applies_when against canonical task signals.""" + +from __future__ import annotations + +import re +import sys +from pathlib import Path + +import yaml + + +# Keep in sync with docs/agent-rules/INDEX.md β€” Task signal definitions. +CANONICAL_TASK_SIGNALS: frozenset[str] = frozenset( + { + "session-bootstrap", + "implementation", + "openspec-change-selection", + "branch-management", + "github-public-work", + "change-readiness", + "finalization", + "release", + "documentation-update", + "repository-orientation", + "command-lookup", + "detailed-reference", + "verification", + } +) + +_FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL | re.MULTILINE) + + +def _parse_frontmatter(text: str) -> dict[str, object] | None: + match = _FRONTMATTER_RE.match(text) + if not match: + return None + try: + loaded = yaml.safe_load(match.group(1)) + except yaml.YAMLError: + return None + return loaded if isinstance(loaded, dict) else None + + +def main() -> int: + root = Path(__file__).resolve().parents[1] + rules_dir = root / "docs" / "agent-rules" + if not rules_dir.is_dir(): + sys.stderr.write("validate_agent_rule_applies_when: docs/agent-rules not found\n") + return 2 + + errors: list[str] = [] + for path in sorted(rules_dir.glob("*.md")): + text = path.read_text(encoding="utf-8") + data = _parse_frontmatter(text) + if data is None: + continue + raw = data.get("applies_when") + if raw is None: + continue + if isinstance(raw, str): + signals = [raw] + elif isinstance(raw, list): + signals = [str(x) for x in raw if x is not None] + else: + errors.append(f"{path.name}: applies_when must be a list or string") + continue + for sig in signals: + if sig not in CANONICAL_TASK_SIGNALS: + errors.append( + f"{path.name}: unknown applies_when value {sig!r} " + f"(not in canonical set; update INDEX.md or fix frontmatter)" + ) + + if errors: + sys.stderr.write( + "validate_agent_rule_applies_when: invalid applies_when values " + "(see docs/agent-rules/INDEX.md β€” Task signal definitions):\n" + ) + for line in errors: + sys.stderr.write(f" {line}\n") + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/skills/specfact-code-review/SKILL.md b/skills/specfact-code-review/SKILL.md index dbcd60d5..9e234bfc 100644 --- a/skills/specfact-code-review/SKILL.md +++ b/skills/specfact-code-review/SKILL.md @@ -9,6 +9,7 @@ allowed-tools: [] Updated: 2026-03-16 | Module: nold-ai/specfact-code-review ## DO + - Ask whether tests should be included before repo-wide review; default to excluding tests unless test changes are the target - Keep functions under 120 LOC and cyclomatic complexity <= 12 - Add @require/@ensure (icontract) + @beartype to all new public APIs @@ -19,11 +20,12 @@ Updated: 2026-03-16 | Module: nold-ai/specfact-code-review - Use get_logger(__name__) from common.logger_setup, never print() ## DON'T + - Don't enable known noisy findings unless you explicitly want strict/full review output - Don't mix read + write in the same method; split responsibilities - Don't use bare except: or except Exception: pass - Don't add # noqa / # type: ignore without inline justification -- Don't call repository.* and http_client.* in the same function +- Don't call repository.*and http_client.* in the same function - Don't import at module level if it triggers network calls - Don't hardcode secrets; use env vars via pydantic.BaseSettings - Don't create functions > 120 lines diff --git a/tests/helpers/doc_frontmatter_types.py b/tests/helpers/doc_frontmatter_types.py index 310eadff..2efa9a91 100644 --- a/tests/helpers/doc_frontmatter_types.py +++ b/tests/helpers/doc_frontmatter_types.py @@ -24,6 +24,7 @@ class CheckDocFrontmatterModule(Protocol): """Structural type for ``scripts/check_doc_frontmatter.py`` loaded via importlib.""" DocFrontmatter: type[DocFrontmatterModel] + AgentRuleFrontmatter: type[AgentRuleFrontmatterModel] parse_frontmatter: Callable[[Path], dict[str, Any]] resolve_owner: Callable[[str], bool] validate_glob_patterns: Callable[[list[str]], bool] @@ -38,12 +39,14 @@ class CheckDocFrontmatterModule(Protocol): @runtime_checkable class DocFrontmatterRecord(Protocol): - """Minimal validated model instance shape used by tests.""" + """Validated doc-sync record shape (``DocFrontmatter`` / ``AgentRuleFrontmatter`` base fields).""" title: str doc_owner: str tracks: list[str] last_reviewed: date + exempt: bool + exempt_reason: str @runtime_checkable @@ -53,3 +56,26 @@ class DocFrontmatterModel(Protocol): @classmethod def model_validate(cls, data: dict[str, object]) -> DocFrontmatterRecord: raise NotImplementedError + + +@runtime_checkable +class AgentRuleFrontmatterRecord(DocFrontmatterRecord, Protocol): + """Validated agent-rule record shape (mirrors ``AgentRuleFrontmatter`` public fields).""" + + id: str + always_load: bool + applies_when: list[str] + priority: int + blocking: bool + user_interaction_required: bool + stop_conditions: list[str] + depends_on: list[str] + + +@runtime_checkable +class AgentRuleFrontmatterModel(Protocol): + """Model type exposing ``model_validate`` for agent-rule docs.""" + + @classmethod + def model_validate(cls, data: dict[str, object]) -> AgentRuleFrontmatterRecord: + raise NotImplementedError diff --git a/tests/unit/docs/test_agent_rules_governance.py b/tests/unit/docs/test_agent_rules_governance.py new file mode 100644 index 00000000..ac0895c0 --- /dev/null +++ b/tests/unit/docs/test_agent_rules_governance.py @@ -0,0 +1,56 @@ +from __future__ import annotations + +from pathlib import Path + +from tests.helpers.doc_frontmatter_types import CheckDocFrontmatterModule + + +REPO_ROOT = Path(__file__).resolve().parents[3] + + +def test_agent_rules_index_and_checklist_exist() -> None: + index_path = REPO_ROOT / "docs" / "agent-rules" / "INDEX.md" + checklist_path = REPO_ROOT / "docs" / "agent-rules" / "05-non-negotiable-checklist.md" + + assert index_path.exists() + assert checklist_path.exists() + + +def test_agent_rules_index_has_deterministic_bootstrap_metadata( + check_doc_frontmatter_module: CheckDocFrontmatterModule, +) -> None: + parse_frontmatter = check_doc_frontmatter_module.parse_frontmatter + frontmatter = parse_frontmatter(REPO_ROOT / "docs" / "agent-rules" / "INDEX.md") + assert isinstance(frontmatter, dict) + + applies_when = frontmatter["applies_when"] + assert isinstance(applies_when, list) + + assert frontmatter["id"] == "agent-rules-index" + assert frontmatter["always_load"] is True + assert "session-bootstrap" in applies_when + assert frontmatter["priority"] == 0 + + +def test_non_negotiable_checklist_is_always_loaded( + check_doc_frontmatter_module: CheckDocFrontmatterModule, +) -> None: + parse_frontmatter = check_doc_frontmatter_module.parse_frontmatter + frontmatter = parse_frontmatter(REPO_ROOT / "docs" / "agent-rules" / "05-non-negotiable-checklist.md") + assert isinstance(frontmatter, dict) + + depends_on = frontmatter["depends_on"] + assert isinstance(depends_on, list) + + assert frontmatter["id"] == "agent-rules-non-negotiable-checklist" + assert frontmatter["always_load"] is True + assert "agent-rules-index" in depends_on + + +def test_agents_references_canonical_rule_docs() -> None: + agents_text = (REPO_ROOT / "AGENTS.md").read_text(encoding="utf-8") + + assert "docs/agent-rules/INDEX.md" in agents_text + assert "docs/agent-rules/05-non-negotiable-checklist.md" in agents_text + assert "## Strategic context" in agents_text + assert "internal-wiki-and-strategic-context" in agents_text diff --git a/tests/unit/scripts/test_check_version_sources.py b/tests/unit/scripts/test_check_version_sources.py new file mode 100644 index 00000000..7b9dc084 --- /dev/null +++ b/tests/unit/scripts/test_check_version_sources.py @@ -0,0 +1,45 @@ +"""Tests for scripts/check_version_sources.py.""" + +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + + +def test_check_version_sources_passes_on_repo() -> None: + """Current checkout must keep canonical version files aligned.""" + script = Path(__file__).resolve().parents[3] / "scripts" / "check_version_sources.py" + completed = subprocess.run( + [sys.executable, str(script)], + check=False, + capture_output=True, + text=True, + ) + assert completed.returncode == 0, completed.stderr + + +def test_check_version_sources_detects_mismatch(tmp_path: Path) -> None: + """Mismatched __version__ in one file must fail the check.""" + script_src = Path(__file__).resolve().parents[3] / "scripts" / "check_version_sources.py" + scripts_dir = tmp_path / "scripts" + scripts_dir.mkdir() + script = scripts_dir / "check_version_sources.py" + script.write_text(script_src.read_text(encoding="utf-8"), encoding="utf-8") + + (tmp_path / "pyproject.toml").write_text('version = "9.9.9"\n', encoding="utf-8") + (tmp_path / "setup.py").write_text('version="9.9.9"', encoding="utf-8") + (tmp_path / "src").mkdir() + (tmp_path / "src" / "__init__.py").write_text('__version__ = "9.9.9"\n', encoding="utf-8") + (tmp_path / "src" / "specfact_cli").mkdir(parents=True) + (tmp_path / "src" / "specfact_cli" / "__init__.py").write_text('__version__ = "1.0.0"\n', encoding="utf-8") + + completed = subprocess.run( + [sys.executable, str(script)], + cwd=str(tmp_path), + check=False, + capture_output=True, + text=True, + ) + assert completed.returncode == 1 + assert "mismatch" in completed.stderr.lower() diff --git a/tests/unit/scripts/test_doc_frontmatter/test_agent_rule_frontmatter.py b/tests/unit/scripts/test_doc_frontmatter/test_agent_rule_frontmatter.py new file mode 100644 index 00000000..1b521447 --- /dev/null +++ b/tests/unit/scripts/test_doc_frontmatter/test_agent_rule_frontmatter.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +"""Tests for agent-rule frontmatter validation.""" + +from __future__ import annotations + +import tempfile +from pathlib import Path + +import pytest + +from tests.helpers.doc_frontmatter import write_enforced +from tests.helpers.doc_frontmatter_types import CheckDocFrontmatterModule + + +VALID_AGENT_RULE_FRONTMATTER = """--- +title: "Agent Rules Index" +id: agent-rules-index +doc_owner: specfact-cli +tracks: + - AGENTS.md + - docs/agent-rules/** +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +always_load: true +applies_when: + - session-bootstrap +priority: 0 +blocking: true +user_interaction_required: false +stop_conditions: + - canonical rule index missing +depends_on: [] +--- + +# Agent Rules Index +""" + + +class TestAgentRuleFrontmatterModel: + """Pydantic model for deterministic agent-rule frontmatter.""" + + def test_model_validate_accepts_valid_rule_dict( + self, check_doc_frontmatter_module: CheckDocFrontmatterModule + ) -> None: + agent_rule_model = check_doc_frontmatter_module.AgentRuleFrontmatter + data = { + "title": "Agent Rules Index", + "id": "agent-rules-index", + "doc_owner": "specfact-cli", + "tracks": ["AGENTS.md", "docs/agent-rules/**"], + "last_reviewed": "2026-04-10", + "exempt": False, + "exempt_reason": "", + "always_load": True, + "applies_when": ["session-bootstrap"], + "priority": 0, + "blocking": True, + "user_interaction_required": False, + "stop_conditions": ["canonical rule index missing"], + "depends_on": [], + } + + rec = agent_rule_model.model_validate(data) + assert rec.id == "agent-rules-index" + assert rec.always_load is True + assert rec.priority == 0 + assert rec.applies_when == ["session-bootstrap"] + + +class TestAgentRuleFrontmatterValidation: + """Integration-style validation of docs/agent-rules files.""" + + def test_validation_accepts_valid_agent_rule_file( + self, monkeypatch: pytest.MonkeyPatch, check_doc_frontmatter_module: CheckDocFrontmatterModule + ) -> None: + validation_main = check_doc_frontmatter_module.main + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + monkeypatch.setenv("DOC_FRONTMATTER_ROOT", str(root)) + rules_dir = root / "docs" / "agent-rules" + rules_dir.mkdir(parents=True) + (rules_dir / "INDEX.md").write_text(VALID_AGENT_RULE_FRONTMATTER, encoding="utf-8") + write_enforced(root, "docs/agent-rules/INDEX.md") + + result = validation_main([]) + assert result == 0 + + def test_validation_rejects_missing_agent_rule_fields( + self, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], + check_doc_frontmatter_module: CheckDocFrontmatterModule, + ) -> None: + validation_main = check_doc_frontmatter_module.main + with tempfile.TemporaryDirectory() as temp_dir: + root = Path(temp_dir) + monkeypatch.setenv("DOC_FRONTMATTER_ROOT", str(root)) + rules_dir = root / "docs" / "agent-rules" + rules_dir.mkdir(parents=True) + (rules_dir / "INDEX.md").write_text( + """--- +title: "Agent Rules Index" +id: agent-rules-index +doc_owner: specfact-cli +tracks: + - AGENTS.md +last_reviewed: 2026-04-10 +exempt: false +exempt_reason: "" +always_load: true +priority: 0 +blocking: true +user_interaction_required: false +stop_conditions: + - canonical rule index missing +depends_on: [] +--- + +# Agent Rules Index +""", + encoding="utf-8", + ) + write_enforced(root, "docs/agent-rules/INDEX.md") + + result = validation_main([]) + assert result != 0 + err = capsys.readouterr().err + assert "docs/agent-rules/INDEX.md" in err + assert "applies_when" in err diff --git a/tests/unit/scripts/test_sync_github_hierarchy_cache.py b/tests/unit/scripts/test_sync_github_hierarchy_cache.py new file mode 100644 index 00000000..6ce7c20c --- /dev/null +++ b/tests/unit/scripts/test_sync_github_hierarchy_cache.py @@ -0,0 +1,508 @@ +"""Tests for scripts/sync_github_hierarchy_cache.py.""" + +from __future__ import annotations + +import importlib.util +import subprocess +import sys +from collections.abc import Iterator +from functools import lru_cache +from pathlib import Path +from typing import Any, TypedDict + +import pytest + + +class IssueOptions(TypedDict, total=False): + """Optional test issue fields.""" + + labels: list[str] + summary: str + parent: tuple[int, str] + children: list[tuple[int, str]] + updated_at: str + + +@lru_cache(maxsize=1) +def _load_script_module() -> Any: + """Load scripts/sync_github_hierarchy_cache.py as a Python module (cached for stable types).""" + script_path = Path(__file__).resolve().parents[3] / "scripts" / "sync_github_hierarchy_cache.py" + spec = importlib.util.spec_from_file_location("sync_github_hierarchy_cache", script_path) + if spec is None or spec.loader is None: + raise AssertionError(f"Unable to load script module at {script_path}") + sys.modules.pop(spec.name, None) + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +@pytest.fixture +def reset_sync_github_hierarchy_module() -> Iterator[None]: + """Reload ``sync_github_hierarchy_cache`` cleanly; teardown runs even if the test fails.""" + _load_script_module.cache_clear() + sys.modules.pop("sync_github_hierarchy_cache", None) + yield + _load_script_module.cache_clear() + sys.modules.pop("sync_github_hierarchy_cache", None) + + +def _make_issue( + module: Any, + *, + number: int, + title: str, + issue_type: str, + options: IssueOptions | None = None, +) -> Any: + """Create a HierarchyIssue instance for tests.""" + issue_options = options or {} + children = issue_options.get("children", []) + child_links = [ + module.IssueLink(number=child_number, title=child_title, url=f"https://example.test/issues/{child_number}") + for child_number, child_title in children + ] + + parent_link = None + parent = issue_options.get("parent") + if parent is not None: + parent_number, parent_title = parent + parent_link = module.IssueLink( + number=parent_number, + title=parent_title, + url=f"https://example.test/issues/{parent_number}", + ) + + return module.HierarchyIssue( + number=number, + title=title, + url=f"https://example.test/issues/{number}", + issue_type=issue_type, + labels=issue_options.get("labels", []), + summary=issue_options.get("summary", ""), + updated_at=issue_options.get("updated_at", "2026-04-09T08:00:00Z"), + parent=parent_link, + children=child_links, + ) + + +def test_compute_hierarchy_fingerprint_is_order_independent() -> None: + """Fingerprinting should stay stable regardless of input ordering.""" + module = _load_script_module() + + epic = _make_issue( + module, + number=485, + title="[Epic] Governance", + issue_type="Epic", + options={ + "labels": ["openspec", "Epic"], + "summary": "Governance epic.", + "children": [(486, "[Feature] Alignment")], + }, + ) + feature = _make_issue( + module, + number=486, + title="[Feature] Alignment", + issue_type="Feature", + options={ + "labels": ["Feature", "openspec"], + "summary": "Alignment feature.", + "parent": (485, "[Epic] Governance"), + }, + ) + + first = module.compute_hierarchy_fingerprint([epic, feature]) + second = module.compute_hierarchy_fingerprint([feature, epic]) + + assert first == second + + +def test_extract_summary_skips_heading_only_lines() -> None: + """Summary extraction should skip markdown section headers.""" + module = _load_script_module() + extract_summary = module._extract_summary # pylint: disable=protected-access + + summary = extract_summary("## Why\n\nThis cache avoids repeated GitHub lookups.") + + assert summary == "This cache avoids repeated GitHub lookups." + + +@pytest.mark.parametrize( + ("url", "expected"), + [ + ("https://github.com/nold-ai/specfact-cli.git", "specfact-cli"), + ("git@github.com:nold-ai/specfact-cli.git", "specfact-cli"), + ("https://github.com/org/my-repo/", "my-repo"), + ], +) +def test_parse_repo_name_from_remote_url(url: str, expected: str) -> None: + """Remote URL tail parsing should yield the GitHub repository name.""" + module = _load_script_module() + assert module.parse_repo_name_from_remote_url(url) == expected + + +def test_parse_repo_name_from_remote_url_empty_returns_none() -> None: + """Blank remote URLs should not produce a repository name.""" + module = _load_script_module() + assert module.parse_repo_name_from_remote_url("") is None + assert module.parse_repo_name_from_remote_url(" ") is None + + +def test_default_repo_name_matches_git_origin_url() -> None: + """When ``remote.origin.url`` exists, DEFAULT_REPO_NAME must match its repository segment (worktrees).""" + module = _load_script_module() + scripts_dir = Path(__file__).resolve().parents[3] / "scripts" + completed = subprocess.run( + ["git", "-C", str(scripts_dir), "config", "--get", "remote.origin.url"], + check=False, + capture_output=True, + text=True, + ) + if completed.returncode != 0 or not completed.stdout.strip(): + pytest.skip("No git origin in this environment") + expected = module.parse_repo_name_from_remote_url(completed.stdout) + assert expected is not None + assert expected == module.DEFAULT_REPO_NAME + + +def test_default_paths_use_ephemeral_specfact_backlog_cache() -> None: + """Default cache files should live in ignored .specfact/backlog storage.""" + module = _load_script_module() + + assert str(module.DEFAULT_OUTPUT_PATH) == ".specfact/backlog/github_hierarchy_cache.md" + assert str(module.DEFAULT_STATE_PATH) == ".specfact/backlog/github_hierarchy_cache_state.json" + + +def test_child_links_include_only_epic_and_feature_subissues() -> None: + """Sub-issue GraphQL nodes should contribute children only when type is Epic or Feature.""" + module = _load_script_module() + child_links = module._child_links( # pylint: disable=protected-access + [ + {"number": 1, "title": "Task", "url": "https://example.test/1", "issueType": {"name": "Task"}}, + {"number": 2, "title": "Feat", "url": "https://example.test/2", "issueType": {"name": "Feature"}}, + {"number": 3, "title": "Ep", "url": "https://example.test/3", "issueType": {"name": "Epic"}}, + {"number": 4, "title": "Untyped", "url": "https://example.test/4"}, + ] + ) + assert [link.number for link in child_links] == [2, 3] + + +def test_render_cache_markdown_groups_epics_and_features() -> None: + """Rendered markdown should be deterministic and grouped by issue type.""" + module = _load_script_module() + + issues = [ + _make_issue( + module, + number=486, + title="[Feature] Alignment", + issue_type="Feature", + options={ + "labels": ["openspec", "Feature"], + "summary": "Alignment feature.", + "parent": (485, "[Epic] Governance"), + }, + ), + _make_issue( + module, + number=485, + title="[Epic] Governance", + issue_type="Epic", + options={ + "labels": ["Epic", "openspec"], + "summary": "Governance epic.", + "children": [(486, "[Feature] Alignment")], + }, + ), + ] + + rendered = module.render_cache_markdown( + repo_full_name="nold-ai/specfact-cli", + issues=issues, + generated_at="2026-04-09T08:30:00Z", + fingerprint="abc123", + ) + + assert "# GitHub Hierarchy Cache" in rendered + assert "## Epics" in rendered + assert "## Features" in rendered + assert rendered.index("### #485") < rendered.index("### #486") + assert "- Parent: none" in rendered + assert "- Parent: #485 [Epic] Governance" in rendered + assert "- Labels: Epic, openspec" in rendered + assert "- Labels: Feature, openspec" in rendered + + +def test_sync_cache_skips_write_when_fingerprint_is_unchanged(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + """sync_cache should not rewrite output when the fingerprint matches state.""" + module = _load_script_module() + + output_path = tmp_path / "GITHUB_HIERARCHY_CACHE.md" + state_path = tmp_path / ".github_hierarchy_cache_state.json" + output_path.write_text("unchanged cache\n", encoding="utf-8") + state_path.write_text( + '{"fingerprint":"same","repo":"nold-ai/specfact-cli"}', + encoding="utf-8", + ) + + issues = [ + _make_issue( + module, + number=485, + title="[Epic] Governance", + issue_type="Epic", + options={ + "labels": ["Epic"], + "summary": "Governance epic.", + }, + ) + ] + + def _fake_fetch(*, repo_owner: str, repo_name: str, fingerprint_only: bool) -> list[Any]: + assert repo_owner == "nold-ai" + assert repo_name == "specfact-cli" + assert fingerprint_only is False + return issues + + def _same_fingerprint(_: list[Any]) -> str: + return "same" + + monkeypatch.setattr(module, "fetch_hierarchy_issues", _fake_fetch) + monkeypatch.setattr(module, "compute_hierarchy_fingerprint", _same_fingerprint) + + result = module.sync_cache( + repo_owner="nold-ai", + repo_name="specfact-cli", + output_path=output_path, + state_path=state_path, + ) + + assert result.changed is False + assert result.issue_count == 1 + assert output_path.read_text(encoding="utf-8") == "unchanged cache\n" + + +def test_sync_cache_repo_mismatch_rewrites_despite_matching_fingerprint( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path +) -> None: + """Matching fingerprint for another repo must not skip; cache would keep wrong Repository metadata.""" + module = _load_script_module() + + output_path = tmp_path / "GITHUB_HIERARCHY_CACHE.md" + state_path = tmp_path / ".github_hierarchy_cache_state.json" + output_path.write_text("# stale\n\n- Repository: `other/other`\n", encoding="utf-8") + state_path.write_text( + '{"fingerprint":"same","repo":"other/other"}', + encoding="utf-8", + ) + + def _fake_fetch(*, repo_owner: str, repo_name: str, fingerprint_only: bool) -> list[Any]: + assert repo_owner == "nold-ai" + assert repo_name == "specfact-cli" + assert fingerprint_only is False + return [] + + monkeypatch.setattr(module, "fetch_hierarchy_issues", _fake_fetch) + monkeypatch.setattr(module, "compute_hierarchy_fingerprint", lambda _: "same") + + result = module.sync_cache( + repo_owner="nold-ai", + repo_name="specfact-cli", + output_path=output_path, + state_path=state_path, + ) + + assert result.changed is True + body = output_path.read_text(encoding="utf-8") + assert "nold-ai/specfact-cli" in body + assert "other/other" not in body + + +def test_sync_cache_missing_repo_in_state_rewrites(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + """State files without repo (pre-fix) must not short-circuit the skip path.""" + module = _load_script_module() + + output_path = tmp_path / "GITHUB_HIERARCHY_CACHE.md" + state_path = tmp_path / ".github_hierarchy_cache_state.json" + output_path.write_text("legacy cache\n", encoding="utf-8") + state_path.write_text('{"fingerprint":"same"}', encoding="utf-8") + + issues = [ + _make_issue( + module, + number=485, + title="[Epic] Governance", + issue_type="Epic", + options={"labels": ["Epic"], "summary": "Governance epic."}, + ) + ] + + def _fake_fetch(*, repo_owner: str, repo_name: str, fingerprint_only: bool) -> list[Any]: + assert repo_owner == "nold-ai" + assert repo_name == "specfact-cli" + assert fingerprint_only is False + return issues + + monkeypatch.setattr(module, "fetch_hierarchy_issues", _fake_fetch) + monkeypatch.setattr(module, "compute_hierarchy_fingerprint", lambda _: "same") + + result = module.sync_cache( + repo_owner="nold-ai", + repo_name="specfact-cli", + output_path=output_path, + state_path=state_path, + ) + + assert result.changed is True + assert "# GitHub Hierarchy Cache" in output_path.read_text(encoding="utf-8") + + +def test_sync_cache_force_rewrites_when_fingerprint_unchanged(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + """sync_cache with force=True must rewrite output even when fingerprint matches state.""" + module = _load_script_module() + + output_path = tmp_path / "GITHUB_HIERARCHY_CACHE.md" + state_path = tmp_path / ".github_hierarchy_cache_state.json" + output_path.write_text("stale cache\n", encoding="utf-8") + state_path.write_text('{"fingerprint":"same"}', encoding="utf-8") + + issues = [ + _make_issue( + module, + number=485, + title="[Epic] Governance", + issue_type="Epic", + options={ + "labels": ["Epic"], + "summary": "Governance epic.", + }, + ) + ] + + def _fake_fetch(*, repo_owner: str, repo_name: str, fingerprint_only: bool) -> list[Any]: + assert repo_owner == "nold-ai" + assert repo_name == "specfact-cli" + assert fingerprint_only is False + return issues + + monkeypatch.setattr(module, "fetch_hierarchy_issues", _fake_fetch) + monkeypatch.setattr(module, "compute_hierarchy_fingerprint", lambda _: "same") + + result = module.sync_cache( + repo_owner="nold-ai", + repo_name="specfact-cli", + output_path=output_path, + state_path=state_path, + force=True, + ) + + assert result.changed is True + assert "# GitHub Hierarchy Cache" in output_path.read_text(encoding="utf-8") + + +def test_sync_cache_propagates_graphql_failure(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + """RuntimeError from GitHub GraphQL should surface to callers.""" + module = _load_script_module() + + def _boom(_query: str, *, repo_owner: str, repo_name: str, **_kwargs: Any) -> Any: + assert repo_owner == "nold-ai" + assert repo_name == "specfact-cli" + raise RuntimeError("graphql failed") + + monkeypatch.setattr(module, "_run_graphql_query", _boom) + + with pytest.raises(RuntimeError, match="graphql failed"): + module.sync_cache( + repo_owner="nold-ai", + repo_name="specfact-cli", + output_path=tmp_path / "out.md", + state_path=tmp_path / "state.json", + ) + + +def test_sync_cache_malformed_state_regenerates_cache(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None: + """Invalid state JSON is treated as missing state and triggers a full sync.""" + module = _load_script_module() + + output_path = tmp_path / "GITHUB_HIERARCHY_CACHE.md" + state_path = tmp_path / ".github_hierarchy_cache_state.json" + state_path.write_text("{not-json", encoding="utf-8") + + issues = [ + _make_issue( + module, + number=485, + title="[Epic] Governance", + issue_type="Epic", + options={ + "labels": ["Epic"], + "summary": "Governance epic.", + }, + ) + ] + + fetch_calls = 0 + + def _fake_fetch(*, repo_owner: str, repo_name: str, fingerprint_only: bool) -> list[Any]: + nonlocal fetch_calls + fetch_calls += 1 + assert repo_owner == "nold-ai" + assert repo_name == "specfact-cli" + assert fingerprint_only is False + return issues + + monkeypatch.setattr(module, "fetch_hierarchy_issues", _fake_fetch) + + result = module.sync_cache( + repo_owner="nold-ai", + repo_name="specfact-cli", + output_path=output_path, + state_path=state_path, + ) + + assert fetch_calls == 1 + assert result.changed is True + assert "# GitHub Hierarchy Cache" in output_path.read_text(encoding="utf-8") + + +def test_default_repo_name_falls_back_when_git_unavailable( + reset_sync_github_hierarchy_module: None, + monkeypatch: pytest.MonkeyPatch, +) -> None: + """If ``git`` is missing, DEFAULT_REPO_NAME must use the checkout directory fallback.""" + + def _no_git(*_args: Any, **_kwargs: Any) -> Any: + raise FileNotFoundError("git not found") + + monkeypatch.setattr(subprocess, "run", _no_git) + module = _load_script_module() + script_path = Path(__file__).resolve().parents[3] / "scripts" / "sync_github_hierarchy_cache.py" + expected_fallback = script_path.resolve().parents[1].name + assert expected_fallback == module.DEFAULT_REPO_NAME + + +def test_main_reports_runtime_error_to_stderr_and_returns_one( + monkeypatch: pytest.MonkeyPatch, tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: + """main() must exit 1 and print a clear error when sync_cache raises RuntimeError.""" + module = _load_script_module() + + def _boom(**_kwargs: Any) -> Any: + raise RuntimeError("GitHub GraphQL query failed") + + monkeypatch.setattr(module, "sync_cache", _boom) + code = module.main( + [ + "--output", + str(tmp_path / "out.md"), + "--state-file", + str(tmp_path / "state.json"), + ] + ) + assert code == 1 + captured = capsys.readouterr() + assert "GitHub hierarchy cache sync failed" in captured.err + assert "GitHub GraphQL query failed" in captured.err + assert captured.out == "" diff --git a/tests/unit/scripts/test_validate_agent_rule_applies_when.py b/tests/unit/scripts/test_validate_agent_rule_applies_when.py new file mode 100644 index 00000000..1664e713 --- /dev/null +++ b/tests/unit/scripts/test_validate_agent_rule_applies_when.py @@ -0,0 +1,18 @@ +"""Tests for scripts/validate_agent_rule_applies_when.py.""" + +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + + +def test_validate_agent_rule_applies_when_passes() -> None: + script = Path(__file__).resolve().parents[3] / "scripts" / "validate_agent_rule_applies_when.py" + completed = subprocess.run( + [sys.executable, str(script)], + check=False, + capture_output=True, + text=True, + ) + assert completed.returncode == 0, completed.stderr