pactkit 2.9.1__tar.gz → 2.9.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pactkit-2.9.1 → pactkit-2.9.2}/.github/workflows/pactkit.yml +1 -1
- {pactkit-2.9.1 → pactkit-2.9.2}/.opencode/pactkit.yaml +3 -68
- {pactkit-2.9.1 → pactkit-2.9.2}/CHANGELOG.md +7 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/PKG-INFO +1 -1
- {pactkit-2.9.1 → pactkit-2.9.2}/pyproject.toml +1 -1
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/__init__.py +1 -1
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/doctor.py +18 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/profiles.py +7 -0
- pactkit-2.9.1/docs/specs/BUG-slim-007.md +0 -138
- {pactkit-2.9.1 → pactkit-2.9.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/.github/dependabot.yml +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/.github/workflows/ci.yml +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/.github/workflows/publish.yml +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/.gitignore +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/AGENTS.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/CODE_OF_CONDUCT.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/CONTRIBUTING.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/LICENSE +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/README.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/SECURITY.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/assets/logo.png +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/guides/codex-integration-preresearch.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/guides/tool-integration-checklist.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-001.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-002.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-003.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-004.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-005.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-006.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-007.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-008.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-009.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-010.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-011.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-012.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-013.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-014.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-015.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-016.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-017.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-018.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-019.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-020.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-021.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-022.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-023.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-024.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-025.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-026.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-027.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-028.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-029.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-030.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-031.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-032.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-033.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-034.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-035.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-slim-001.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-slim-002.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-slim-003.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-slim-004.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-slim-005.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/specs/BUG-slim-006.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/test_cases/BUG-001_case.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/docs/test_cases/BUG-002_case.md +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/opencode.json +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/__main__.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/backfill.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/cleaners.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/cli.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/config.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/context_gen.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/coverage_gate.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/generators/__init__.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/generators/adapter.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/generators/deploy_base.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/generators/deployer.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/guards.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/id_generator.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/invariants.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/issue_sync.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/lazy_visualize.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/lessons.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/lint_runner.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/prompts/__init__.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/prompts/agents.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/prompts/commands.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/prompts/references.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/prompts/rules.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/prompts/skills.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/prompts/workflows.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/regression.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/schemas.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/scripts.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/sec_scope.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/skills/__init__.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/skills/board.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/skills/scaffold.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/skills/spec_linter.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/skills/visualize.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/spec_status.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/test_mapper.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/utils.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/src/pactkit/validators.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/conftest.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/e2e/__init__.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/e2e/cli/__init__.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/e2e/cli/test_cli_e2e.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/fixtures/agent_parser/agents_dir/researcher.yaml +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/fixtures/agent_parser/agents_dir/writer.yaml +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/fixtures/agent_parser/langgraph_app.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/fixtures/agent_parser/mcp_settings.json +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/fixtures/api_call_parser/axios_style.tsx +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/fixtures/api_call_parser/dynamic.tsx +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/fixtures/api_call_parser/page.tsx +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/integration/__init__.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/integration/test_deploy_classic.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_agent_features.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_agent_frontmatter.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_agents_enrichment.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_board_bug027.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_board_sections.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug001_skill_path.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug002_plugin_paths.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug003_multi_import.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug004_dead_set.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug005_archive_taskless.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug006_scan_excludes.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug007_stale_trace_refs.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug008_stale_command_refs.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug009_project_config_backfill.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug010_rewrite_yaml.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug011_stale_refs.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug012_call_graph_filter.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug013_config_single_source.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug014_version_hygiene.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug017_project_init_playbook.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug018_issue_tracker_verification.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug019_venv_deployment.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug020_claude_md_backup.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug025_release_delegation.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug026_version_sync.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug028_ghost_refs.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug029_stack_detection_fallback.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug030_spec_lint_cli.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug031_docstring_accuracy.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug034_plan_metadata_template.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug_021.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug_022.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug_023.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug_024.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug_slim001_env_detection.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug_slim002_instruction_collision.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug_slim003.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug_slim004.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug_slim005.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_bug_slim006.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_check_command.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_command_frontmatter.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_command_visualize_modes.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_config.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_config_auto_merge.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_constitution_sharpening.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_create_skill.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_cross_flow_matrix.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_deploy_base.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_deployer_cleanup.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_deployer_plugin.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_design_command.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_done_gates.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_draw_prompt.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_draw_references.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_drawio_mcp.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_home_path_fix.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_hotfix_command.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_init_guard.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_lang_profiles.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_list_stories.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_mcp_integration.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_model_config.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_modular_constitution.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_monorepo_detect.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_multi_prefix.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_pdca_slim.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_profiles.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_project_visibility.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_prompt_cli_refs.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_prompt_structural_invariants.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_prompts_package.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_release.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_release_field.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_release_v110.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_render_prompt.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_review_command.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_rules_enrichment.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_scaffold.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_scaffold_developer_prefix.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_schemas.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_script_extraction.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_selective_deploy.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_session_context.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_skill_structure.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_skills_enrichment.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_smart_regression.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_sprint_command.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_stack_references.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_status_command.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_statusline.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story014_release.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story015_ci_lint_gate.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story016_claude_md.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story017_init_claude_md.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story018_arch_staleness.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story019_bailout.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story020_horizon.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story021_rfc.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story022_decision_tree.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story023_test_quality.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story024_native_agent.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story025_ci_pipeline.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story026_issue_tracker.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story027_hooks.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story028_rule_scoping.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story029_doctor.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story030_lint.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story031_git_init_guard.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story032_greenfield_redirect.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story033_config_backfill.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story034_plan_config_refresh.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story035_readme_docs.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story037_regression_fix.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story038_call_graph_update.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story039_venv_config.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story040_layered_claude_md.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story042_spec_linter.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story043_active_clarify.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story044_consistency_check.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story045_auto_pr.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story046_agent_adapter.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story047_enterprise_flags.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story048_worktree_isolation.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story049_community_standards.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story050_doc_only_shortcut.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story051_workflow_streamlining.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story052_conditional_github_release.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story053_impact_regression.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story055_commands.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story055_config.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story055_spec_linter.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story056_commands.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story056_config.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story057_implicit_cleanup.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story058_opencode_extraction.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story058_routing_fix.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story059_codex_removal.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story060_init_hang.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story061_remove_thinking.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story062_mcp_recommendations.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story063_prompt_slimming.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story064_venv_local_md.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story065_sprint_model.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story072_developer_prefix.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim009_lazy_rules.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim010_dry_refactor.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim011_command_rules.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim012_ci.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim014_clean.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim014_context.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim014_guard.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim014_lazy_viz.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim014_next_id.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim014_regression.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim014_sec_scope.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim014_validators.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim015_doctor.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim016_testmap_lint.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim017.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim018.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim019_plan_subphases.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim020_explore_stall_fix.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim021.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim022.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim023.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim024.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim025.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim026.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim027.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim028.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim029.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim030.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim031.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim032.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim033.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim034.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim035.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim036.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim037.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim038.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim039.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim040.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim041.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim042.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim043.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim044.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim045.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim046.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim047.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim048.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim049.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim051.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim052.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim053.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim054.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim055.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim056.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim060_codex_profile.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim063.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_story_slim065.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_tools.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_topology_parsers_066.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_update_task.py +0 -0
- {pactkit-2.9.1 → pactkit-2.9.2}/tests/unit/test_visualize_modes.py +0 -0
|
@@ -1,81 +1,16 @@
|
|
|
1
1
|
# PactKit Configuration
|
|
2
|
-
#
|
|
3
|
-
#
|
|
2
|
+
# All agents, commands, skills, and rules are deployed by default.
|
|
3
|
+
# To exclude specific items, add an exclude list (e.g., exclude_skills: [pactkit-draw]).
|
|
4
4
|
|
|
5
5
|
version: "2.9.1"
|
|
6
6
|
stack: python
|
|
7
7
|
root: .
|
|
8
8
|
developer: "slim"
|
|
9
9
|
|
|
10
|
-
# Agents — AI role definitions
|
|
11
|
-
agents:
|
|
12
|
-
- code-explorer
|
|
13
|
-
- product-designer
|
|
14
|
-
- qa-engineer
|
|
15
|
-
- repo-maintainer
|
|
16
|
-
- security-auditor
|
|
17
|
-
- senior-developer
|
|
18
|
-
- system-architect
|
|
19
|
-
- system-medic
|
|
20
|
-
- visual-architect
|
|
21
|
-
|
|
22
|
-
# Commands — PDCA playbooks
|
|
23
|
-
commands:
|
|
24
|
-
- project-act
|
|
25
|
-
- project-check
|
|
26
|
-
- project-clarify
|
|
27
|
-
- project-design
|
|
28
|
-
- project-done
|
|
29
|
-
- project-hotfix
|
|
30
|
-
- project-init
|
|
31
|
-
- project-plan
|
|
32
|
-
- project-pr
|
|
33
|
-
- project-release
|
|
34
|
-
- project-sprint
|
|
35
|
-
|
|
36
|
-
# Skills — tool scripts
|
|
37
|
-
skills:
|
|
38
|
-
- pactkit-analyze
|
|
39
|
-
- pactkit-board
|
|
40
|
-
- pactkit-doctor
|
|
41
|
-
- pactkit-draw
|
|
42
|
-
- pactkit-release
|
|
43
|
-
- pactkit-review
|
|
44
|
-
- pactkit-scaffold
|
|
45
|
-
- pactkit-status
|
|
46
|
-
- pactkit-trace
|
|
47
|
-
- pactkit-visualize
|
|
48
|
-
- project-act
|
|
49
|
-
- project-check
|
|
50
|
-
- project-clarify
|
|
51
|
-
- project-design
|
|
52
|
-
- project-done
|
|
53
|
-
- project-hotfix
|
|
54
|
-
- project-init
|
|
55
|
-
- project-plan
|
|
56
|
-
- project-pr
|
|
57
|
-
- project-release
|
|
58
|
-
- project-sprint
|
|
59
|
-
|
|
60
|
-
# Rules — constitution modules
|
|
61
|
-
rules:
|
|
62
|
-
- 01-core-protocol
|
|
63
|
-
- 02-hierarchy-of-truth
|
|
64
|
-
- 03-file-atlas
|
|
65
|
-
- 04-routing-table
|
|
66
|
-
- 05-workflow-conventions
|
|
67
|
-
- 06-mcp-integration
|
|
68
|
-
- 07-shared-protocols
|
|
69
|
-
- 08-architecture-principles
|
|
70
|
-
- 09-sectional-write
|
|
71
|
-
|
|
72
10
|
# CI/CD — set provider to github or gitlab to generate pipeline config
|
|
73
11
|
ci:
|
|
74
12
|
provider: github
|
|
75
|
-
|
|
76
|
-
# language_version: "3.11"
|
|
77
|
-
# github_host: "" # GHE server (empty = github.com)
|
|
78
|
-
# actions_ref: "" # GHE actions prefix
|
|
13
|
+
install_cmd: "python -m pip install --upgrade pip\n pip install -e \".[multilang]\"\n pip install pytest ruff\n pactkit init"
|
|
79
14
|
|
|
80
15
|
# Issue Tracker — set provider to github to link stories to issues
|
|
81
16
|
issue_tracker:
|
|
@@ -4,6 +4,13 @@ All notable changes to PactKit will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|
6
6
|
|
|
7
|
+
## [2.9.2] - 2026-03-30
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **FormatProfile.excluded_commands** — `project-sprint` excluded for OpenCode/Codex (requires subagent team, Claude Code only). Doctor `check_config_drift` now respects format-level exclusions.
|
|
11
|
+
- **Redundant pactkit.yaml component lists** — Removed explicit agents/commands/skills/rules lists from `.opencode/pactkit.yaml` (absence = deploy all).
|
|
12
|
+
- **Orphaned spec cleanup** — Removed 7 pre-developer-prefix spec files that were already archived under old IDs.
|
|
13
|
+
|
|
7
14
|
## [2.9.1] - 2026-03-30
|
|
8
15
|
|
|
9
16
|
### Added
|
|
@@ -76,6 +76,18 @@ def check_config_drift(project_root: Path) -> dict:
|
|
|
76
76
|
config_dir = yaml_path.parent # .claude/ or .opencode/ or .codex/
|
|
77
77
|
missing: list[dict] = []
|
|
78
78
|
|
|
79
|
+
# Detect format from yaml path to get format-level excluded commands
|
|
80
|
+
_format_excluded_commands: frozenset = frozenset()
|
|
81
|
+
try:
|
|
82
|
+
from pactkit.profiles import FORMAT_PROFILES
|
|
83
|
+
dir_name = config_dir.name # e.g. ".claude", ".opencode", ".codex"
|
|
84
|
+
for prof in FORMAT_PROFILES.values():
|
|
85
|
+
if prof.project_config_dir == dir_name:
|
|
86
|
+
_format_excluded_commands = prof.excluded_commands
|
|
87
|
+
break
|
|
88
|
+
except Exception:
|
|
89
|
+
pass
|
|
90
|
+
|
|
79
91
|
# Files are deployed globally (e.g., ~/.claude/, ~/.config/opencode/, ~/.codex/),
|
|
80
92
|
# not per-project. Check all known global deploy directories + project-local.
|
|
81
93
|
home = Path.home()
|
|
@@ -110,6 +122,9 @@ def check_config_drift(project_root: Path) -> dict:
|
|
|
110
122
|
if not isinstance(declared, list):
|
|
111
123
|
continue
|
|
112
124
|
for item in declared:
|
|
125
|
+
# Skip format-level excluded commands (e.g., project-sprint for opencode/codex)
|
|
126
|
+
if key == "commands" and item in _format_excluded_commands:
|
|
127
|
+
continue
|
|
113
128
|
if not _exists_in_any(subdir, f"{item}{suffix}"):
|
|
114
129
|
missing.append({"type": key.rstrip("s"), "name": item})
|
|
115
130
|
|
|
@@ -117,6 +132,9 @@ def check_config_drift(project_root: Path) -> dict:
|
|
|
117
132
|
declared_skills = data.get("skills")
|
|
118
133
|
if isinstance(declared_skills, list):
|
|
119
134
|
for skill in declared_skills:
|
|
135
|
+
# Skip format-level excluded commands deployed as skills
|
|
136
|
+
if skill in _format_excluded_commands:
|
|
137
|
+
continue
|
|
120
138
|
if not _dir_or_file_in_any("skills", skill):
|
|
121
139
|
missing.append({"type": "skill", "name": skill})
|
|
122
140
|
|
|
@@ -88,6 +88,10 @@ class FormatProfile:
|
|
|
88
88
|
excluded_agent_fields: frozenset
|
|
89
89
|
"""Agent YAML fields to exclude for this format. Replaces CLAUDE_ONLY_FIELDS."""
|
|
90
90
|
|
|
91
|
+
# Exclusions
|
|
92
|
+
excluded_commands: frozenset
|
|
93
|
+
"""Commands not applicable to this format (e.g., project-sprint needs subagent team)."""
|
|
94
|
+
|
|
91
95
|
# Capabilities
|
|
92
96
|
has_custom_commands: bool
|
|
93
97
|
"""Whether the tool supports custom slash commands."""
|
|
@@ -121,6 +125,7 @@ FORMAT_PROFILES: dict[str, FormatProfile] = {
|
|
|
121
125
|
agent_format="md",
|
|
122
126
|
rules_import_style="@import",
|
|
123
127
|
excluded_agent_fields=frozenset(), # Classic: include all fields
|
|
128
|
+
excluded_commands=frozenset(), # Classic: all commands supported
|
|
124
129
|
has_custom_commands=True,
|
|
125
130
|
supports_model_routing=False,
|
|
126
131
|
supports_mcp=True,
|
|
@@ -141,6 +146,7 @@ FORMAT_PROFILES: dict[str, FormatProfile] = {
|
|
|
141
146
|
agent_format="md",
|
|
142
147
|
rules_import_style="instructions",
|
|
143
148
|
excluded_agent_fields=frozenset({"permissionMode", "memory", "skills"}),
|
|
149
|
+
excluded_commands=frozenset({"project-sprint"}),
|
|
144
150
|
has_custom_commands=True,
|
|
145
151
|
supports_model_routing=True,
|
|
146
152
|
supports_mcp=True,
|
|
@@ -161,6 +167,7 @@ FORMAT_PROFILES: dict[str, FormatProfile] = {
|
|
|
161
167
|
agent_format="md",
|
|
162
168
|
rules_import_style="inline",
|
|
163
169
|
excluded_agent_fields=frozenset({"permissionMode", "memory", "skills", "hooks"}),
|
|
170
|
+
excluded_commands=frozenset({"project-sprint"}),
|
|
164
171
|
has_custom_commands=True,
|
|
165
172
|
supports_model_routing=False,
|
|
166
173
|
supports_mcp=True,
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
# BUG-slim-007: W007 substring matching causes false negatives and inconsistency
|
|
2
|
-
|
|
3
|
-
| Field | Value |
|
|
4
|
-
|-------|-------|
|
|
5
|
-
| ID | BUG-slim-007 |
|
|
6
|
-
| Status | Draft |
|
|
7
|
-
| Priority | P1 |
|
|
8
|
-
| Release | 2.3.6 |
|
|
9
|
-
| Origin | Cross-model code review (2026-03-24) |
|
|
10
|
-
|
|
11
|
-
## Background
|
|
12
|
-
|
|
13
|
-
W007 (Req-AC coverage check) was introduced in STORY-slim-024 (v2.3.4) and patched in v2.3.5 to use `SPEC_RFC_KEYWORDS` instead of hardcoded `SHOULD`. Code review found that the implementation uses Python `in` (substring) matching in two places where word-boundary regex is required, plus a pattern divergence from the canonical source.
|
|
14
|
-
|
|
15
|
-
### Bug 1: R{N} coverage check — substring false negative
|
|
16
|
-
|
|
17
|
-
`spec_linter.py:241`:
|
|
18
|
-
```python
|
|
19
|
-
if req_id.lower() not in ac_lower:
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
When AC section mentions `R10`, the string `"r1"` is a substring of `"r10"` — so `R1` is falsely reported as covered. Similarly `"r1" in "r12"`, `"r2" in "r20"`, etc. Also non-R prefixed text like `"CR1"`, `"ERROR1"`, `"PR1"` would suppress W007 for `R1`.
|
|
23
|
-
|
|
24
|
-
**Impact**: Any spec with 10+ requirements silently passes W007 for R1-R9 when R10+ exists in AC. This directly undermines W007's purpose.
|
|
25
|
-
|
|
26
|
-
### Bug 2: RFC keyword detection — substring inconsistency with W003
|
|
27
|
-
|
|
28
|
-
`spec_linter.py:244`:
|
|
29
|
-
```python
|
|
30
|
-
found_keyword = next((kw for kw in SPEC_RFC_KEYWORDS if kw in req_upper), None)
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
W003 at line 172 uses `_RFC2119.search(body)` which is `SPEC_RFC_PATTERN` — a compiled regex with `\b` word boundaries. W007 uses plain `kw in req_upper` (substring). This means `"MAYONNAISE"` matches `MAY`, `"SHALLOW"` matches `SHALL`, etc.
|
|
34
|
-
|
|
35
|
-
Two rules in the same file detecting RFC 2119 keywords with different strategies is a consistency violation.
|
|
36
|
-
|
|
37
|
-
### Bug 3: _REQ_ID_PATTERN diverges from SPEC_REQUIREMENT_PATTERN
|
|
38
|
-
|
|
39
|
-
`spec_linter.py:203`:
|
|
40
|
-
```python
|
|
41
|
-
_REQ_ID_PATTERN = re.compile(r"###\s+(R\d+)[:\s]", re.MULTILINE)
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
`schemas.py:32`:
|
|
45
|
-
```python
|
|
46
|
-
SPEC_REQUIREMENT_PATTERN = r"### R\d+[:\s]"
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
E004 uses `SPEC_REQUIREMENT_PATTERN` (literal single space after `###`), while W007 uses `_REQ_ID_PATTERN` (`\s+` — allows tab, multiple spaces). A heading `### R1:` (double space) would be found by W007 but missed by E004. Both patterns should derive from the same canonical source.
|
|
50
|
-
|
|
51
|
-
## Requirements
|
|
52
|
-
|
|
53
|
-
### R1: Word-boundary matching for R{N} coverage (MUST)
|
|
54
|
-
|
|
55
|
-
W007 MUST use word-boundary regex to check whether an R{N} ID appears in the AC section. `R1` MUST NOT match `R10`, `R12`, `CR1`, `ERROR1`, or any other superstring.
|
|
56
|
-
|
|
57
|
-
Pattern: `re.search(rf"\b{req_id}\b", ac_section, re.IGNORECASE)`
|
|
58
|
-
|
|
59
|
-
### R2: Word-boundary matching for RFC 2119 keywords (MUST)
|
|
60
|
-
|
|
61
|
-
W007's RFC 2119 keyword detection MUST use `SPEC_RFC_PATTERN` (from `schemas.py`) — the same compiled regex with `\b` word boundaries that W003 already uses. `MAY` MUST NOT match `MAYONNAISE`.
|
|
62
|
-
|
|
63
|
-
### R3: _REQ_ID_PATTERN MUST derive from SPEC_REQUIREMENT_PATTERN (SHOULD)
|
|
64
|
-
|
|
65
|
-
`_REQ_ID_PATTERN` SHOULD be constructed from `SPEC_REQUIREMENT_PATTERN` or share the same whitespace semantics, so that E004 and W007 agree on what constitutes a valid `### R{N}` heading.
|
|
66
|
-
|
|
67
|
-
If intentional divergence is needed (e.g., W007 is more tolerant), document the reason inline.
|
|
68
|
-
|
|
69
|
-
### R4: Test coverage for multi-digit R{N} (MUST)
|
|
70
|
-
|
|
71
|
-
Tests MUST include a spec with R1 through R10+ where only R10 is referenced in AC, to verify R1 is correctly flagged as uncovered.
|
|
72
|
-
|
|
73
|
-
### R5: Test coverage for all 7 RFC 2119 keywords (SHOULD)
|
|
74
|
-
|
|
75
|
-
Tests SHOULD cover all 7 keywords from `SPEC_RFC_KEYWORDS`, not just MUST/SHOULD/MAY. Tests SHOULD also cover the no-keyword case (empty emphasis).
|
|
76
|
-
|
|
77
|
-
## Acceptance Criteria
|
|
78
|
-
|
|
79
|
-
### AC1: R1 not falsely covered by R10 (R1, R4)
|
|
80
|
-
|
|
81
|
-
- **Given** a spec with `### R1: First` and `### R10: Tenth` in Requirements
|
|
82
|
-
- **When** AC section mentions only `R10` (not `R1`)
|
|
83
|
-
- **Then** W007 fires for R1 with message "Requirement R1 has no corresponding AC reference"
|
|
84
|
-
- **And** W007 does NOT fire for R10
|
|
85
|
-
|
|
86
|
-
### AC2: Non-R prefix text does not suppress W007 (R1)
|
|
87
|
-
|
|
88
|
-
- **Given** a spec with `### R1: Something` in Requirements
|
|
89
|
-
- **When** AC section contains `ERROR1` or `CR1` but not standalone `R1`
|
|
90
|
-
- **Then** W007 fires for R1
|
|
91
|
-
|
|
92
|
-
### AC3: RFC keyword word-boundary (R2)
|
|
93
|
-
|
|
94
|
-
- **Given** a spec with unreferenced `### R1: The system MAYONNAISE handler`
|
|
95
|
-
- **When** spec linter runs
|
|
96
|
-
- **Then** W007 message does NOT contain `(MAY)` — substring `MAY` inside `MAYONNAISE` is not a match
|
|
97
|
-
- **And** emphasis is empty (no RFC keyword found)
|
|
98
|
-
|
|
99
|
-
### AC4: All 7 RFC keywords detected (R2, R5)
|
|
100
|
-
|
|
101
|
-
- **Given** specs with unreferenced requirements containing each of: MUST, SHOULD, MAY, SHALL, REQUIRED, RECOMMENDED, OPTIONAL
|
|
102
|
-
- **When** spec linter runs
|
|
103
|
-
- **Then** each W007 message contains the correct keyword in parentheses: `(MUST)`, `(SHOULD)`, etc.
|
|
104
|
-
|
|
105
|
-
### AC5: W003 and W007 use same detection mechanism (R2)
|
|
106
|
-
|
|
107
|
-
- **Given** the spec_linter.py source code
|
|
108
|
-
- **When** inspecting both W003 and W007 RFC 2119 detection
|
|
109
|
-
- **Then** both use `SPEC_RFC_PATTERN` (or `_RFC2119` alias) for matching — no plain `kw in string`
|
|
110
|
-
|
|
111
|
-
### AC6: Existing tests still pass (regression)
|
|
112
|
-
|
|
113
|
-
- **Given** all pre-existing tests in test_story_slim024.py and test_story042_spec_linter.py
|
|
114
|
-
- **When** running the full test suite
|
|
115
|
-
- **Then** all tests pass — no regressions from word-boundary change
|
|
116
|
-
|
|
117
|
-
## Implementation Steps
|
|
118
|
-
|
|
119
|
-
| Step | File | Action | Dependencies | Risk |
|
|
120
|
-
|------|------|--------|-------------|------|
|
|
121
|
-
| 1 | `src/pactkit/skills/spec_linter.py:241` | Replace `req_id.lower() not in ac_lower` with `re.search(rf"\b{re.escape(req_id)}\b", ac_section, re.IGNORECASE)` | None | Low |
|
|
122
|
-
| 2 | `src/pactkit/skills/spec_linter.py:244` | Replace `next((kw for kw in SPEC_RFC_KEYWORDS if kw in req_upper), None)` with `SPEC_RFC_PATTERN.search(req_body)` using match group | Step 1 | Low |
|
|
123
|
-
| 3 | `src/pactkit/skills/spec_linter.py:203` | Add inline comment documenting why `_REQ_ID_PATTERN` uses `\s+` vs `SPEC_REQUIREMENT_PATTERN`'s literal space, or unify | None | Low |
|
|
124
|
-
| 4 | `tests/unit/test_story_slim024.py` | Add R10 multi-digit test (AC1), non-R-prefix test (AC2), MAYONNAISE test (AC3), all-7-keywords test (AC4), no-keyword test (AC5 empty emphasis) | Steps 1-2 | Low |
|
|
125
|
-
| 5 | `tests/unit/test_story_slim024.py` | Migrate from `tempfile.NamedTemporaryFile(delete=False)` to pytest `tmp_path` fixture (consistency with test_story042) | None | Low |
|
|
126
|
-
|
|
127
|
-
## Security Scope
|
|
128
|
-
|
|
129
|
-
| Check | Applicable | Reason |
|
|
130
|
-
|-------|------------|--------|
|
|
131
|
-
| SEC-1~8 | No | Lint rule logic change, no I/O or auth |
|
|
132
|
-
|
|
133
|
-
## Out of Scope
|
|
134
|
-
|
|
135
|
-
- `_section_text()` returning heading line (fragile but extremely unlikely to cause false suppression)
|
|
136
|
-
- Sectional write rule 150 vs 300 inconsistency in `rules.py:359` (trivial fix, separate commit)
|
|
137
|
-
- `config.py:961` api_spec empty string default (docs already fixed, code default is cosmetic)
|
|
138
|
-
- Board archive section ordering (functional, not a bug)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|