tcw-cli 3.0.0__tar.gz → 3.0.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.
- {tcw_cli-3.0.0/tcw_cli.egg-info → tcw_cli-3.0.2}/PKG-INFO +3 -2
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/README.md +2 -1
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/pyproject.toml +1 -1
- tcw_cli-3.0.2/tcw/__init__.py +1 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/store/project.py +2 -2
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/jira/tickets.py +3 -1
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/procedures/unattended-work.md +1 -1
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/prompts/plan.md +8 -2
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/prompts/qa.md +10 -6
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/prompts/request.md +12 -2
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/prompts/spec.md +2 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/resolve.py +8 -2
- {tcw_cli-3.0.0 → tcw_cli-3.0.2/tcw_cli.egg-info}/PKG-INFO +3 -2
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_project_registry.py +5 -6
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_prompt_fallback.py +5 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_repo_lifecycle.py +1 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_shipped_prompts.py +47 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_store_provisioning.py +18 -18
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_upstream_projects.py +22 -22
- tcw_cli-3.0.0/tcw/__init__.py +0 -1
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/LICENSE +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/setup.cfg +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/capabilities/__init__.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/capabilities/cli.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/cli.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/cli_help.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/cli_suggest.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/config.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/config_cli.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/errors.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/exit.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/findings.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/output.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/projects_cli.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/refs.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/serve/__init__.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/serve/dist/client/assets/index-BGwvKkRc.js +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/serve/dist/client/assets/index-DkarCRfj.css +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/serve/dist/client/index.html +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/serve/dist/client/theme-init.js +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/serve/files.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/serve/records.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/serve/server.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/serve/services.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/serve/work.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/stdin.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/store/__init__.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/store/base.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/store/checkouts.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/store/config_edit.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/store/fs.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/store/yaml_source.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/taxonomy/__init__.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/taxonomy/cli.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/validate.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/__init__.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/advance.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/backend.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/cli.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/config.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/fs_backend.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/gates.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/generate.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/hooks.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/jira/__init__.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/jira/backend.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/jira/client.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/jira/config.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/jira/jql.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/jira/mapping.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/jira/remote.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/layout.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/model.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/open.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/passages.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/procedures/audit-backlog.md +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/procedures/create-work.md +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/procedures/documentation-sync.md +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/procedures/pause-work.md +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/procedures/search.md +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/procedures/triage-issues.md +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/prompts/implement.md +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/prompts/inbox.md +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/prompts/postmortem.md +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/prompts/review.md +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/record.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw/work/references.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw_cli.egg-info/SOURCES.txt +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw_cli.egg-info/dependency_links.txt +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw_cli.egg-info/entry_points.txt +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw_cli.egg-info/requires.txt +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tcw_cli.egg-info/top_level.txt +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_capabilities.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_capabilities_cli_contract.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_capabilities_federation.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_capabilities_reset.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_capabilities_rm.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_capability_ref_wording.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_check_versions.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_cli_contract.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_cli_guide.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_cli_handler.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_cli_help_standard.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_command_suggestions.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_committed_paths.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_config_edit.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_config_edit_writers.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_configuration_text_home.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_cut_version.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_doc_vocabulary.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_docs_describe_3_0.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_documentation_config.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_documentation_prompt.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_documentation_sync_wiring.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_documented_cli_surface.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_dynamic_skill_marker.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_environment_hardness.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_eval_coverage.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_eval_files_changed.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_eval_fixture.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_eval_grading.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_eval_runner.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_falsification_rule.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_findings_type.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_generate_hook.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_git_example.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_init_configured_tree_path.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_init_provision_contract.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_item_tags_read.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_ledger_3_0.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_legacy_store_config.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_lifecycle_config_tags.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_literal_store_names.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_migration_guide.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_multiproject.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_no_node.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_override_in_linked_worktree.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_override_letter_case.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_override_references.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_partial_removal.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_plugin_manifests.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_procedure_config.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_procedure_verb.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_project_graph_paths.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_project_overrides.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_projects_list.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_refs.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_remote_session_setup.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_require_artifact.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_routing_nodes.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_session_bootstrap.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_shipped_procedures.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_shipped_text.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_skill_flow.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_skill_path_pointers.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_smoke.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_stage_verb.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_static_guards.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_stdin.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_stdin_cli.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_store_bounds.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_store_editor.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_store_nodes.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_subprocess_stdin.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_suite_runs_its_own_tcw.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_taxonomy.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_taxonomy_cli_contract.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_taxonomy_rm_gaps.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_tree_store_files_only.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_tree_store_raises.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_unattended_work_skill.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_unpushed_version_script.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_upcoming_entries.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_validate.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_validate_command.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_validate_moved_stores.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_validate_target.py +0 -0
- {tcw_cli-3.0.0 → tcw_cli-3.0.2}/tests/test_worktree_sibling_nodes.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tcw-cli
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.2
|
|
4
4
|
Summary: Taxonomy · Capabilities · Work — a storage-abstracted framework for describing and evolving a software project.
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -724,7 +724,8 @@ A change to the product is followed from the first request to the records:
|
|
|
724
724
|
In filesystem mode, the agent working `qa` may record the verdict itself, unless
|
|
725
725
|
the project's own instructions for `qa` ask for a person to decide. When they
|
|
726
726
|
do, the agent still writes the round, recording the person's decision. In Jira
|
|
727
|
-
mode, a person accepts or rejects on the ticket
|
|
727
|
+
mode, a person accepts or rejects: on the ticket, or by telling the agent,
|
|
728
|
+
which then moves the ticket and records who decided.
|
|
728
729
|
|
|
729
730
|
Work reaches taxonomy through those capabilities: a new capability names the
|
|
730
731
|
vocabulary and feature it involves, and those entries must exist first.
|
|
@@ -508,7 +508,8 @@ A change to the product is followed from the first request to the records:
|
|
|
508
508
|
In filesystem mode, the agent working `qa` may record the verdict itself, unless
|
|
509
509
|
the project's own instructions for `qa` ask for a person to decide. When they
|
|
510
510
|
do, the agent still writes the round, recording the person's decision. In Jira
|
|
511
|
-
mode, a person accepts or rejects on the ticket
|
|
511
|
+
mode, a person accepts or rejects: on the ticket, or by telling the agent,
|
|
512
|
+
which then moves the ticket and records who decided.
|
|
512
513
|
|
|
513
514
|
Work reaches taxonomy through those capabilities: a new capability names the
|
|
514
515
|
vocabulary and feature it involves, and those entries must exist first.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tcw-cli"
|
|
7
|
-
version = "3.0.
|
|
7
|
+
version = "3.0.2"
|
|
8
8
|
description = "Taxonomy · Capabilities · Work — a storage-abstracted framework for describing and evolving a software project."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "3.0.2"
|
|
@@ -39,8 +39,8 @@ def validate_project_id(project_id: str) -> str:
|
|
|
39
39
|
def override_variable(project_id: str) -> str:
|
|
40
40
|
"""The environment variable that says where `project_id` lives on this machine.
|
|
41
41
|
|
|
42
|
-
The id, uppercased, with `-` as `_`: `
|
|
43
|
-
`
|
|
42
|
+
The id, uppercased, with `-` as `_`: `app-repo` →
|
|
43
|
+
`TCW_PROJECT_APP_REPO`. **Project ids, not repository names** — the two are
|
|
44
44
|
routinely different, and a workspace where they are crossed is what this
|
|
45
45
|
mapping is most often got wrong against.
|
|
46
46
|
|
|
@@ -392,7 +392,9 @@ def delegate_to_jira(project_id: str, title: str, request: str | None,
|
|
|
392
392
|
raise _named(BackendError(f"created {key} for {project_id}, but did not finish "
|
|
393
393
|
f"making it the item {slug}: {error}; finish with "
|
|
394
394
|
f"`tcw work tickets adopt {key}` there"), key) from None
|
|
395
|
-
report(f"the new files in {backend.work_path / slug.folder} are left uncommitted"
|
|
395
|
+
report(f"the new files in {backend.work_path / slug.folder} are left uncommitted; "
|
|
396
|
+
f"record them in that project's version control before filing into "
|
|
397
|
+
f"{project_id} again, or the next ticket stays in its Jira inbox")
|
|
396
398
|
return str(slug)
|
|
397
399
|
|
|
398
400
|
|
|
@@ -29,7 +29,7 @@ advisor alone.
|
|
|
29
29
|
|
|
30
30
|
| The stage asks for | Do this instead |
|
|
31
31
|
| --------------------------- | --------------- |
|
|
32
|
-
| What the requester wants, at request | Do not ask the advisors: what the requester wants is not theirs to know. Write the request from the evidence available, marking each assumption, as the request prompt says when there is no user to ask. Where the backend keeps the request, as Jira does, post your questions with `tcw work comment <slug>` instead and leave its text alone. Then
|
|
32
|
+
| What the requester wants, at request | Do not ask the advisors: what the requester wants is not theirs to know. Write the request from the evidence available, marking each assumption, as the request prompt says when there is no user to ask. Where the backend keeps the request, as Jira does, post your questions with `tcw work comment <slug>` instead and leave its text alone, and leave the item at request unless the user's own instruction for this run explicitly said to move it out of request. Then move on to other work. |
|
|
33
33
|
| One agent or several | Decide yourself. One, unless the parts are truly independent. |
|
|
34
34
|
| An open question in a spec or plan | Ask the advisors, then write the answer into the document with the assumption stated in the text. |
|
|
35
35
|
| Review | Run the review stage as its prompt says: a read-only reviewer, never the agent that implemented. Apply what you agree with; write down what you reject and why. |
|
|
@@ -36,8 +36,14 @@ tests cannot check.
|
|
|
36
36
|
5. Record dependencies between items as blockers, not prose:
|
|
37
37
|
`tcw work edit <slug> --blocked-by <ref>`.
|
|
38
38
|
6. **Too large for one item?** Split it here: `tcw work new "<title>" --parent
|
|
39
|
-
<slug>`
|
|
40
|
-
|
|
39
|
+
<slug>` per part, its request on stdin; this item is the parent. A part for
|
|
40
|
+
another project: `tcw work new "<title>" --project <id>` (no `--parent`). A
|
|
41
|
+
ticket key printed instead of a slug means no item exists there yet: report
|
|
42
|
+
it. Otherwise, from inside that project, set `--parent` (a Jira project
|
|
43
|
+
takes only a ticket on its own site) and `--blocked-by` with `tcw work edit`,
|
|
44
|
+
then record TCW's uncommitted changes in that project's version control
|
|
45
|
+
before filing there again: until then a filing is refused, or its Jira
|
|
46
|
+
ticket stays in the inbox.
|
|
41
47
|
7. **No placeholders.** "TBD", "add error handling", "as in step 3": a step
|
|
42
48
|
nobody can carry out without asking you a question is not a step.
|
|
43
49
|
8. **Self-review.** Re-read the plan against the spec: every acceptance
|
|
@@ -52,7 +52,7 @@ QA plan scenario as passed, failed or not run, with the evidence for each.
|
|
|
52
52
|
<!-- /backend -->
|
|
53
53
|
<!-- backend: jira -->
|
|
54
54
|
**Produce** findings, not a verdict. In Jira, qa happens on the ticket, and the
|
|
55
|
-
ticket's status, which
|
|
55
|
+
ticket's status, which a person decides, is the verdict. You may run the scenarios
|
|
56
56
|
and post what you found with `tcw work comment <slug>`.
|
|
57
57
|
|
|
58
58
|
## Steps
|
|
@@ -60,11 +60,15 @@ and post what you found with `tcw work comment <slug>`.
|
|
|
60
60
|
1. Run each scenario in the QA plan, in the environment it names.
|
|
61
61
|
2. Post one comment listing each scenario as passed, failed or not run, with
|
|
62
62
|
evidence. **No claim without output or an observation from just now.**
|
|
63
|
-
3.
|
|
64
|
-
|
|
65
|
-
`tcw work advance <slug> --to completed --reason "
|
|
66
|
-
`tcw work advance <slug> --to implement --reason
|
|
67
|
-
reason becomes the comment
|
|
63
|
+
3. A person decides the verdict, never you. Ask them for it. When the person
|
|
64
|
+
who decides gives you the verdict in this conversation, record it: accepted
|
|
65
|
+
with `tcw work advance <slug> --to completed --reason "accepted by <name>:
|
|
66
|
+
<why>"`, rejected with `tcw work advance <slug> --to implement --reason
|
|
67
|
+
"rejected by <name>: <what to fix>"`, whose reason becomes the comment
|
|
68
|
+
implement reads. A verdict comes after they have seen your findings: an
|
|
69
|
+
instruction given before that, such as "accept it if it passes", is not
|
|
70
|
+
one. They may instead move the ticket in Jira themselves. If they give no
|
|
71
|
+
verdict, stop at qa and say the item waits for them.
|
|
68
72
|
<!-- /backend -->
|
|
69
73
|
|
|
70
74
|
## Exit badly
|
|
@@ -32,10 +32,20 @@ Write it to `tcw work path <slug> request`. Keep any dated `## Added`
|
|
|
32
32
|
sections already there: they are amendments made after it was written.
|
|
33
33
|
<!-- /backend -->
|
|
34
34
|
<!-- backend: jira -->
|
|
35
|
-
The request is the ticket's description, and Jira owns it: never rewrite it
|
|
36
|
-
When a section is missing or unclear, post a comment with
|
|
35
|
+
The request is the ticket's description, and Jira owns it: never rewrite it on
|
|
36
|
+
your own judgment. When a section is missing or unclear, post a comment with
|
|
37
37
|
`tcw work comment <slug>` listing what is missing and proposing wording, for
|
|
38
38
|
the requester to adopt.
|
|
39
|
+
|
|
40
|
+
The requester decides when the request is ready. When they tell you to move
|
|
41
|
+
the item on, in this conversation, do it. If they adopted changed wording,
|
|
42
|
+
first put it into the ticket's description: write it yourself if your harness
|
|
43
|
+
can edit Jira tickets, or ask them to paste it in. Change only what they
|
|
44
|
+
adopted, and keep everything else in the description. Then read it back and
|
|
45
|
+
confirm the description now says what they adopted. Then run
|
|
46
|
+
`tcw work advance <slug> --reason "<who told you to move it>"`. A question,
|
|
47
|
+
such as whether accepting the wording moves the item, is not an instruction to
|
|
48
|
+
move it.
|
|
39
49
|
<!-- /backend -->
|
|
40
50
|
|
|
41
51
|
## Steps
|
|
@@ -51,6 +51,8 @@ it to `tcw work path <slug> spec --handoff`.
|
|
|
51
51
|
|
|
52
52
|
- _The code contradicts the request._ Stop and return to request. A spec that
|
|
53
53
|
quietly reinterprets the request is worse than one that refuses.
|
|
54
|
+
- _The requester revises the request._ It stays at spec: record it as an
|
|
55
|
+
amendment (a dated `## Added`; in Jira, an `Added` comment), and follow it.
|
|
54
56
|
- _The work is too large for one item._ Say so; plan decomposes it.
|
|
55
57
|
- _You cannot ground a claim._ Mark it as an assumption, not a fact.
|
|
56
58
|
- _`tcw work comment` failed._ Report the command and its exit code, do not
|
|
@@ -450,8 +450,14 @@ def stage_footer(row, external, target: str) -> str | None:
|
|
|
450
450
|
if row.artifact == NO_ARTIFACT:
|
|
451
451
|
return None # how an arrival becomes work differs by backend
|
|
452
452
|
if row.name in external:
|
|
453
|
-
|
|
454
|
-
|
|
453
|
+
# A person decides; the agent carries out the move once told (TCW-100).
|
|
454
|
+
move = (f"`tcw work advance {target} --to <stage> --reason \"<who decided, "
|
|
455
|
+
f"and why>\"`" if row.verdict else
|
|
456
|
+
f"`tcw work advance {target} --reason \"<who told you>\"`")
|
|
457
|
+
text = ("> **When this stage's work is done, a person decides whether the "
|
|
458
|
+
"item moves on.** Move it only when that person tells you to, in "
|
|
459
|
+
"this conversation; a question or an implied yes is not that. "
|
|
460
|
+
f"Then run {move}.")
|
|
455
461
|
if row.verdict:
|
|
456
462
|
text += " The verdict is that move."
|
|
457
463
|
return text
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tcw-cli
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.2
|
|
4
4
|
Summary: Taxonomy · Capabilities · Work — a storage-abstracted framework for describing and evolving a software project.
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -724,7 +724,8 @@ A change to the product is followed from the first request to the records:
|
|
|
724
724
|
In filesystem mode, the agent working `qa` may record the verdict itself, unless
|
|
725
725
|
the project's own instructions for `qa` ask for a person to decide. When they
|
|
726
726
|
do, the agent still writes the round, recording the person's decision. In Jira
|
|
727
|
-
mode, a person accepts or rejects on the ticket
|
|
727
|
+
mode, a person accepts or rejects: on the ticket, or by telling the agent,
|
|
728
|
+
which then moves the ticket and records who decided.
|
|
728
729
|
|
|
729
730
|
Work reaches taxonomy through those capabilities: a new capability names the
|
|
730
731
|
vocabulary and feature it involves, and those entries must exist first.
|
|
@@ -66,13 +66,12 @@ def test_the_override_variable_name_follows_the_id():
|
|
|
66
66
|
"""The id, uppercased with `-` as `_` — node ids, never repository names.
|
|
67
67
|
|
|
68
68
|
The distinction costs an hour when the two are crossed, which they are in
|
|
69
|
-
the workspace that prompted this: the directory `
|
|
70
|
-
|
|
71
|
-
node `proposit-app-repo`.
|
|
69
|
+
the workspace that prompted this: the directory `workspace` holds the
|
|
70
|
+
node `product`, and the directory `app` holds the node `app-repo`.
|
|
72
71
|
"""
|
|
73
|
-
assert override_variable("
|
|
74
|
-
assert override_variable("
|
|
75
|
-
assert override_variable("
|
|
72
|
+
assert override_variable("app-repo") == "TCW_PROJECT_APP_REPO"
|
|
73
|
+
assert override_variable("product") == "TCW_PROJECT_PRODUCT"
|
|
74
|
+
assert override_variable("app-repo-web") == "TCW_PROJECT_APP_REPO_WEB"
|
|
76
75
|
assert override_variable("a") == "TCW_PROJECT_A"
|
|
77
76
|
|
|
78
77
|
|
|
@@ -21,6 +21,11 @@ re-baselining to hide that is what this file exists to prevent.
|
|
|
21
21
|
**Re-captured again by TCW-76**, for one paragraph only: TCW-74's review fixes
|
|
22
22
|
(`e411e681`) added "Who reviews" to the review prompt after the capture, and
|
|
23
23
|
the recorded bytes were never brought up to it. Nothing else moved.
|
|
24
|
+
|
|
25
|
+
**Re-captured again by TCW-101**, for two passages it added on purpose: the
|
|
26
|
+
spec prompt's "The requester revises the request" bullet, and the plan
|
|
27
|
+
prompt's step 6 on filing a part into another project. A diff of the old and
|
|
28
|
+
new bytes showed nothing else moved.
|
|
24
29
|
"""
|
|
25
30
|
|
|
26
31
|
import json
|
|
@@ -47,6 +47,7 @@ DOCUMENTATION = {
|
|
|
47
47
|
("docs/changelogs/upcoming/<folder>.md", "Any-Code-Change"),
|
|
48
48
|
("skills/<component>/SKILL.md", "Skill-Driven-Component"),
|
|
49
49
|
("skills/configure/references/<document>.md", "Configuration-Key-Change"),
|
|
50
|
+
("docs/use-cases.md", "Project-Setup-Change"),
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
# Programs a `command:` hook runs a script with; the script is then its
|
|
@@ -267,6 +267,26 @@ def test_jira_qa_records_no_verdict():
|
|
|
267
267
|
assert "tcw work path <slug> qa" not in text and "verdict:" not in text
|
|
268
268
|
|
|
269
269
|
|
|
270
|
+
def test_jira_qa_moves_the_ticket_when_the_person_gives_the_verdict():
|
|
271
|
+
"""TCW-100: the person decides; once they give the verdict in the
|
|
272
|
+
conversation, the agent records it with `advance`, and otherwise stops."""
|
|
273
|
+
paragraph = _paragraph_with(prompt("qa", "jira"), "gives you the verdict")
|
|
274
|
+
assert "--to completed --reason" in paragraph
|
|
275
|
+
assert "--to implement --reason" in paragraph
|
|
276
|
+
assert "after they have seen your findings" in paragraph
|
|
277
|
+
assert "stop at qa" in paragraph
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def test_jira_request_writes_adopted_wording_before_moving():
|
|
281
|
+
"""TCW-100: adopted wording goes into the description, is read back, and
|
|
282
|
+
only then is the item moved, with a reason naming who said to move it."""
|
|
283
|
+
paragraph = _paragraph_with(prompt("request", "jira"), "tell you to move")
|
|
284
|
+
for words in ("description", "paste", "read it back",
|
|
285
|
+
"tcw work advance <slug> --reason"):
|
|
286
|
+
assert words in paragraph, words
|
|
287
|
+
assert paragraph.index("read it back") < paragraph.index("tcw work advance")
|
|
288
|
+
|
|
289
|
+
|
|
270
290
|
@pytest.mark.parametrize("sid", sorted(SHIPPED))
|
|
271
291
|
def test_no_prompt_tells_the_agent_to_ask_what_it_is_reading(sid):
|
|
272
292
|
"""`tcw work lifecycle --stage <id>` inside the text that command reports
|
|
@@ -291,3 +311,30 @@ def test_the_self_review_pass_appears_in_exactly_three_prompts():
|
|
|
291
311
|
prompts = load_builtins().stage_prompts
|
|
292
312
|
found = {sid for sid, text in prompts.items() if "self-review" in text.lower()}
|
|
293
313
|
assert found == {"spec", "plan", "implement"}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def test_jira_request_keeps_what_the_requester_did_not_change():
|
|
317
|
+
"""TCW-101: adopted wording changes only what was adopted."""
|
|
318
|
+
paragraph = _paragraph_with(prompt("request", "jira"), "tell you to move")
|
|
319
|
+
assert "keep everything else in the description" in paragraph
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
@pytest.mark.parametrize("backend", BACKENDS)
|
|
323
|
+
def test_spec_says_what_to_do_when_the_requester_revises_the_request(backend):
|
|
324
|
+
"""TCW-101: the item stays at spec, and the change is recorded where the
|
|
325
|
+
request lives, in either backend's form."""
|
|
326
|
+
paragraph = _paragraph_with(prompt("spec", backend), "The requester revises")
|
|
327
|
+
for words in ("stays at spec", "amendment", "## Added", "`Added` comment"):
|
|
328
|
+
assert words in paragraph, words
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def test_plan_says_how_to_file_a_part_into_another_project():
|
|
332
|
+
"""TCW-101: --project takes no --parent; the parent is set from inside
|
|
333
|
+
that project, and only then is everything recorded in its version
|
|
334
|
+
control, because the edit itself writes a file there; a ticket key in
|
|
335
|
+
place of a slug means there is no item to edit."""
|
|
336
|
+
paragraph = _paragraph_with(prompt("plan", "filesystem"), "Too large for one item")
|
|
337
|
+
order = [paragraph.index(w) for w in ("--project", "tcw work edit",
|
|
338
|
+
"version control")]
|
|
339
|
+
assert order == sorted(order), paragraph
|
|
340
|
+
assert "ticket key printed instead of a slug" in paragraph
|
|
@@ -320,25 +320,25 @@ def test_the_cache_key_is_unchanged():
|
|
|
320
320
|
exactly what a shared normalizer would change.
|
|
321
321
|
"""
|
|
322
322
|
expected = {
|
|
323
|
-
("https://github.com/
|
|
324
|
-
"github.com-
|
|
325
|
-
("https://github.com/
|
|
326
|
-
"github.com-
|
|
327
|
-
("git@github.com:
|
|
328
|
-
"github.com-
|
|
329
|
-
("https://github.com/
|
|
330
|
-
"github.com-
|
|
323
|
+
("https://github.com/Example-Org/workspace.git", "main"):
|
|
324
|
+
"github.com-example-org-workspace-ec7917e6e4c3",
|
|
325
|
+
("https://github.com/Example-Org/workspace", "main"):
|
|
326
|
+
"github.com-example-org-workspace-f86dcb6399fe",
|
|
327
|
+
("git@github.com:Example-Org/workspace.git", "main"):
|
|
328
|
+
"github.com-example-org-workspace-1a37f0559bb2",
|
|
329
|
+
("https://github.com/Example-Org/workspace.git", "dev"):
|
|
330
|
+
"github.com-example-org-workspace-c6c86f756591",
|
|
331
331
|
}
|
|
332
332
|
for (url, ref), key in expected.items():
|
|
333
333
|
assert checkouts._cache_key(RepositoryDeclaration(url=url, ref=ref)) == key
|
|
334
334
|
|
|
335
335
|
|
|
336
336
|
@pytest.mark.parametrize("spelling", [
|
|
337
|
-
"https://github.com/
|
|
338
|
-
"https://github.com/
|
|
339
|
-
"https://github.com/
|
|
340
|
-
" https://github.com/
|
|
341
|
-
"git@github.com:
|
|
337
|
+
"https://github.com/Example-Org/workspace",
|
|
338
|
+
"https://github.com/Example-Org/workspace.git",
|
|
339
|
+
"https://github.com/Example-Org/workspace/",
|
|
340
|
+
" https://github.com/Example-Org/workspace.git ",
|
|
341
|
+
"git@github.com:Example-Org/workspace.git",
|
|
342
342
|
])
|
|
343
343
|
def test_two_spellings_of_one_repository_normalize_alike(spelling):
|
|
344
344
|
"""The question `_cache_key` deliberately answers the other way.
|
|
@@ -348,18 +348,18 @@ def test_two_spellings_of_one_repository_normalize_alike(spelling):
|
|
|
348
348
|
new resolution rung has to see through that.
|
|
349
349
|
"""
|
|
350
350
|
canonical = checkouts.normalized_url(
|
|
351
|
-
"https://github.com/
|
|
351
|
+
"https://github.com/Example-Org/workspace")
|
|
352
352
|
assert checkouts.normalized_url(spelling) == canonical
|
|
353
353
|
|
|
354
354
|
|
|
355
355
|
@pytest.mark.parametrize("other", [
|
|
356
|
-
"https://github.com/
|
|
357
|
-
"https://github.com/Someone-Else/
|
|
358
|
-
"https://gitlab.invalid/
|
|
356
|
+
"https://github.com/Example-Org/core",
|
|
357
|
+
"https://github.com/Someone-Else/workspace",
|
|
358
|
+
"https://gitlab.invalid/Example-Org/workspace",
|
|
359
359
|
])
|
|
360
360
|
def test_two_different_repositories_do_not_normalize_alike(other):
|
|
361
361
|
assert checkouts.normalized_url(other) != checkouts.normalized_url(
|
|
362
|
-
"https://github.com/
|
|
362
|
+
"https://github.com/Example-Org/workspace")
|
|
363
363
|
|
|
364
364
|
|
|
365
365
|
def test_an_undeclared_component_is_a_no_op(tmp_path):
|
|
@@ -90,7 +90,7 @@ def test_the_upstream_does_not_see_its_reader(tmp_path):
|
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
def test_an_upstream_reached_through_a_parent_is_found(tmp_path):
|
|
93
|
-
"""The
|
|
93
|
+
"""The app-repository shape: a package reaches core through its parent."""
|
|
94
94
|
config(tmp_path / "repo", "id: repo\nprojects:\n children:\n"
|
|
95
95
|
" pkg: pkg\n upstream:\n core: ../core\n")
|
|
96
96
|
config(tmp_path / "repo" / "pkg", "id: pkg\nprojects:\n parent:\n"
|
|
@@ -545,7 +545,7 @@ def test_an_incomplete_tracker_with_a_parent_does_not(tmp_path):
|
|
|
545
545
|
assert "required" in text and _NO_PARENT not in text, text
|
|
546
546
|
|
|
547
547
|
|
|
548
|
-
# ── the
|
|
548
|
+
# ── the workspace shape, end to end ──────────────────────────────────────────
|
|
549
549
|
|
|
550
550
|
_ROOT_TRACKER = {
|
|
551
551
|
"provider": "jira-cloud",
|
|
@@ -561,33 +561,33 @@ def _edit(node: Path, change) -> None:
|
|
|
561
561
|
(node / "tcw-config.yaml").write_text(yaml.safe_dump(cfg, sort_keys=False))
|
|
562
562
|
|
|
563
563
|
|
|
564
|
-
def
|
|
565
|
-
"""The shape the request came from: an orchestration root `
|
|
566
|
-
children `
|
|
564
|
+
def _workspace(tmp_path: Path) -> dict[str, Path]:
|
|
565
|
+
"""The shape the request came from: an orchestration root `product` with
|
|
566
|
+
children `core` and `app-repo`; a package `shared` below the
|
|
567
567
|
app repository extending core's taxonomy; core's tracker block holding only
|
|
568
568
|
`candidate-query` and inheriting the rest from the root."""
|
|
569
569
|
root = tmp_path / "ws"
|
|
570
|
-
nodes = {"root": root, "core": root / "
|
|
571
|
-
"app": root / "
|
|
572
|
-
"shared": root / "
|
|
573
|
-
for key, pid in (("root", "
|
|
574
|
-
("app", "
|
|
570
|
+
nodes = {"root": root, "core": root / "core",
|
|
571
|
+
"app": root / "app",
|
|
572
|
+
"shared": root / "app" / "packages" / "shared"}
|
|
573
|
+
for key, pid in (("root", "product"), ("core", "core"),
|
|
574
|
+
("app", "app-repo"), ("shared", "shared")):
|
|
575
575
|
nodes[key].mkdir(parents=True)
|
|
576
576
|
if key != "shared": # three repositories, as in the workspace
|
|
577
577
|
_git(nodes[key], "init", "-q", "-b", "main")
|
|
578
578
|
init(["taxonomy", "capabilities", "work"], nodes[key], pid)
|
|
579
579
|
_edit(root, lambda c: c.update({"projects": {"children": {
|
|
580
|
-
"
|
|
580
|
+
"core": "core", "app-repo": "app"}}}))
|
|
581
581
|
_set_tracker(root, dict(_ROOT_TRACKER))
|
|
582
582
|
_edit(nodes["core"], lambda c: c.update({"projects": {
|
|
583
|
-
"parent": {"
|
|
583
|
+
"parent": {"product": ".."}}}))
|
|
584
584
|
_set_tracker(nodes["core"], {"candidate-query": "project = CORE"})
|
|
585
585
|
_edit(nodes["app"], lambda c: c.update({"projects": {
|
|
586
|
-
"parent": {"
|
|
586
|
+
"parent": {"product": ".."}, "children": {"shared": "packages/shared"}}}))
|
|
587
587
|
_edit(nodes["shared"], lambda c: c.update({"projects": {
|
|
588
|
-
"parent": {"
|
|
588
|
+
"parent": {"app-repo": "../.."}}}))
|
|
589
589
|
assert _tcw(nodes["core"], "taxonomy", "add", "Argument").returncode == 0
|
|
590
|
-
out = _tcw(nodes["shared"], "taxonomy", "extends", "add", "
|
|
590
|
+
out = _tcw(nodes["shared"], "taxonomy", "extends", "add", "core")
|
|
591
591
|
assert out.returncode == 0, out.stderr
|
|
592
592
|
return nodes
|
|
593
593
|
|
|
@@ -605,16 +605,16 @@ def _assert_state(nodes, *, warned: set[str]) -> None:
|
|
|
605
605
|
assert code == 0, (key, text)
|
|
606
606
|
has_warning = "read as an upstream project" in text
|
|
607
607
|
assert has_warning == (key in warned), (key, text)
|
|
608
|
-
out = _tcw(nodes["shared"], "taxonomy", "show", "
|
|
608
|
+
out = _tcw(nodes["shared"], "taxonomy", "show", "core/argument")
|
|
609
609
|
assert out.returncode == 0, out.stderr
|
|
610
610
|
|
|
611
611
|
|
|
612
|
-
def
|
|
613
|
-
nodes =
|
|
612
|
+
def test_the_workspace_migration_passes_through_no_blocked_state(tmp_path):
|
|
613
|
+
nodes = _workspace(tmp_path)
|
|
614
614
|
_assert_state(nodes, warned=set())
|
|
615
615
|
|
|
616
616
|
step_2 = lambda c: c["projects"].update( # noqa: E731
|
|
617
|
-
{"upstream": {"
|
|
617
|
+
{"upstream": {"core": "../core"}})
|
|
618
618
|
|
|
619
619
|
# The forbidden middle state: step 2 before step 1.
|
|
620
620
|
_edit(nodes["app"], step_2)
|
|
@@ -624,8 +624,8 @@ def test_the_proposit_migration_passes_through_no_blocked_state(tmp_path):
|
|
|
624
624
|
|
|
625
625
|
# Step 1: the root moves core from children to upstream, one edit.
|
|
626
626
|
def step_1(c):
|
|
627
|
-
c["projects"]["children"].pop("
|
|
628
|
-
c["projects"]["upstream"] = {"
|
|
627
|
+
c["projects"]["children"].pop("core")
|
|
628
|
+
c["projects"]["upstream"] = {"core": "core"}
|
|
629
629
|
_edit(nodes["root"], step_1)
|
|
630
630
|
_assert_state(nodes, warned={"root", "core", "app", "shared"})
|
|
631
631
|
|
|
@@ -642,7 +642,7 @@ def test_the_proposit_migration_passes_through_no_blocked_state(tmp_path):
|
|
|
642
642
|
_edit(nodes["core"], lambda c: c["work"].pop("tracker"))
|
|
643
643
|
_assert_state(nodes, warned=set())
|
|
644
644
|
out = _tcw(nodes["core"], "validate")
|
|
645
|
-
assert "
|
|
645
|
+
assert "product" not in out.stdout + out.stderr, out.stdout
|
|
646
646
|
|
|
647
647
|
|
|
648
648
|
# ── the CLI's view of an absent upstream, and of what lies beyond one ────────
|
tcw_cli-3.0.0/tcw/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "3.0.0"
|
|
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
|
|
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
|