tcw-cli 3.0.1__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.1/tcw_cli.egg-info → tcw_cli-3.0.2}/PKG-INFO +1 -1
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/pyproject.toml +1 -1
- tcw_cli-3.0.2/tcw/__init__.py +1 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/store/project.py +2 -2
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/jira/tickets.py +3 -1
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/prompts/plan.md +8 -2
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/prompts/request.md +2 -1
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/prompts/spec.md +2 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2/tcw_cli.egg-info}/PKG-INFO +1 -1
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_project_registry.py +5 -6
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_prompt_fallback.py +5 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_repo_lifecycle.py +1 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_shipped_prompts.py +27 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_store_provisioning.py +18 -18
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_upstream_projects.py +22 -22
- tcw_cli-3.0.1/tcw/__init__.py +0 -1
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/LICENSE +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/README.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/setup.cfg +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/capabilities/__init__.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/capabilities/cli.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/cli.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/cli_help.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/cli_suggest.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/config.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/config_cli.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/errors.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/exit.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/findings.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/output.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/projects_cli.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/refs.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/serve/__init__.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/serve/dist/client/assets/index-BGwvKkRc.js +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/serve/dist/client/assets/index-DkarCRfj.css +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/serve/dist/client/index.html +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/serve/dist/client/theme-init.js +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/serve/files.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/serve/records.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/serve/server.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/serve/services.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/serve/work.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/stdin.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/store/__init__.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/store/base.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/store/checkouts.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/store/config_edit.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/store/fs.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/store/yaml_source.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/taxonomy/__init__.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/taxonomy/cli.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/validate.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/__init__.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/advance.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/backend.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/cli.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/config.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/fs_backend.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/gates.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/generate.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/hooks.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/jira/__init__.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/jira/backend.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/jira/client.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/jira/config.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/jira/jql.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/jira/mapping.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/jira/remote.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/layout.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/model.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/open.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/passages.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/procedures/audit-backlog.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/procedures/create-work.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/procedures/documentation-sync.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/procedures/pause-work.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/procedures/search.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/procedures/triage-issues.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/procedures/unattended-work.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/prompts/implement.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/prompts/inbox.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/prompts/postmortem.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/prompts/qa.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/prompts/review.md +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/record.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/references.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw/work/resolve.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw_cli.egg-info/SOURCES.txt +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw_cli.egg-info/dependency_links.txt +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw_cli.egg-info/entry_points.txt +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw_cli.egg-info/requires.txt +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tcw_cli.egg-info/top_level.txt +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_capabilities.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_capabilities_cli_contract.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_capabilities_federation.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_capabilities_reset.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_capabilities_rm.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_capability_ref_wording.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_check_versions.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_cli_contract.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_cli_guide.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_cli_handler.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_cli_help_standard.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_command_suggestions.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_committed_paths.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_config_edit.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_config_edit_writers.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_configuration_text_home.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_cut_version.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_doc_vocabulary.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_docs_describe_3_0.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_documentation_config.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_documentation_prompt.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_documentation_sync_wiring.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_documented_cli_surface.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_dynamic_skill_marker.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_environment_hardness.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_eval_coverage.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_eval_files_changed.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_eval_fixture.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_eval_grading.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_eval_runner.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_falsification_rule.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_findings_type.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_generate_hook.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_git_example.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_init_configured_tree_path.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_init_provision_contract.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_item_tags_read.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_ledger_3_0.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_legacy_store_config.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_lifecycle_config_tags.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_literal_store_names.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_migration_guide.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_multiproject.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_no_node.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_override_in_linked_worktree.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_override_letter_case.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_override_references.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_partial_removal.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_plugin_manifests.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_procedure_config.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_procedure_verb.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_project_graph_paths.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_project_overrides.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_projects_list.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_refs.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_remote_session_setup.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_require_artifact.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_routing_nodes.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_session_bootstrap.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_shipped_procedures.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_shipped_text.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_skill_flow.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_skill_path_pointers.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_smoke.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_stage_verb.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_static_guards.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_stdin.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_stdin_cli.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_store_bounds.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_store_editor.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_store_nodes.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_subprocess_stdin.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_suite_runs_its_own_tcw.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_taxonomy.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_taxonomy_cli_contract.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_taxonomy_rm_gaps.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_tree_store_files_only.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_tree_store_raises.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_unattended_work_skill.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_unpushed_version_script.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_upcoming_entries.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_validate.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_validate_command.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_validate_moved_stores.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_validate_target.py +0 -0
- {tcw_cli-3.0.1 → tcw_cli-3.0.2}/tests/test_worktree_sibling_nodes.py +0 -0
|
@@ -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
|
|
|
@@ -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
|
|
@@ -40,7 +40,8 @@ the requester to adopt.
|
|
|
40
40
|
The requester decides when the request is ready. When they tell you to move
|
|
41
41
|
the item on, in this conversation, do it. If they adopted changed wording,
|
|
42
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.
|
|
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
|
|
44
45
|
confirm the description now says what they adopted. Then run
|
|
45
46
|
`tcw work advance <slug> --reason "<who told you to move it>"`. A question,
|
|
46
47
|
such as whether accepting the wording moves the item, is not an instruction to
|
|
@@ -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
|
|
@@ -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
|
|
@@ -311,3 +311,30 @@ def test_the_self_review_pass_appears_in_exactly_three_prompts():
|
|
|
311
311
|
prompts = load_builtins().stage_prompts
|
|
312
312
|
found = {sid for sid, text in prompts.items() if "self-review" in text.lower()}
|
|
313
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.1/tcw/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "3.0.1"
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|