tuner-testkit 4.2.0__tar.gz → 4.3.0__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.
- {tuner_testkit-4.2.0/tuner_testkit.egg-info → tuner_testkit-4.3.0}/PKG-INFO +4 -4
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/README.md +3 -3
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/pyproject.toml +2 -1
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/__init__.py +1 -1
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_test/__init__.py +8 -2
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_test/model.py +27 -0
- tuner_testkit-4.3.0/tuner_testkit/apps/dna/dna_payload/.cursor/agents/sut-source-to-design.md +33 -0
- tuner_testkit-4.3.0/tuner_testkit/apps/dna/dna_payload/.cursor/skills/analyze-mcp-network/SKILL.md +30 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/derive-design-knowledge/SKILL.md +5 -4
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/explore-sut/SKILL.md +14 -4
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/freeze-api-objects/SKILL.md +3 -2
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/run-feature-playwright-mcp/SKILL.md +2 -1
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/MANIFEST.txt +1 -1
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/docs/spec/api-objects-syntax.md +3 -1
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/docs/spec/design-knowledge-syntax.md +15 -5
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/docs/spec/sut-self-learning.md +4 -4
- tuner_testkit-4.3.0/tuner_testkit/apps/evidence/__init__.py +5 -0
- tuner_testkit-4.3.0/tuner_testkit/apps/evidence/__main__.py +4 -0
- tuner_testkit-4.3.0/tuner_testkit/apps/evidence/cli.py +89 -0
- tuner_testkit-4.3.0/tuner_testkit/apps/evidence/persist.py +274 -0
- tuner_testkit-4.3.0/tuner_testkit/apps/evidence/tests/test_cli.py +26 -0
- tuner_testkit-4.3.0/tuner_testkit/apps/evidence/tests/test_persist.py +45 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/scaffold.py +9 -2
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/config/env.py +5 -1
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/tests/test_scaffold.py +2 -0
- tuner_testkit-4.3.0/tuner_testkit/apps/recorder/tests/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/config.py +3 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/driver.py +2 -1
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0/tuner_testkit.egg-info}/PKG-INFO +4 -4
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit.egg-info/SOURCES.txt +7 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit.egg-info/entry_points.txt +1 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/LICENSE +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/setup.cfg +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/__main__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/_internal/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/_internal/api.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/_internal/db.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/_internal/generators.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/_internal/ids.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/_internal/wait.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/base.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/context.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/plane.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/action_words/registry.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_mock/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_mock/app.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_mock/errors.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_mock/router.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_mock/spec.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_mock/store.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_objects/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_objects/auth.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_objects/recording/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_objects/recording/capture.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_objects/recording/codegen.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_objects/recording/freeze.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_objects/recording/mocks.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_objects/recording/normalize.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_objects/recording/pipeline.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_objects/recording/sanitize.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_test/client.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_test/errors.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/api_test/jsonpath.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/_shared/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/_shared/changelog.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/_shared/plane.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/_shared/plane_app.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/_shared/plane_asset.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/_shared/tests/test_plane_asset.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/_shared/tests/test_plane_collect.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/api_recorder/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/api_recorder/__main__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/api_recorder/addon.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/api_recorder/cli.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/api_recorder/playwright_tap.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/api_recorder/tests/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/api_recorder/tests/test_addon.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/__main__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/build.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/cli.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/agents/bdd-asset-pipeline.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/hooks/sync_registry.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/hooks.json +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/apps-authoring.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/apps-handover.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/assets-knowledge.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/bdd-asset-layering.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/bdd-pipeline-gates.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/bdd-stage-layout.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/commit-convention.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/explore-safety.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/index-hygiene.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/packages-db.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/packages-fake.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/packages-logging.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/rules/packages-page-test.mdc +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/create-action-word/SKILL.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/create-app/SKILL.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/derive-feature-sets/SKILL.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/dump-ddl/SKILL.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/feature-authoring/SKILL.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/feature-review-lint/SKILL.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/maintain-behave-steps/SKILL.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/maintain-index/SKILL.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/maintain-page-objects/SKILL.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/release-template/SKILL.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/.cursor/skills/reuse-analysis/SKILL.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/AGENTS.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/docs/spec/action-words-syntax.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/docs/spec/apps-authoring-syntax.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/docs/spec/assets-knowledge-syntax.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/docs/spec/bdd-run-gherkin-in-playwrightMCP.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/docs/spec/behave-gerkin-syntax.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/docs/spec/behave-step-definitions.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/docs/spec/commit-convention.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/docs/spec/page-objects-syntax.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/tools/git-hooks/README.md +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/tools/git-hooks/commit-msg +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/tools/git-hooks/pre-push +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/dna_payload/tools/git-hooks/validate_commit_msg.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/paths.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/source.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dna/tests/test_cli.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/dump_ddl.py +0 -0
- {tuner_testkit-4.2.0/tuner_testkit/apps/page_recorder → tuner_testkit-4.3.0/tuner_testkit/apps/evidence}/tests/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/index_ai/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/index_ai/__main__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/index_ai/registry.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/index_platform/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/index_platform/__main__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/index_platform/build.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/index_platform/catalog.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/index_platform/plane.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/index_platform/tests/test_catalog.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/__main__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/manifest.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/apps/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/behave.ini +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/action_words/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/action_words/_internal/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/action_words/_internal/params.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/action_words/models.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/api_objects/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/api_objects/auth.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/api_objects/plane.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/api_objects/recording/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/api_objects/registry.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/page_objects/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/page_objects/plane.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/init_repo/stubs/packages/page_objects/session.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/mock_server/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/mock_server/__main__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/mock_server/cli.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/mock_server/plane.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/mock_server/seed.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/page_recorder/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/page_recorder/__main__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/page_recorder/capture.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/page_recorder/cli.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/page_recorder/codegen.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/page_recorder/freeze.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/page_recorder/inject.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/page_recorder/session.py +0 -0
- {tuner_testkit-4.2.0/tuner_testkit/apps/recorder → tuner_testkit-4.3.0/tuner_testkit/apps/page_recorder}/tests/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/page_recorder/tests/test_capture.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/page_recorder/tests/test_cli.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/page_recorder/tests/test_freeze.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/recorder/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/recorder/__main__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/recorder/cli.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/apps/recorder/tests/test_cli.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/db/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/db/client.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/db/connection.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/excel/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/excel/workbook.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/fake/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/fake/__main__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/fake/catalog.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/fake/china.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/fake/cli.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/fake/core.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/fake/medical.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/fake/wrappers.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/logging/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/logging/context.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/logging/core.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/logging/helpers.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/logging/hooks.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/logging/scenario_file.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/__init__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/__main__.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/base.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/errors.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/harvest.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/health.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/locator.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/model.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/registry.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/session.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/steps.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/page_test/testing.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit/project.py +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit.egg-info/dependency_links.txt +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit.egg-info/requires.txt +0 -0
- {tuner_testkit-4.2.0 → tuner_testkit-4.3.0}/tuner_testkit.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tuner-testkit
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.3.0
|
|
4
4
|
Summary: Shared test runtime, recorders, and DNA overlay for SUT test repos
|
|
5
5
|
Author: tuner-testkit contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -138,10 +138,10 @@ uv tool upgrade tuner-testkit
|
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
140
|
# 钉到某一发行版(推荐,和团队 lock 一致)
|
|
141
|
-
uv add "tuner-testkit[db,api]==4.
|
|
141
|
+
uv add "tuner-testkit[db,api]==4.3.0"
|
|
142
142
|
|
|
143
143
|
# 或只抬下限,让解析器选允许范围内的最新
|
|
144
|
-
# uv add "tuner-testkit[db,api]>=4.
|
|
144
|
+
# uv add "tuner-testkit[db,api]>=4.3.0"
|
|
145
145
|
|
|
146
146
|
uv sync
|
|
147
147
|
```
|
|
@@ -210,4 +210,4 @@ uv sync --extra all
|
|
|
210
210
|
|
|
211
211
|
`uv sync --extra dev` 与 `--extra all` 等价(`dev` 只是引用 `[all]`)。
|
|
212
212
|
|
|
213
|
-
4.
|
|
213
|
+
4.3.0 发行说明见 [`docs/changelog/TEMPLATE-4.3.0.md`](docs/changelog/TEMPLATE-4.3.0.md);4.2.0 见 [`TEMPLATE-4.2.0.md`](docs/changelog/TEMPLATE-4.2.0.md);3.x → 4.x 迁移见 [`TEMPLATE-4.0.0.md`](docs/changelog/TEMPLATE-4.0.0.md)。平台能力地图见 [`INDEX.md`](INDEX.md)。
|
|
@@ -93,10 +93,10 @@ uv tool upgrade tuner-testkit
|
|
|
93
93
|
|
|
94
94
|
```bash
|
|
95
95
|
# 钉到某一发行版(推荐,和团队 lock 一致)
|
|
96
|
-
uv add "tuner-testkit[db,api]==4.
|
|
96
|
+
uv add "tuner-testkit[db,api]==4.3.0"
|
|
97
97
|
|
|
98
98
|
# 或只抬下限,让解析器选允许范围内的最新
|
|
99
|
-
# uv add "tuner-testkit[db,api]>=4.
|
|
99
|
+
# uv add "tuner-testkit[db,api]>=4.3.0"
|
|
100
100
|
|
|
101
101
|
uv sync
|
|
102
102
|
```
|
|
@@ -165,4 +165,4 @@ uv sync --extra all
|
|
|
165
165
|
|
|
166
166
|
`uv sync --extra dev` 与 `--extra all` 等价(`dev` 只是引用 `[all]`)。
|
|
167
167
|
|
|
168
|
-
4.
|
|
168
|
+
4.3.0 发行说明见 [`docs/changelog/TEMPLATE-4.3.0.md`](docs/changelog/TEMPLATE-4.3.0.md);4.2.0 见 [`TEMPLATE-4.2.0.md`](docs/changelog/TEMPLATE-4.2.0.md);3.x → 4.x 迁移见 [`TEMPLATE-4.0.0.md`](docs/changelog/TEMPLATE-4.0.0.md)。平台能力地图见 [`INDEX.md`](INDEX.md)。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tuner-testkit"
|
|
3
|
-
version = "4.
|
|
3
|
+
version = "4.3.0"
|
|
4
4
|
description = "Shared test runtime, recorders, and DNA overlay for SUT test repos"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -73,6 +73,7 @@ tuner-index-platform = "tuner_testkit.apps.index_platform.__main__:main"
|
|
|
73
73
|
tuner-fake = "tuner_testkit.fake.cli:main"
|
|
74
74
|
tuner-page-test = "tuner_testkit.page_test.__main__:main"
|
|
75
75
|
tuner-action-words = "tuner_testkit.action_words.__main__:main"
|
|
76
|
+
tuner-evidence = "tuner_testkit.apps.evidence.cli:main"
|
|
76
77
|
|
|
77
78
|
[build-system]
|
|
78
79
|
requires = ["setuptools>=77", "wheel"]
|
|
@@ -6,7 +6,13 @@ Primary entrypoints:
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
from .client import ApiClient
|
|
9
|
-
from .model import
|
|
9
|
+
from .model import (
|
|
10
|
+
APIModel,
|
|
11
|
+
ApiResponse,
|
|
12
|
+
AssertOperation,
|
|
13
|
+
ExtractVariableOperation,
|
|
14
|
+
interpolate_path,
|
|
15
|
+
)
|
|
10
16
|
|
|
11
17
|
__all__ = [
|
|
12
18
|
"APIModel",
|
|
@@ -14,5 +20,5 @@ __all__ = [
|
|
|
14
20
|
"AssertOperation",
|
|
15
21
|
"ExtractVariableOperation",
|
|
16
22
|
"ApiClient",
|
|
23
|
+
"interpolate_path",
|
|
17
24
|
]
|
|
18
|
-
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import re
|
|
3
4
|
from dataclasses import dataclass, field, replace
|
|
4
5
|
from typing import Any, Dict, Iterable, List, Mapping, MutableMapping, Optional
|
|
5
6
|
|
|
@@ -102,6 +103,22 @@ def _required_file_fields(schema: Mapping[str, Any]) -> list[str]:
|
|
|
102
103
|
return out
|
|
103
104
|
|
|
104
105
|
|
|
106
|
+
_PATH_PLACEHOLDER_RE = re.compile(r"\{([A-Za-z_][A-Za-z0-9_]*)\}")
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def interpolate_path(path: str, params: Mapping[str, Any]) -> str:
|
|
110
|
+
"""Replace ``{name}`` segments. Raises if any named placeholder remains."""
|
|
111
|
+
out = path
|
|
112
|
+
for key, value in params.items():
|
|
113
|
+
out = out.replace("{" + str(key) + "}", str(value))
|
|
114
|
+
leftover = _PATH_PLACEHOLDER_RE.findall(out)
|
|
115
|
+
if leftover:
|
|
116
|
+
raise SchemaValidationError(
|
|
117
|
+
f"path still has placeholders {leftover}; pass them via set_path"
|
|
118
|
+
)
|
|
119
|
+
return out
|
|
120
|
+
|
|
121
|
+
|
|
105
122
|
@dataclass(frozen=True)
|
|
106
123
|
class APIModel:
|
|
107
124
|
# Identity
|
|
@@ -156,6 +173,9 @@ class APIModel:
|
|
|
156
173
|
def set_headers(self, values: Optional[Mapping[str, Any]] = None) -> "APIInvocation":
|
|
157
174
|
return APIInvocation(self).set_headers(values)
|
|
158
175
|
|
|
176
|
+
def set_path(self, values: Optional[Mapping[str, Any]] = None) -> "APIInvocation":
|
|
177
|
+
return APIInvocation(self).set_path(values)
|
|
178
|
+
|
|
159
179
|
def override_query(self, values: Optional[Mapping[str, Any]] = None) -> "APIInvocation":
|
|
160
180
|
return APIInvocation(self).override_query(values)
|
|
161
181
|
|
|
@@ -181,6 +201,7 @@ class APIInvocation:
|
|
|
181
201
|
_set_json: Dict[str, Any] = field(default_factory=dict)
|
|
182
202
|
_set_files: Dict[str, Any] = field(default_factory=dict)
|
|
183
203
|
_set_headers: Dict[str, Any] = field(default_factory=dict)
|
|
204
|
+
_path_params: Dict[str, Any] = field(default_factory=dict)
|
|
184
205
|
_override_query: Optional[Dict[str, Any]] = None
|
|
185
206
|
_override_json: Any = None
|
|
186
207
|
_override_json_set: bool = False
|
|
@@ -237,6 +258,10 @@ class APIInvocation:
|
|
|
237
258
|
checked = _enforce_headers_policy(headers=values, allowlist=allowlist, forbidden=forbidden)
|
|
238
259
|
return replace(self, _set_headers=_merge_shallow(self._set_headers, checked))
|
|
239
260
|
|
|
261
|
+
def set_path(self, values: Optional[Mapping[str, Any]] = None) -> "APIInvocation":
|
|
262
|
+
values = {str(k): v for k, v in dict(values or {}).items()}
|
|
263
|
+
return replace(self, _path_params=_merge_shallow(self._path_params, values))
|
|
264
|
+
|
|
240
265
|
def override_query(self, values: Optional[Mapping[str, Any]] = None) -> "APIInvocation":
|
|
241
266
|
values = dict(values or {})
|
|
242
267
|
# override still must respect schema; allow autofill via set_query if needed
|
|
@@ -308,6 +333,8 @@ class APIInvocation:
|
|
|
308
333
|
from .client import ApiClient # local import to avoid cycles
|
|
309
334
|
|
|
310
335
|
m = self._effective_model()
|
|
336
|
+
if self._path_params:
|
|
337
|
+
m = replace(m, path=interpolate_path(m.path, self._path_params))
|
|
311
338
|
use_client = client or m._client or ApiClient.default()
|
|
312
339
|
files = self._final_files()
|
|
313
340
|
self._validate_required_files(m, files)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sut-source-to-design
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Walk SUT backend source (Django models/views/serializers, or equivalent) plus assets/ddl to draft assets/design/<app>/<route-slug>.md. Use when the engineer points at a source tree. Do not invent tables. Complements derive-design-knowledge; this agent is the source-channel playbook.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# SUT source → design knowledge
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Highest-quality `writes` / `noise` / `reads` often come from **models.save / views**, not from MCP bodies. Plane dogfood: `Issue.save` showed `issue_sequences` + `_ensure_default_state` before any DB diff existed.
|
|
12
|
+
|
|
13
|
+
## Inputs
|
|
14
|
+
|
|
15
|
+
- Source root the user named (e.g. `C:\dev\repo\plane\apps\api`)
|
|
16
|
+
- Matching `assets/ddl/<datasource>/` if present (dump missing tables rather than hallucinate)
|
|
17
|
+
- Optional `artifacts/evidence/<run_id>/` to fill `api_object_ref` / `page_ref`
|
|
18
|
+
|
|
19
|
+
## Workflow
|
|
20
|
+
|
|
21
|
+
1. Locate the route: urls.py / router → view/serializer.
|
|
22
|
+
2. Locate the model `.save()` / service function for that write.
|
|
23
|
+
3. List INSERT/UPDATE/DELETE tables. Audit / `*_activities` / operation_log → `noise`.
|
|
24
|
+
4. If a table is named in source but missing from DDL, run `tuner-dump-ddl <table> --datasource …` then continue. Do not skip the table.
|
|
25
|
+
5. Write one `assets/design/<app>/<route-slug>.md` per **route** (not per button) using `docs/spec/design-knowledge-syntax.md` v0.1.
|
|
26
|
+
6. Record schema gaps under `## notes` (conditional writes, defaulted FKs, dual identity). Do **not** invent new top-level keys yet.
|
|
27
|
+
|
|
28
|
+
## Hard rules
|
|
29
|
+
|
|
30
|
+
- Table/column names must appear in DDL or source. No guessed identifiers.
|
|
31
|
+
- `confidence: inferred` for source-only; `observed` only with DB/log evidence.
|
|
32
|
+
- No secrets.
|
|
33
|
+
- Do not generate action words here.
|
tuner_testkit-4.3.0/tuner_testkit/apps/dna/dna_payload/.cursor/skills/analyze-mcp-network/SKILL.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: analyze-mcp-network
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Turn a persisted artifacts/evidence/<run_id>/network.jsonl into a unique method+path route table (JSON) for freeze-api-objects. Prefer tuner-evidence routes (deterministic). Use after explore-sut or run-feature-playwright-mcp. Do not invent routes that are not in the JSONL.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Analyze MCP network evidence
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
- **Primary goal**: list freeze candidates from on-disk evidence.
|
|
12
|
+
- **Write scope**: none (read-only). Optionally paste the JSON into the freeze handoff.
|
|
13
|
+
- **Must follow**: `docs/spec/bdd-run-gherkin-in-playwrightMCP.md` sanitizer — if the JSONL still contains Cookie/Authorization, stop and re-run `tuner-evidence persist`.
|
|
14
|
+
|
|
15
|
+
## Steps
|
|
16
|
+
|
|
17
|
+
1. Confirm `artifacts/evidence/<run_id>/network.jsonl` exists.
|
|
18
|
+
2. Run (stdout is JSON):
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uv run python -m tuner_testkit.apps.evidence routes --run-id <run_id>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
3. Hand the `routes[]` list to `freeze-api-objects`. Skip static/HTML. Skip `POST /api/sign-in/` (password body) unless the user explicitly wants a login API asset **without** persisting credentials.
|
|
25
|
+
4. For slug paths that `normalize_path` left as literals (e.g. `/api/workspaces/tuner/...`), freeze with `{workspace_slug}` and tell callers to `set_path`.
|
|
26
|
+
|
|
27
|
+
## Hard rules
|
|
28
|
+
|
|
29
|
+
- Do not re-fetch live traffic here.
|
|
30
|
+
- Do not write api_objects in this skill.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: derive-design-knowledge
|
|
3
|
-
version: 0.
|
|
3
|
+
version: 0.2.0
|
|
4
4
|
description: Compile assets/design/<app>/<route-slug>.md from open evidence channels (MCP network, DDL, SUT source, optional logs). Schema is docs/spec/design-knowledge-syntax.md v0.1 (draft). Use after an explore session or API freeze when DB effects must ground assertions. Do not invent tables that are not in DDL or source.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -19,9 +19,10 @@ description: Compile assets/design/<app>/<route-slug>.md from open evidence chan
|
|
|
19
19
|
2. For each captured **non-static mutating** route (POST/PUT/PATCH/DELETE):
|
|
20
20
|
- Draft front-matter (`confidence`: `observed` only if DB/log proved it; else `inferred`).
|
|
21
21
|
- Fill `writes` / `noise` / `reads` from the **highest quality channel available**:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
1. source (models/views/serializers) — see `.cursor/agents/sut-source-to-design.md`
|
|
23
|
+
2. SQL logs / DB diff
|
|
24
|
+
3. inference from response body + DDL names
|
|
25
|
+
- If source names a table that is not in `assets/ddl`, dump it (`tuner-dump-ddl`) before writing `writes`.
|
|
25
26
|
- Skip `operation_log` / `*_activities` / audit tables as `noise` unless the user says they are the assertion target.
|
|
26
27
|
3. Every `writes` row must cite evidence or a source path in `## notes`.
|
|
27
28
|
4. If the v0.1 schema cannot express something (multi-API button, external side effects), write it under `## notes` and do **not** invent new top-level keys yet — the draft must be stress-tested first.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: explore-sut
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.1.0
|
|
4
4
|
description: Explore-first Playwright MCP session against a live SUT. Produces sanitized artifacts/evidence/<run_id>/ plus assets/explore capability notes. Use when learning a web SUT, mapping pages/buttons, or gathering evidence before any .feature exists. Do NOT use when the user already has a feature to implement — use run-feature-playwright-mcp instead.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -25,7 +25,17 @@ description: Explore-first Playwright MCP session against a live SUT. Produces s
|
|
|
25
25
|
1. Read `INDEX.md` and `INDEX.project.md` if present. Note base URLs from `config`.
|
|
26
26
|
2. Pick a **vertical slice** (e.g. workspace → project → issue), not a full-site crawl.
|
|
27
27
|
3. Navigate with Playwright MCP. After each meaningful action, dump network + snapshot.
|
|
28
|
-
4. Persist:
|
|
28
|
+
4. Persist with **`tuner-evidence persist`** (do not leave captures only in chat):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
uv run python -m tuner_testkit.apps.evidence persist \
|
|
32
|
+
--run-id YYYYMMDDTHHMMSSZ-<slice> \
|
|
33
|
+
--scenario explore:<intent> \
|
|
34
|
+
--network captures.jsonl \
|
|
35
|
+
--intent "short vertical-slice intent"
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Manual fallback (same files):
|
|
29
39
|
- `artifacts/evidence/<run_id>/run_summary.md`
|
|
30
40
|
- `artifacts/evidence/<run_id>/network.jsonl` (sanitized)
|
|
31
41
|
- optional `actions.jsonl` / `snapshots/`
|
|
@@ -35,11 +45,11 @@ description: Explore-first Playwright MCP session against a live SUT. Produces s
|
|
|
35
45
|
- primary actions (button labels)
|
|
36
46
|
- APIs observed (method + path only)
|
|
37
47
|
- blockers / i18n / env notes
|
|
38
|
-
6. Stop.
|
|
48
|
+
6. Stop. Next: `tuner-evidence routes --run-id …` then freeze / derive-design / author feature.
|
|
39
49
|
|
|
40
50
|
## Output checklist
|
|
41
51
|
|
|
42
52
|
- `run_id` and evidence directory exist.
|
|
43
53
|
- Sanitizer applied statement in `run_summary.md`.
|
|
44
54
|
- Explore note cites `evidence`.
|
|
45
|
-
- Next-step
|
|
55
|
+
- Next-step: `tuner-evidence routes` then freeze / derive-design / author feature.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: freeze-api-objects
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.1.0
|
|
4
4
|
description: Freezes sanitized Playwright MCP network captures into route-aligned, deduped API Objects under packages/api_objects/ following api-objects-syntax.md and bdd-run-gherkin-in-playwrightMCP.md. Use when creating/updating APIModel assets.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -72,4 +72,5 @@ description: Freezes sanitized Playwright MCP network captures into route-aligne
|
|
|
72
72
|
- No forbidden header keys or raw tokens in file content.
|
|
73
73
|
- No host in APIModel path/url.
|
|
74
74
|
- No scenario-specific constants asserted in APIModel.
|
|
75
|
-
-
|
|
75
|
+
- Skip `POST /api/sign-in/` (and password-bearing login routes) unless explicitly required; never persist the password body.
|
|
76
|
+
- If the path contains a workspace/org **slug**, freeze `{workspace_slug}` (not the live slug) and document `set_path`..
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: run-feature-playwright-mcp
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.2.0
|
|
4
4
|
description: Runs behave Gherkin scenarios (or records an explore session) via Playwright MCP with Run→Capture→Freeze workflow and strict sanitization. Evidence always lands under artifacts/evidence/<run_id>/. Do NOT require this skill when the user explicitly requests pytest-only work with no MCP capture need.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -49,6 +49,7 @@ description: Runs behave Gherkin scenarios (or records an explore session) via P
|
|
|
49
49
|
- sanitizer applied statement
|
|
50
50
|
- **`artifacts/evidence/<run_id>/network.jsonl`** (required, sanitized):
|
|
51
51
|
- one capture per request (see spec minimum fields)
|
|
52
|
+
- Prefer writing it via `tuner-evidence persist --network captures.jsonl` so the sanitizer is deterministic.
|
|
52
53
|
- **`artifacts/evidence/<run_id>/actions.jsonl`** / `snapshots/` (optional)
|
|
53
54
|
|
|
54
55
|
## Verification checklist (must include in result)
|
|
@@ -56,7 +56,9 @@ APIModel 的字段被分为三类:**Identity**、**Contract**、**Operations**
|
|
|
56
56
|
- `name`: string(可中文)
|
|
57
57
|
- `description`: string(允许 Markdown,建议结构化)
|
|
58
58
|
- `method`: string(GET/POST/PUT/DELETE/…)
|
|
59
|
-
- `path`: string(只允许 path,不允许写 host
|
|
59
|
+
- `path`: string(只允许 path,不允许写 host)。动态段写成 `{workspace_slug}` /
|
|
60
|
+
`{project_id}` / `{uuid}` 等;调用时用 **`set_path({...})`** 替换(4.3.0)。
|
|
61
|
+
`normalize_path` 只自动识别 uuid 与纯数字,**slug 不会被工具改写**,冻结时请手写占位名。
|
|
60
62
|
|
|
61
63
|
### 2) Contract(弱 schema 也可,但必须可约束)
|
|
62
64
|
|
|
@@ -94,9 +94,19 @@ page_ref:
|
|
|
94
94
|
标成 `observed`。
|
|
95
95
|
- 更新文档必须 bump `version`。
|
|
96
96
|
|
|
97
|
-
## 6. 实践待决(dogfood 回来改)
|
|
97
|
+
## 6. 实践待决(Plane dogfood 2026-09-13 回来改)
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
已用 workspace→project→issue 切片压过 v0.1。**本版不升 schema**,只把缺口记在这里,待积累第二条切片再升 v0.2。
|
|
100
|
+
|
|
101
|
+
| 缺口 | 实践 |
|
|
102
|
+
| --- | --- |
|
|
103
|
+
| 按路由还是按用户动作成文? | **继续按路由**。本切片 Save=1 POST;登录是多条 PATCH。动作视图可写在 `assets/explore`,design 保持一路由一文。 |
|
|
104
|
+
| `page_ref` 多个 trigger | 需要。工具栏 `Add work item` 与侧栏 `New work item` 打同一 POST。v0.2 考虑 `triggers: []`。 |
|
|
105
|
+
| `writes` 的 `when:` | 需要。`issue_assignees` 仅当 `assignee_ids` 非空。 |
|
|
106
|
+
| 默认值 / 空 body → DB 有值 | `state_id=""` → `_ensure_default_state`。`correlate` 表达不了。 |
|
|
107
|
+
| 业务 identity 双键 | UUID `id` + UI `TUNER-{sequence_id}`。考虑 `business_identity`。 |
|
|
108
|
+
| slug 占位 | `normalize_path` 不替换非 uuid slug;冻结时手写 `{workspace_slug}`,运行时 `set_path`。 |
|
|
109
|
+
| `externals:` | 本切片未遇到(无邮件/S3)。仍待决。 |
|
|
110
|
+
| 源码表先于 DDL | 允许:先从 models 写 design,再 `tuner-dump-ddl` 补表。 |
|
|
111
|
+
|
|
112
|
+
样本:下游 dogfood `assets/design/web/post-api-workspaces-projects-issues.md`。
|
|
@@ -82,10 +82,10 @@ artifacts/evidence/<run_id>/ ← 统一落盘(强制)
|
|
|
82
82
|
|
|
83
83
|
## 5. 分阶段路线图
|
|
84
84
|
|
|
85
|
-
- **阶段 0
|
|
86
|
-
- **阶段 1
|
|
87
|
-
- **阶段 2 design 闭环**:`derive-design-knowledge`
|
|
88
|
-
- **阶段 3 双路汇合**:`derive-feature-sets`;bdd-asset-pipeline
|
|
85
|
+
- **阶段 0(4.2.0)**:契约先行——本文件 + design schema v0.1 + gates/路径/分诊树纠偏。
|
|
86
|
+
- **阶段 1 证据落盘(4.3.0)**:`tuner-evidence persist|routes`;`explore-sut` 强制走 CLI;PageModel `url_path` 占位插值;API `set_path`。
|
|
87
|
+
- **阶段 2 design 闭环**:`derive-design-knowledge` + `sut-source-to-design` agent;schema 仍为 v0.1(Plane 切片缺口记在 design spec §6)。
|
|
88
|
+
- **阶段 3 双路汇合**:`derive-feature-sets`;bdd-asset-pipeline 下半程。
|
|
89
89
|
- **阶段 4 知识自治**:knowledge-curator;确定性 `secret-scan` hook。
|
|
90
90
|
|
|
91
91
|
内循环用 `uv tool install --from <platform-repo>`;阶段收口才打 tag 发 PyPI。
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""CLI: persist / list sanitized MCP evidence under artifacts/evidence/<run_id>/.
|
|
2
|
+
|
|
3
|
+
python -m tuner_testkit.apps.evidence persist --run-id ID --scenario explore:x --network captures.jsonl
|
|
4
|
+
python -m tuner_testkit.apps.evidence routes --run-id ID
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import json
|
|
11
|
+
import sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from tuner_testkit.logging import log_error
|
|
15
|
+
from tuner_testkit.project import ensure_project_on_path, project_root
|
|
16
|
+
|
|
17
|
+
from .persist import load_jsonl, load_run, persist
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
21
|
+
parser = argparse.ArgumentParser(
|
|
22
|
+
prog="python -m tuner_testkit.apps.evidence",
|
|
23
|
+
description=(
|
|
24
|
+
"Sanitize Playwright MCP / network dumps and persist them under "
|
|
25
|
+
"artifacts/evidence/<run_id>/ as JSON (LLM-consumable)."
|
|
26
|
+
),
|
|
27
|
+
)
|
|
28
|
+
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
29
|
+
|
|
30
|
+
persist_p = sub.add_parser("persist", help="write sanitized evidence for one run")
|
|
31
|
+
persist_p.add_argument("--run-id", required=True, help="ASCII run id, e.g. 20260913T134137Z-issue-slice")
|
|
32
|
+
persist_p.add_argument("--scenario", required=True, help="scenario_id or explore:<intent>")
|
|
33
|
+
persist_p.add_argument("--network", required=True, type=Path, help="JSONL of captures (one object per line)")
|
|
34
|
+
persist_p.add_argument("--actions", type=Path, default=None, help="optional actions JSONL")
|
|
35
|
+
persist_p.add_argument("--intent", default="", help="short intent blurb for generated run_summary.md")
|
|
36
|
+
persist_p.add_argument("--summary", type=Path, default=None, help="optional run_summary.md to copy")
|
|
37
|
+
persist_p.add_argument(
|
|
38
|
+
"--root",
|
|
39
|
+
type=Path,
|
|
40
|
+
default=None,
|
|
41
|
+
help="project root (default: tuner project_root())",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
routes_p = sub.add_parser("routes", help="print unique method+path from an existing run")
|
|
45
|
+
routes_p.add_argument("--run-id", required=True)
|
|
46
|
+
routes_p.add_argument("--root", type=Path, default=None)
|
|
47
|
+
return parser
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _print_json(payload: object) -> None:
|
|
51
|
+
sys.stdout.write(json.dumps(payload, ensure_ascii=False, default=str))
|
|
52
|
+
sys.stdout.write("\n")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def main(argv: list[str] | None = None) -> int:
|
|
56
|
+
if hasattr(sys.stdout, "reconfigure"):
|
|
57
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
58
|
+
ns = build_parser().parse_args(argv)
|
|
59
|
+
root = ns.root
|
|
60
|
+
if root is None:
|
|
61
|
+
ensure_project_on_path()
|
|
62
|
+
root = project_root()
|
|
63
|
+
try:
|
|
64
|
+
if ns.cmd == "persist":
|
|
65
|
+
captures = load_jsonl(ns.network)
|
|
66
|
+
actions = load_jsonl(ns.actions) if ns.actions else None
|
|
67
|
+
summary = ns.summary.read_text(encoding="utf-8") if ns.summary else None
|
|
68
|
+
payload = persist(
|
|
69
|
+
run_id=ns.run_id,
|
|
70
|
+
scenario_id=ns.scenario,
|
|
71
|
+
captures=captures,
|
|
72
|
+
actions=actions,
|
|
73
|
+
intent=ns.intent,
|
|
74
|
+
summary_text=summary,
|
|
75
|
+
root=root,
|
|
76
|
+
)
|
|
77
|
+
_print_json(payload)
|
|
78
|
+
return 0
|
|
79
|
+
if ns.cmd == "routes":
|
|
80
|
+
_print_json(load_run(ns.run_id, root=root))
|
|
81
|
+
return 0
|
|
82
|
+
except (OSError, ValueError, json.JSONDecodeError) as exc:
|
|
83
|
+
log_error("evidence cli failed", error=f"{type(exc).__name__}: {exc}")
|
|
84
|
+
return 1
|
|
85
|
+
return 2
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
if __name__ == "__main__":
|
|
89
|
+
raise SystemExit(main())
|