ticketwright 2.0.0__tar.gz → 3.2.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.
- ticketwright-3.2.0/.claude/commands/.gitkeep +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/config/stack.example.asana-bq.yaml +3 -1
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/config/stack.example.azure.yaml +3 -2
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/config/stack.schema.md +2 -1
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/config/stack.yaml +3 -2
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/hooks/regenerate_ticket_index.py +23 -3
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/hooks/session_context.py +0 -2
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/hooks/ticket_index_context.py +5 -1
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/settings.json.tmpl +1 -1
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/productize/SKILL.md +2 -2
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/productize/authoring.md +1 -1
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/review/SKILL.md +7 -2
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/setup/SKILL.md +12 -4
- ticketwright-3.2.0/.claude/skills/setup/scaffold.md +80 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/ship/SKILL.md +7 -3
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/spec-and-build/SKILL.md +4 -2
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/ticket/SKILL.md +8 -3
- {ticketwright-2.0.0 → ticketwright-3.2.0}/CHANGELOG.md +103 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/PKG-INFO +38 -6
- {ticketwright-2.0.0 → ticketwright-3.2.0}/README.md +37 -5
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/chat/slack.md +9 -9
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/chat/teams.md +5 -5
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/docstore/gdrive.md +3 -3
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/docstore/sharepoint.md +1 -1
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/tracker/asana.md +4 -4
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/tracker/azure-devops.md +2 -2
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/tracker/jira.md +17 -13
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/tracker/linear.md +4 -4
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/tracker/monday.md +4 -4
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/warehouse/bigquery.md +6 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/warehouse/databricks.md +1 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/warehouse/snowflake.md +6 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/warehouse/synapse.md +5 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/bin/build_ticket_index.py +91 -2
- {ticketwright-2.0.0 → ticketwright-3.2.0}/bin/enrich_ticket.py +7 -2
- {ticketwright-2.0.0 → ticketwright-3.2.0}/bin/selftest.sh +185 -12
- {ticketwright-2.0.0 → ticketwright-3.2.0}/bin/verify_stack.sh +12 -3
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/AGENTS.md.tmpl +14 -2
- ticketwright-3.2.0/templates/CLAUDE.md.tmpl +1 -0
- ticketwright-3.2.0/templates/gitignore.tmpl +46 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/ticketwright/__init__.py +1 -1
- ticketwright-2.0.0/.claude/commands/build-context-pack.md +0 -8
- ticketwright-2.0.0/.claude/commands/build-ticket-index.md +0 -8
- ticketwright-2.0.0/.claude/commands/configure-workspace.md +0 -8
- ticketwright-2.0.0/.claude/commands/deliver-ticket.md +0 -8
- ticketwright-2.0.0/.claude/commands/onboard-teammate.md +0 -8
- ticketwright-2.0.0/.claude/commands/prime-domain.md +0 -8
- ticketwright-2.0.0/.claude/commands/prime-ticket.md +0 -8
- ticketwright-2.0.0/.claude/commands/prime-warehouse.md +0 -8
- ticketwright-2.0.0/.claude/commands/productize-workflow.md +0 -8
- ticketwright-2.0.0/.claude/commands/qc-review.md +0 -8
- ticketwright-2.0.0/.claude/commands/recall.md +0 -8
- ticketwright-2.0.0/.claude/commands/start-ticket.md +0 -8
- ticketwright-2.0.0/.claude/skills/setup/scaffold.md +0 -38
- ticketwright-2.0.0/templates/gitignore.tmpl +0 -35
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/agents/qc-reviewer.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/hooks/db_write_guard.py +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/refresh/SKILL.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/refresh/context-pack.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/refresh/index.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/setup/adopt.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/setup/teammate.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/skills/ticket/priming.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.claude/statusline.sh +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/.gitignore +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/LICENSE +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/README.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/tracker/github-issues.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/vcs/azure-repos.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/vcs/github.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/vcs/gitlab.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/warehouse/postgres.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/adapters/warehouse/redshift.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/bin/ingest_index_records.py +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/bin/recall.py +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/bin/render.sh +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/bin/render_and_validate.sh +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/bin/split_and_export.sh +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/pyproject.toml +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/plan.md.tmpl +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/productized-skill/SKILL.md.tmpl +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/productized-skill/bin/drift_check.sh +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/productized-skill/golden/example.json +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/productized-skill/sql/qc.sql.tmpl +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/productized-skill/sql/step.sql.tmpl +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/productized-skill/templates/README.md.tmpl +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/productized-skill/templates/tracker_comment.txt.tmpl +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/roles/analyst.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/roles/engineer.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/roles/generalist.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/roles/scientist.md +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/spec.md.tmpl +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/templates/ticket-README.md.tmpl +0 -0
- {ticketwright-2.0.0 → ticketwright-3.2.0}/ticketwright/cli.py +0 -0
|
File without changes
|
|
@@ -17,6 +17,7 @@ seams:
|
|
|
17
17
|
tool: asana
|
|
18
18
|
adapter: adapters/tracker/asana.md
|
|
19
19
|
transport: mcp
|
|
20
|
+
mcp: asana # your Asana MCP server name (adapter calls resolve as mcp__{mcp}__…)
|
|
20
21
|
workspace_gid: "1122334455"
|
|
21
22
|
default_project_gid: "9988776655"
|
|
22
23
|
verify: null # MCP-only
|
|
@@ -34,6 +35,7 @@ seams:
|
|
|
34
35
|
tool: teams
|
|
35
36
|
adapter: adapters/chat/teams.md
|
|
36
37
|
transport: mcp
|
|
38
|
+
mcp: teams # your Teams MCP server name (adapter calls resolve as mcp__{mcp}__…)
|
|
37
39
|
channel: "Data Team / General"
|
|
38
40
|
default_mode: draft
|
|
39
41
|
always_include: [Ana]
|
|
@@ -60,7 +62,7 @@ policies:
|
|
|
60
62
|
db_write_requires_approval: true
|
|
61
63
|
chat_default_draft: true
|
|
62
64
|
hyperlink_everything: true
|
|
63
|
-
|
|
65
|
+
skillify_everything: true
|
|
64
66
|
reduce_assumptions: true
|
|
65
67
|
commit_plan_before_implement: true
|
|
66
68
|
system_evolution: true
|
|
@@ -22,7 +22,7 @@ seams:
|
|
|
22
22
|
org: acme-corp
|
|
23
23
|
project: Platform
|
|
24
24
|
done_state: Closed
|
|
25
|
-
verify: "az
|
|
25
|
+
verify: "az devops project show --project {project}"
|
|
26
26
|
|
|
27
27
|
warehouse:
|
|
28
28
|
tool: synapse
|
|
@@ -37,6 +37,7 @@ seams:
|
|
|
37
37
|
tool: teams
|
|
38
38
|
adapter: adapters/chat/teams.md
|
|
39
39
|
transport: mcp
|
|
40
|
+
mcp: teams # your Teams MCP server name (adapter calls resolve as mcp__{mcp}__…)
|
|
40
41
|
channel: "Data Platform / General"
|
|
41
42
|
default_mode: draft
|
|
42
43
|
always_include: [Dana]
|
|
@@ -66,7 +67,7 @@ policies:
|
|
|
66
67
|
db_write_requires_approval: true
|
|
67
68
|
chat_default_draft: true
|
|
68
69
|
hyperlink_everything: true
|
|
69
|
-
|
|
70
|
+
skillify_everything: true
|
|
70
71
|
reduce_assumptions: true
|
|
71
72
|
commit_plan_before_implement: true
|
|
72
73
|
system_evolution: true
|
|
@@ -33,6 +33,7 @@ policies: # behavioral rules every skill inherits (the kit's "global rules
|
|
|
33
33
|
| `terminal_status` | string | `Done` | The "done" workflow state (not always "Done"). |
|
|
34
34
|
| `ticket_url_template` | template \| null | `https://acme.atlassian.net/browse/{id}` | How `tickets/INDEX.md` links each ticket (`{id}` token). Null/omitted → the index renders no per-ticket link. |
|
|
35
35
|
| `word_limits` | map | `{tracker_comment: 100, chat: 100, pr: 200, ticket: 200}` | Hard caps the comms skills enforce. |
|
|
36
|
+
| `graph_notes` | bool | `true` | Generate the Obsidian graph layer (`tickets/graph/` + `tickets/objects/`). On by default; set `false` to disable. |
|
|
36
37
|
|
|
37
38
|
## `seams`
|
|
38
39
|
|
|
@@ -57,7 +58,7 @@ and `refresh context` degrade gracefully (skip warehouse steps) when it is.
|
|
|
57
58
|
| `db_write_requires_approval` | `true` | any skill issuing a non-SELECT — show SQL, explain, wait for `yes`. |
|
|
58
59
|
| `chat_default_draft` | `true` | `chat.draft` not `chat.send` unless the user says "send it". |
|
|
59
60
|
| `hyperlink_everything` | `true` | comms skills wrap every ticket-ID / file / PR in a smart link. |
|
|
60
|
-
| `
|
|
61
|
+
| `skillify_everything` | `true` | recurring work → a `/productize` skill the agent can invoke, not a one-off. |
|
|
61
62
|
| `reduce_assumptions` | `true` | ask before building; still document every assumption in the ticket README. |
|
|
62
63
|
| `commit_plan_before_implement` | `true` | `spec-and-build` commits the spec/plan artifact before `build` (blame-free retry). |
|
|
63
64
|
| `system_evolution` | `true` | `ship` retro: a failure fixes the AI layer (rule/context/command/adapter), not just the ticket. |
|
|
@@ -17,6 +17,7 @@ project:
|
|
|
17
17
|
terminal_status: Done # the workflow's "done" state (not always "Done")
|
|
18
18
|
ticket_url_template: "https://acme.atlassian.net/browse/{id}" # how INDEX.md links a ticket ({id} token); omit/null = no link
|
|
19
19
|
word_limits: {tracker_comment: 100, chat: 100, pr: 200, ticket: 200}
|
|
20
|
+
graph_notes: true # Obsidian graph layer (tickets/graph + tickets/objects); false to disable
|
|
20
21
|
|
|
21
22
|
seams:
|
|
22
23
|
tracker:
|
|
@@ -26,7 +27,7 @@ seams:
|
|
|
26
27
|
site: acme.atlassian.net
|
|
27
28
|
cli: acli
|
|
28
29
|
mcp: atlassian
|
|
29
|
-
verify:
|
|
30
|
+
verify: 'acli jira workitem search --jql "project = {key_prefix}" --limit 1'
|
|
30
31
|
|
|
31
32
|
warehouse:
|
|
32
33
|
tool: snowflake
|
|
@@ -69,7 +70,7 @@ policies:
|
|
|
69
70
|
db_write_requires_approval: true
|
|
70
71
|
chat_default_draft: true
|
|
71
72
|
hyperlink_everything: true
|
|
72
|
-
|
|
73
|
+
skillify_everything: true
|
|
73
74
|
reduce_assumptions: true
|
|
74
75
|
commit_plan_before_implement: true
|
|
75
76
|
system_evolution: true
|
|
@@ -46,12 +46,23 @@ def main() -> int:
|
|
|
46
46
|
# index_data.json is source, NOT generated — editing the store SHOULD re-render.
|
|
47
47
|
if changed.name in ("INDEX.md", "OBJECTS.md"):
|
|
48
48
|
return 0
|
|
49
|
+
# generated graph layer (tickets/graph/, tickets/objects/) — regenerated below; never react to it
|
|
50
|
+
if any(part in ("graph", "objects") for part in changed.relative_to(tickets_dir).parts[:-1]):
|
|
51
|
+
return 0
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
# The renderer module ships with the kit (bin/), not the user's project — resolve it against
|
|
54
|
+
# CLAUDE_PLUGIN_ROOT (else this hook's own kit dir). `root` stays the project for the data.
|
|
55
|
+
kit = os.environ.get("CLAUDE_PLUGIN_ROOT")
|
|
56
|
+
bindir = (Path(kit).resolve() if kit else Path(__file__).resolve().parent.parent.parent) / "bin"
|
|
57
|
+
sys.path.insert(0, str(bindir))
|
|
51
58
|
try:
|
|
52
|
-
from build_ticket_index import build_rows, render, render_objects # type: ignore
|
|
59
|
+
from build_ticket_index import build_rows, render, render_objects, render_graph_layer, load_config # type: ignore
|
|
53
60
|
rows = build_rows(root)
|
|
61
|
+
cfg = load_config(root)
|
|
54
62
|
fresh = {tickets_dir / "INDEX.md": render(rows), tickets_dir / "OBJECTS.md": render_objects(rows)}
|
|
63
|
+
graph_dirs = [tickets_dir / "graph", tickets_dir / "objects"] # always tracked so disabling cleans up
|
|
64
|
+
if cfg.get("graph_notes", True):
|
|
65
|
+
fresh.update(render_graph_layer(rows, root))
|
|
55
66
|
except SystemExit:
|
|
56
67
|
return 0 # malformed index_data.json — surfaced when the agent runs the renderer
|
|
57
68
|
except Exception:
|
|
@@ -61,9 +72,18 @@ def main() -> int:
|
|
|
61
72
|
changed_any = False
|
|
62
73
|
for p, txt in fresh.items():
|
|
63
74
|
if (p.read_text() if p.is_file() else None) != txt:
|
|
75
|
+
p.parent.mkdir(parents=True, exist_ok=True)
|
|
64
76
|
p.write_bytes(txt.encode("utf-8")); changed_any = True
|
|
77
|
+
fresh_keys_ci = {str(p).lower() for p in fresh} # orphan cleanup (case-insensitive for macOS)
|
|
78
|
+
for gd in graph_dirs:
|
|
79
|
+
if gd.is_dir():
|
|
80
|
+
for existing in gd.glob("*.md"):
|
|
81
|
+
if str(existing).lower() not in fresh_keys_ci:
|
|
82
|
+
existing.unlink(); changed_any = True
|
|
83
|
+
if not any(gd.iterdir()):
|
|
84
|
+
gd.rmdir()
|
|
65
85
|
if changed_any:
|
|
66
|
-
print("Auto-regenerated tickets/INDEX.md + OBJECTS.md (ticket folder changed). "
|
|
86
|
+
print("Auto-regenerated tickets/INDEX.md + OBJECTS.md + graph layer (ticket folder changed). "
|
|
67
87
|
"Run bin/ingest_index_records.py to refresh a ticket's curated summary.")
|
|
68
88
|
except OSError:
|
|
69
89
|
pass
|
|
@@ -52,10 +52,8 @@ def main() -> int:
|
|
|
52
52
|
|
|
53
53
|
skills = sorted(p.parent.name for p in (root / ".claude/skills").glob("*/SKILL.md")) \
|
|
54
54
|
if (root / ".claude/skills").is_dir() else []
|
|
55
|
-
# Hide deprecated v1 alias stubs — they route to v2 skills and shouldn't re-clutter the surface.
|
|
56
55
|
commands = sorted(
|
|
57
56
|
p.stem for p in (root / ".claude/commands").glob("*.md")
|
|
58
|
-
if "Deprecated" not in p.read_text(errors="replace")[:200]
|
|
59
57
|
) if (root / ".claude/commands").is_dir() else []
|
|
60
58
|
|
|
61
59
|
lines = [
|
|
@@ -38,7 +38,11 @@ def discovered_total(root: Path) -> int | None:
|
|
|
38
38
|
"""True ticket count from the renderer's discovery (cheap globs, no README reads)."""
|
|
39
39
|
try:
|
|
40
40
|
import sys
|
|
41
|
-
|
|
41
|
+
# The renderer module ships with the kit (bin/), not the project — resolve via
|
|
42
|
+
# CLAUDE_PLUGIN_ROOT (else this hook's own kit dir); `root` stays the project for discover().
|
|
43
|
+
kit = os.environ.get("CLAUDE_PLUGIN_ROOT")
|
|
44
|
+
bindir = (Path(kit).resolve() if kit else Path(__file__).resolve().parent.parent.parent) / "bin"
|
|
45
|
+
sys.path.insert(0, str(bindir))
|
|
42
46
|
from build_ticket_index import discover # type: ignore
|
|
43
47
|
return len(discover(root))
|
|
44
48
|
except Exception:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_README": "Rendered to .claude/settings.json by /setup.
|
|
2
|
+
"_README": "Rendered to .claude/settings.json by /setup. The hooks block below is for a VENDORED (cp -r) install, where nothing else wires the kit's hooks. On a PLUGIN install (CLAUDE_PLUGIN_ROOT set), .claude-plugin/plugin.json already wires these same hooks from the plugin dir, so /setup OMITS this hooks block to avoid double-firing (double db-write prompts, double index regen) AND ADDS an 'extraKnownMarketplaces' (ticketwright github source, autoUpdate:true) + 'enabledPlugins' (ticketwright@ticketwright:true) block so the REPO opts every user into the plugin at PROJECT scope (a plugin cannot set its own install scope — the repo does; teammates are prompted to install), committed with the repo, and self-updates on formal releases only (version-string change; never un-released main commits). Do NOT add those two keys on a vendored install — there is no marketplace to enable from. permissions + statusLine are written in both modes (setup appends stack-specific CLI allows); on a plugin install setup copies statusline.sh into .claude/ so the relative command resolves. The db_write_guard hook turns 'db_write_requires_approval' into a mechanical ask before any destructive warehouse statement.",
|
|
3
3
|
"hooks": {
|
|
4
4
|
"PreToolUse": [
|
|
5
5
|
{
|
|
@@ -8,7 +8,7 @@ disable-model-invocation: true
|
|
|
8
8
|
|
|
9
9
|
# /productize
|
|
10
10
|
|
|
11
|
-
The meta-skill (`
|
|
11
|
+
The meta-skill (`skillify_everything`). When a task recurs (quarterly sale, monthly report,
|
|
12
12
|
periodic reconciliation), this stamps out a new **folder skill** with the productized skeleton:
|
|
13
13
|
a phased pipeline with hard halts, parameterized SQL/steps, a QC checkpoint, a golden-replay
|
|
14
14
|
regression test, and a hard halt before any external post. `/refresh index` + the `--recurring`
|
|
@@ -25,7 +25,7 @@ Ask (AskUserQuestion) for:
|
|
|
25
25
|
to assert; the byte-identical output file to diff against).
|
|
26
26
|
|
|
27
27
|
## Phase 2 — Stamp from the template
|
|
28
|
-
1. Copy
|
|
28
|
+
1. Copy `${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/templates/productized-skill/` → `.claude/skills/<name>/` (SKILL.md + `sql/ templates/
|
|
29
29
|
bin/ golden/`).
|
|
30
30
|
2. Render `SKILL.md.tmpl` with the interview answers into the canonical phase shape:
|
|
31
31
|
**Phase 0** pre-flight (validate params + `verify_stack` for touched seams + an object drift
|
|
@@ -24,7 +24,7 @@ warehouse round-trip — cheap insurance. It asserts: zero leftover tokens · no
|
|
|
24
24
|
balanced single-quotes / parens · flags an unquoted SQL literal. Halt on any error.
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
bash bin/render_and_validate.sh sql/step.sql.tmpl asof=2026-06-30 src=T [--strict]
|
|
27
|
+
bash "${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/bin/render_and_validate.sh" sql/step.sql.tmpl asof=2026-06-30 src=T [--strict]
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
## Export helpers — `bin/split_and_export.sh`
|
|
@@ -24,7 +24,10 @@ Read-only: it reviews and re-runs, it does not edit code (the build owns fixes).
|
|
|
24
24
|
- `= NULL` → must be `IS NULL`; missing div-by-zero guard; `SELECT *` in deliverables; functions on
|
|
25
25
|
filtered columns; implicit cross-source type mismatches (missing `CAST`); hardcoded values that
|
|
26
26
|
should be parameters; missing required schema/instance filters; `LEFT JOIN` predicate in `WHERE`
|
|
27
|
-
that silently becomes an inner join; `NOT IN` with nullable columns; `UNION` vs `UNION ALL
|
|
27
|
+
that silently becomes an inner join; `NOT IN` with nullable columns; `UNION` vs `UNION ALL`;
|
|
28
|
+
session-variable/`DECLARE` parameterization in a shipped or exported query (prefer a CTE params
|
|
29
|
+
row — portable, single-statement, export-clean; session vars pollute `--format csv` output and
|
|
30
|
+
don't survive into single-statement JDBC/ODBC clients).
|
|
28
31
|
|
|
29
32
|
**② Counts & dedup** (re-run independently)
|
|
30
33
|
- Re-run the row-count; compare to the documented count. **Duplicate detection is the primary test:**
|
|
@@ -42,7 +45,9 @@ Read-only: it reviews and re-runs, it does not edit code (the build owns fixes).
|
|
|
42
45
|
maintainability). Classify each finding Critical / Should-fix / Review.
|
|
43
46
|
|
|
44
47
|
**⑤ Human sign-off**
|
|
45
|
-
- Output format check (CSV headers row 1, no preamble/blank rows; filenames carry record counts
|
|
48
|
+
- Output format check (CSV headers row 1, no preamble/blank rows; filenames carry record counts;
|
|
49
|
+
**ASCII punctuation only in cell values** — no em/en dashes, smart quotes, or ellipsis chars, which
|
|
50
|
+
render as mojibake in Excel/CSV viewers).
|
|
46
51
|
- README completeness (assumptions enumerated, QC results, business context). Flag for the human.
|
|
47
52
|
|
|
48
53
|
## Tiers & halting
|
|
@@ -46,10 +46,11 @@ include the key commented with a `# TODO` and keep going — `verify` will point
|
|
|
46
46
|
4. Compose `.claude/config/stack.yaml` per `stack.schema.md` (chosen seams live; optional seams as
|
|
47
47
|
commented blocks; the 9 policies with a one-line "when to change this" comment each). Warn if a
|
|
48
48
|
chosen adapter is `status: stub`.
|
|
49
|
-
5. Scaffold the repo per [scaffold.md](scaffold.md): render `AGENTS.md` (+ role focus)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
5. Scaffold the repo per [scaffold.md](scaffold.md): render `AGENTS.md` (+ role focus) and a one-line
|
|
50
|
+
`CLAUDE.md` (`@AGENTS.md`, so Claude Code auto-loads the rules),
|
|
51
|
+
`.claude/settings.json` (hooks — omitted on a plugin install — + read-only CLI allows), folders,
|
|
52
|
+
`.gitignore` (deliverable CSVs committed by default; PII opts out via `*.private.csv` / a
|
|
53
|
+
`private/` subfolder), the AI-layer index, and the seeded ticket index.
|
|
53
54
|
|
|
54
55
|
### Phase 4 — Verify & hand off
|
|
55
56
|
6. Run `!bash "${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/bin/selftest.sh"` (kit integrity — a failure here is fatal) and
|
|
@@ -57,3 +58,10 @@ include the key commented with a `# TODO` and keep going — `verify` will point
|
|
|
57
58
|
**not** fatal at setup time; print its adapter's auth notes as the fix).
|
|
58
59
|
7. **Report:** the chosen stack, files written, any `# TODO` keys or stub adapters, and the next
|
|
59
60
|
step — `/ticket <id>` to start work, or `/setup --teammate` for a new person.
|
|
61
|
+
8. **Offer to commit the scaffold.** What setup just wrote (`.claude/config/stack.yaml`, `AGENTS.md`,
|
|
62
|
+
`CLAUDE.md`, `.claude/settings.json`, `.gitignore`, `documentation/AI_LAYER_INDEX.md`, the seeded `tickets/`
|
|
63
|
+
index — plus, on a vendored install, the kit itself) is untracked; if it isn't committed, a later
|
|
64
|
+
ticket PR references rules/adapters absent from the repo's history. Offer a commit (e.g.
|
|
65
|
+
`chore: initialize ticketwright workspace`). First flag that `stack.yaml` may hold internal
|
|
66
|
+
identifiers (tracker site, warehouse project/dataset) — config, not secrets, but worth a glance
|
|
67
|
+
before committing to a public repo.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Scaffold details — what `/setup` writes into a fresh repo
|
|
2
|
+
|
|
3
|
+
## Global rules (`AGENTS.md`)
|
|
4
|
+
Render `${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/templates/AGENTS.md.tmpl` → `AGENTS.md` (tokens
|
|
5
|
+
from `stack.yaml`: tool names, key_prefix, terminal_status, word limits, policies). Fill
|
|
6
|
+
`{{role_focus}}` from `${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/templates/roles/<role>.md` using
|
|
7
|
+
`project.role` (`generalist` unless the user changed it). This is the always-loaded tier —
|
|
8
|
+
keep it the rendered template; repo-specific rules get added by humans over time.
|
|
9
|
+
|
|
10
|
+
Also write `CLAUDE.md` from `${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/templates/CLAUDE.md.tmpl` — a
|
|
11
|
+
one-line `@AGENTS.md` import so **Claude Code** auto-loads these rules (it reads `CLAUDE.md`; other
|
|
12
|
+
agents read `AGENTS.md` directly). Keep it to that single import line.
|
|
13
|
+
|
|
14
|
+
## Hooks + settings (`.claude/settings.json`)
|
|
15
|
+
Render `${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/.claude/settings.json.tmpl` → `.claude/settings.json`.
|
|
16
|
+
**The `hooks` block is install-mode-dependent:**
|
|
17
|
+
- **Plugin install** (`${CLAUDE_PLUGIN_ROOT}` set): `.claude-plugin/plugin.json` already wires the
|
|
18
|
+
kit's hooks from the plugin dir — **OMIT the `hooks` block** here, or they double-fire (double
|
|
19
|
+
db-write prompts, double index regen). Keep `permissions` + `statusLine`.
|
|
20
|
+
- **Vendored install** (`cp -r`, no `${CLAUDE_PLUGIN_ROOT}`): **keep the `hooks` block** — nothing
|
|
21
|
+
else wires it: `db_write_guard.py` (PreToolUse) makes `db_write_requires_approval` mechanical;
|
|
22
|
+
`session_context.py` + `ticket_index_context.py` (SessionStart) prime the stack + ticket catalog;
|
|
23
|
+
`regenerate_ticket_index.py` (PostToolUse) keeps `tickets/INDEX.md` fresh on folder changes.
|
|
24
|
+
|
|
25
|
+
**On a plugin install, also commit a project-scoped enablement.** A plugin can't set its own install
|
|
26
|
+
scope — the *repo* opts in. Merge these two keys
|
|
27
|
+
into the rendered `.claude/settings.json` so the plugin is enabled *for this repo* (committed →
|
|
28
|
+
travels with the repo; teammates who open and trust it are prompted to install it; it survives the
|
|
29
|
+
original author leaving) and refreshes
|
|
30
|
+
itself:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"extraKnownMarketplaces": {
|
|
35
|
+
"ticketwright": {
|
|
36
|
+
"source": { "source": "github", "repo": "kyle-chalmers/ticketwright" },
|
|
37
|
+
"autoUpdate": true
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"enabledPlugins": {
|
|
41
|
+
"ticketwright@ticketwright": true
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`autoUpdate` re-installs **only when the plugin's version string changes** — i.e. only on a formal
|
|
47
|
+
release (the release commit bumps `plugin.json`/`marketplace.json`/`__init__.py` in lockstep and tags
|
|
48
|
+
`v*`). Between releases, ordinary commits to the default branch leave the version untouched, so
|
|
49
|
+
teammates are never pulled onto un-released mid-flight work. Do **not** add these keys on a vendored
|
|
50
|
+
(`cp -r`/pip) install — there's no marketplace to enable from; the kit is already in-repo.
|
|
51
|
+
|
|
52
|
+
Then append the chosen warehouse/tracker/vcs **read-only** CLI allows to `permissions.allow` (e.g.
|
|
53
|
+
`Bash(<warehouse_cli> …:*)`). **Statusline:** the template's `statusLine.command` is the
|
|
54
|
+
project-relative `.claude/statusline.sh`, so on a plugin install **copy
|
|
55
|
+
`${CLAUDE_PLUGIN_ROOT}/.claude/statusline.sh` → `.claude/statusline.sh`** so it resolves (on a
|
|
56
|
+
vendored install it's already there).
|
|
57
|
+
|
|
58
|
+
## Folders + `.gitignore`
|
|
59
|
+
Create `tickets/{assignee_dir}/`, `documentation/`, `resources/`, `specs/` (and `ci/` if wanted).
|
|
60
|
+
Render `${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/templates/gitignore.tmpl` → `.gitignore` (merge if
|
|
61
|
+
one exists). Deliverable exports (`final_deliverables/*.csv` etc.) are **committed by default** so
|
|
62
|
+
results live with the ticket and show in the PR; PII/customer data opts out via a `*.private.csv`
|
|
63
|
+
name or a `private/` subfolder (both gitignored). If the tracker adapter ships an attachment-download
|
|
64
|
+
helper, copy it into `resources/`.
|
|
65
|
+
|
|
66
|
+
## AI-layer index (`documentation/AI_LAYER_INDEX.md`)
|
|
67
|
+
A one-line-each inventory of the installed skills (`setup`, `ticket`, `spec-and-build`, `review`,
|
|
68
|
+
`ship`, `productize`, `refresh`), the `qc-reviewer` agent, the hooks, and the adapters in use — so
|
|
69
|
+
humans and agents can find what exists.
|
|
70
|
+
|
|
71
|
+
## Ticket index
|
|
72
|
+
Seed an empty curated store — `tickets/index_data.json` with
|
|
73
|
+
`{"schema_version": 1, "tickets": []}` — then run
|
|
74
|
+
`python3 "${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/bin/build_ticket_index.py"` to write the
|
|
75
|
+
initial `tickets/INDEX.md`. From here it self-maintains (PostToolUse regen on folder changes,
|
|
76
|
+
SessionStart surfacing, curated summaries at ship time). An existing backlog gets bootstrapped with
|
|
77
|
+
`/refresh index --all`.
|
|
78
|
+
|
|
79
|
+
That renderer also writes the Obsidian graph layer (`tickets/graph/` + `tickets/objects/`) when
|
|
80
|
+
`project.graph_notes` is on (the default), committed alongside `INDEX.md`/`OBJECTS.md`.
|
|
@@ -40,9 +40,13 @@ authorization, execute in order:
|
|
|
40
40
|
7. **chat.draft** to `seams.chat.default_channel` (policy `chat_default_draft` — the human clicks
|
|
41
41
|
send unless they said "send it", in which case `chat.send`). Smart links for ticket id(s),
|
|
42
42
|
files, PR.
|
|
43
|
-
8. **vcs.commit**
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
8. **vcs.commit** — stage this ticket's paths (deliverable files included: they're committed by
|
|
44
|
+
default so results live with the ticket and show in the PR) **plus `tickets/INDEX.md` +
|
|
45
|
+
`tickets/OBJECTS.md` + `tickets/index_data.json`** (all three, or `--check` flags drift in CI;
|
|
46
|
+
semantic message + Co-Authored-By). **Before staging, list the `final_deliverables/` files that
|
|
47
|
+
will be committed and confirm none carry PII/customer data that shouldn't be in git** — if any do,
|
|
48
|
+
have the user rename them `*.private.csv` (etc.) or move them under a `private/` subfolder (both
|
|
49
|
+
gitignored) first. Then **vcs.open_pr** (semantic title; body = Business Impact / Deliverables /
|
|
46
50
|
Technical Notes / QC).
|
|
47
51
|
9. **transition** the ticket toward `project.terminal_status` if appropriate.
|
|
48
52
|
|
|
@@ -27,7 +27,7 @@ context-engineering core idea: AI fails from missing context, not weak models.
|
|
|
27
27
|
`bin/recall.py --for <id>`) and reuse their SQL/QC where it fits;
|
|
28
28
|
- pull the business rules from the `documentation/` glossary (the domain slice).
|
|
29
29
|
Research agents return findings only; **they do not write code.**
|
|
30
|
-
3. **Write the spec** from
|
|
30
|
+
3. **Write the spec** from `${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/templates/spec.md.tmpl` into the ticket's folder
|
|
31
31
|
(`specs/<id>-<slug>.md` or `final_deliverables/`): operation type (new/alter), data grain,
|
|
32
32
|
sources + join/cast rules, transformation logic, **validation gates** (the exact QC the build must
|
|
33
33
|
pass), downstream impact, dev-env target (`seams.warehouse.dev_db`), and a **confidence score
|
|
@@ -41,7 +41,9 @@ context-engineering core idea: AI fails from missing context, not weak models.
|
|
|
41
41
|
6. **Load** the committed spec (path arg or newest in the ticket's `specs/`). Treat it as the source
|
|
42
42
|
of truth, but **validate each step independently** — don't blindly follow; the spec can be wrong.
|
|
43
43
|
7. **Implement in small build-and-check sub-loops:** one object/step at a time. Develop against
|
|
44
|
-
`seams.warehouse.dev_db` first; parameterize values at the top
|
|
44
|
+
`seams.warehouse.dev_db` first; parameterize values at the top **via a CTE params row
|
|
45
|
+
(`WITH params AS (SELECT … AS anchor) … CROSS JOIN params`), not a session `DECLARE`/`SET`** —
|
|
46
|
+
CTE params stay portable and keep CSV exports clean; explicit `ORDER BY` on any export
|
|
45
47
|
(deterministic outputs).
|
|
46
48
|
8. **Embed validation between steps** — after each, run the relevant gate from the spec; self-correct.
|
|
47
49
|
9. **Any non-SELECT / DDL** ⇒ policy `db_write_requires_approval`: show the exact SQL, explain the
|
|
@@ -35,9 +35,14 @@ this before?", "which tickets touched VW_X?").
|
|
|
35
35
|
5. **Branch** via the vcs adapter, named `<id>`, off `seams.vcs.default_branch` — or a **worktree**
|
|
36
36
|
when `--worktree` is passed (isolates this ticket from other in-flight work; recommended when you
|
|
37
37
|
run several tickets in parallel).
|
|
38
|
-
6. **Scaffold** `project.ticket_path` with `project.ticket_subdirs
|
|
39
|
-
`
|
|
40
|
-
`
|
|
38
|
+
6. **Scaffold** `project.ticket_path` with `project.ticket_subdirs` (create the subdirs empty —
|
|
39
|
+
**no `.gitkeep` placeholders**; they fill with real files during build and git picks them up
|
|
40
|
+
then); tracker adapter `download_attachments` → `source_materials/` (silent if none); render
|
|
41
|
+
`${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/templates/ticket-README.md.tmpl` → the ticket dir.
|
|
42
|
+
6b. **Refresh the catalog** so the new ticket shows up immediately — it won't otherwise, because the
|
|
43
|
+
PostToolUse index hook only fires on `Write`/`Edit` and scaffolding happens via Bash:
|
|
44
|
+
`python3 "${CLAUDE_PLUGIN_ROOT:-$CLAUDE_PROJECT_DIR}/bin/build_ticket_index.py"` (writes this
|
|
45
|
+
project's `tickets/INDEX.md` + `OBJECTS.md`; the new row shows `▱` until `/ship` curates it).
|
|
41
46
|
|
|
42
47
|
## Phase 3 — Prime context automatically (the part you never have to ask for)
|
|
43
48
|
7. Follow [priming.md](priming.md), in order:
|
|
@@ -3,6 +3,109 @@
|
|
|
3
3
|
All notable changes to this project are documented here. Format loosely follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/); this project uses semantic-ish versioning.
|
|
5
5
|
|
|
6
|
+
## [3.2.0] — 2026-07-06
|
|
7
|
+
|
|
8
|
+
Makes a plugin install **project-scoped by default** — the *repo* commits the enablement (a plugin
|
|
9
|
+
can't set its own install scope), so it travels with the repo and every teammate who opens the repo is
|
|
10
|
+
prompted to install it (no marketplace to add or config to write), staying current across the team.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Project-scoped enablement is the default on plugin installs.** On a plugin install, `/setup` now
|
|
14
|
+
also writes an `extraKnownMarketplaces` (ticketwright github source, `autoUpdate: true`) +
|
|
15
|
+
`enabledPlugins` (`ticketwright@ticketwright`) block into the repo's committed
|
|
16
|
+
`.claude/settings.json`. It's the *repo* opting in at project scope (a plugin can't set its own install scope): teammates who
|
|
17
|
+
open and trust the repo are prompted to install it — that repo only — and it keeps working after the
|
|
18
|
+
person who set it up moves on. `autoUpdate` re-installs
|
|
19
|
+
**only on a formal release**: Claude Code refreshes when the version string changes, and the version
|
|
20
|
+
only moves in a tagged release commit, so ordinary `main` commits never pull teammates onto
|
|
21
|
+
un-released work. Not written on vendored (`cp -r`/pip) installs — there's no marketplace to enable
|
|
22
|
+
from. README documents it as the recommended team install; `scaffold.md` + the `settings.json.tmpl`
|
|
23
|
+
`_README` carry the rationale.
|
|
24
|
+
- **`bin/selftest.sh` §21** asserts the enablement block is documented, is valid JSON, targets the
|
|
25
|
+
real marketplace repo, and is release-gated (`autoUpdate`).
|
|
26
|
+
|
|
27
|
+
## [3.1.0] — 2026-07-06
|
|
28
|
+
|
|
29
|
+
Hardening release from two real end-to-end runs (a `/setup` session and a full analysis ticket). Fixes
|
|
30
|
+
the plugin/pip install path bugs those runs surfaced, de-hardcodes the shipped adapters for a clean
|
|
31
|
+
first run on any stack, adds a SQL-portability guardrail, and flips the deliverable-CSV default to
|
|
32
|
+
commit-by-default with a PII opt-out. Also adds an **Obsidian graph layer** for browsing the ticket
|
|
33
|
+
archive as a knowledge graph.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
- **Kit-root vs project-root resolution** (the class behind a `/ship` crash on plugin/pip installs).
|
|
37
|
+
`bin/enrich_ticket.py` resolved its sibling scripts off the project dir (`repo_root() / "bin"`),
|
|
38
|
+
which doesn't exist when the kit and project diverge — now resolved from the script's own kit dir.
|
|
39
|
+
The two SessionStart/PostToolUse index hooks imported the renderer from the project's `bin/` (silent
|
|
40
|
+
fail-open on a plugin install) — now from `${CLAUDE_PLUGIN_ROOT}` / the hook's own kit dir.
|
|
41
|
+
`bin/verify_stack.sh` resolved adapters off the stack.yaml location (the project) — now off the kit
|
|
42
|
+
(`${CLAUDE_PLUGIN_ROOT}`, else the script's dir), with a project fallback for repo-vendored adapters.
|
|
43
|
+
- **Self-maintaining index went stale on ticket creation.** `/ticket` scaffolds via Bash, which the
|
|
44
|
+
`Write|Edit` PostToolUse hook never sees, so a new ticket didn't appear in `INDEX.md` until a manual
|
|
45
|
+
run. `/ticket` now renders the index as its final scaffold step; `build_ticket_index.py` also gained
|
|
46
|
+
a `git rev-parse --show-toplevel` fallback for by-hand runs without `CLAUDE_PROJECT_DIR`.
|
|
47
|
+
|
|
48
|
+
### Changed
|
|
49
|
+
- **Deliverable exports are committed by default.** The old `.gitignore` blanket-ignored
|
|
50
|
+
`**/final_deliverables/*.csv` (+ `.tsv`/`.xlsx`/`.parquet`), so results silently never reached git or
|
|
51
|
+
the PR. Now they're committed; PII/customer data opts out via a `*.private.csv` name or a `private/`
|
|
52
|
+
subfolder (both gitignored). `/ship` lists the deliverable files and asks you to confirm none carry
|
|
53
|
+
PII before committing. To restore the strict "docstore only" default, uncomment the four blanket
|
|
54
|
+
rules in the rendered `.gitignore` (documented in-file).
|
|
55
|
+
- **Adapters de-hardcoded for a clean first run on any stack.** MCP server names now use the `{mcp}`
|
|
56
|
+
token from `stack.yaml` (was `mcp__atlassian__…` / `mcp__slack__…` / `mcp__plugin_productivity_*__…`);
|
|
57
|
+
the Jira and Azure DevOps `verify` no longer depend on the nullable `default_epic`; removed
|
|
58
|
+
org-specific Jira issue-type / mandatory-Epic / terminal-state content; `--parent` is now conditional
|
|
59
|
+
on `default_epic` being set. Added `mcp` to the asana/linear/monday adapter `requires`.
|
|
60
|
+
- **`/setup` is install-mode aware.** On a plugin install it omits the settings.json `hooks` block
|
|
61
|
+
(`plugin.json` already wires them — duplicating double-fired), copies `statusline.sh` into the repo
|
|
62
|
+
so the statusline resolves, and offers to commit the scaffold at the end (so a later ticket PR
|
|
63
|
+
doesn't reference rules/adapters absent from history).
|
|
64
|
+
- Ticket scaffolding no longer creates `.gitkeep` placeholders in subdirs that fill during build.
|
|
65
|
+
- **Renamed the `commandify_everything` policy → `skillify_everything`**, completing the v2/v3
|
|
66
|
+
commands→skills shift: recurring work becomes a `/productize` **skill the agent can invoke itself**,
|
|
67
|
+
not a command a human must issue. Behavior is unchanged; update the key in an existing `stack.yaml`.
|
|
68
|
+
- Spelled out **KISS (Keep It Simple, Stupid)** and **YAGNI (You Aren't Gonna Need It)** in the
|
|
69
|
+
always-loaded rules so a human reviewer knows exactly what they mean.
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
- **Obsidian graph layer.** `build_ticket_index.py` now also generates `tickets/graph/<id>.md`
|
|
73
|
+
(a node per ticket) and `tickets/objects/<object>.md` (a node per data object), so the repo opens as
|
|
74
|
+
an Obsidian vault: object clusters show every ticket that touched a table, and cross-refs show as
|
|
75
|
+
direct build-on lines. Plain markdown (no plugins/wikilinks), auto-maintained on the index hook, on
|
|
76
|
+
by default (`project.graph_notes: false` to disable). README convention untouched.
|
|
77
|
+
- **Portable-params guardrail (CTE vs session `DECLARE`).** BigQuery/Snowflake/Synapse `dialect_notes`,
|
|
78
|
+
the `/review` dialect-lint tier, and `/spec-and-build` now steer to a `WITH params AS (…) … CROSS
|
|
79
|
+
JOIN params` row — a `DECLARE` script pollutes `--format csv` exports and breaks in single-statement
|
|
80
|
+
JDBC/ODBC clients (DataGrip/Simba).
|
|
81
|
+
- **CSV deliverables use ASCII punctuation only.** Em/en dashes, smart quotes, and ellipsis characters
|
|
82
|
+
render as mojibake in Excel and many CSV viewers, so the always-loaded rules (`AGENTS.md`) and the
|
|
83
|
+
`/review` output-format check now steer cell values to plain ASCII (`-`, or "to" for ranges).
|
|
84
|
+
- **Scaffolds a one-line `CLAUDE.md` (`@AGENTS.md`)** so Claude Code auto-loads the always-loaded
|
|
85
|
+
rules (it reads `CLAUDE.md`; other agents read `AGENTS.md`). `AGENTS.md` now also points at
|
|
86
|
+
`tickets/INDEX.md` / `OBJECTS.md`, putting the reuse-prior-work habit in the always-loaded tier.
|
|
87
|
+
- **`bin/selftest.sh` §20** (path resolution + adapter hygiene) locks each fix above; §19 updated for
|
|
88
|
+
the new CSV default. Suite is at **119 checks**.
|
|
89
|
+
|
|
90
|
+
## [3.0.0] — 2026-07-04
|
|
91
|
+
|
|
92
|
+
Breaking: removed the 12 deprecated v1 alias stubs. The v2.0 release kept the old command names
|
|
93
|
+
(`/start-ticket`, `/qc-review`, …) working as thin routers to their v2 skills, with a documented
|
|
94
|
+
"removed in v3" lifecycle. That removal is this release. No engine, adapter, template, or skill
|
|
95
|
+
behavior changed — purely the scheduled drop of the compatibility layer.
|
|
96
|
+
|
|
97
|
+
### Removed
|
|
98
|
+
- **12 v1 alias stubs** (`.claude/commands/`). Use the v2 names instead:
|
|
99
|
+
`start-ticket` / `prime-ticket` / `prime-domain` / `prime-warehouse` / `recall` → **`/ticket`**
|
|
100
|
+
(`--recall` for standalone lookups) · `qc-review` → **`/review`** · `deliver-ticket` →
|
|
101
|
+
**`/ship`** · `configure-workspace` / `onboard-teammate` → **`/setup`** (`--teammate`) ·
|
|
102
|
+
`productize-workflow` → **`/productize`** · `build-ticket-index` / `build-context-pack` →
|
|
103
|
+
**`/refresh`** (`index` / `context` / `all`). Full map: `docs/troubleshooting.md`.
|
|
104
|
+
|
|
105
|
+
### Changed
|
|
106
|
+
- `bin/selftest.sh` check 14b now asserts the 12 stubs are absent (was: asserts all present).
|
|
107
|
+
- `.claude/hooks/session_context.py` dropped the now-unneeded deprecated-alias filter.
|
|
108
|
+
|
|
6
109
|
## [2.0.0] — 2026-07-01
|
|
7
110
|
|
|
8
111
|
The UX release: **13 invokables → 7 skills**, one front door, ≤5-question setup, plain language on
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ticketwright
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Tool-agnostic AI layer for ticket-driven work repos: prior-art recall, object reverse-index, deep QC, and a self-maintaining ticket index. Stdlib-only, no vector store.
|
|
5
5
|
Project-URL: Homepage, https://github.com/kyle-chalmers/ticketwright
|
|
6
6
|
Project-URL: Repository, https://github.com/kyle-chalmers/ticketwright
|
|
@@ -59,6 +59,26 @@ That's it. `setup` also handles repos that **already have** ticket history — i
|
|
|
59
59
|
existing layout instead of scaffolding, and writes a `MIGRATION.md` checklist (see
|
|
60
60
|
[Adopting an existing repo](#adopting-an-existing-repo)).
|
|
61
61
|
|
|
62
|
+
**Project-scoped by default.** A plugin can't set its own install scope — so instead, `setup` commits
|
|
63
|
+
the enablement into the repo's `.claude/settings.json`. That's the *repo* opting in at project scope:
|
|
64
|
+
it travels *with the repo*, so every teammate who opens (and trusts) the repo is prompted to install
|
|
65
|
+
Ticketwright (no marketplace to add, no config to write), and it keeps working after the person who
|
|
66
|
+
set it up moves on:
|
|
67
|
+
|
|
68
|
+
```jsonc
|
|
69
|
+
{
|
|
70
|
+
"extraKnownMarketplaces": {
|
|
71
|
+
"ticketwright": { "source": { "source": "github", "repo": "kyle-chalmers/ticketwright" }, "autoUpdate": true }
|
|
72
|
+
},
|
|
73
|
+
"enabledPlugins": { "ticketwright@ticketwright": true }
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`autoUpdate` re-installs **only on a formal release** — Claude Code refreshes when the plugin's
|
|
78
|
+
*version* changes, and the version only moves in a tagged release commit, so day-to-day commits to
|
|
79
|
+
`main` never pull teammates onto un-released work. (Prefer the user-level `/plugin install` above for
|
|
80
|
+
personal, cross-repo use; use the committed block when you want the whole team on it.)
|
|
81
|
+
|
|
62
82
|
## How work flows
|
|
63
83
|
|
|
64
84
|
Four steps — **plan → build → check → ship** — and one command to remember:
|
|
@@ -80,8 +100,8 @@ Three supporting skills you'll reach for occasionally:
|
|
|
80
100
|
| `/productize` | Turn a recurring workflow (quarterly pull, monthly report) into its own parameterized, golden-tested skill |
|
|
81
101
|
|
|
82
102
|
Plugin skills are namespaced (`/ticketwright:ticket`); inside a configured repo the short names
|
|
83
|
-
work too. v1 command names
|
|
84
|
-
|
|
103
|
+
work too. (The v1 command names — `/start-ticket`, `/qc-review`, … — were retired in v3; see the
|
|
104
|
+
rename map in [docs/troubleshooting.md](docs/troubleshooting.md#upgrading).)
|
|
85
105
|
|
|
86
106
|
## Never rebuild what's been built
|
|
87
107
|
|
|
@@ -92,6 +112,17 @@ vector store) and writes a *reuse brief*: what to copy, which gotchas carry over
|
|
|
92
112
|
this time. `tickets/OBJECTS.md` answers the reverse question — "which tickets touched `VW_X`?"
|
|
93
113
|
Details: [docs/ticket-index.md](docs/ticket-index.md).
|
|
94
114
|
|
|
115
|
+
## See it as a graph (Obsidian)
|
|
116
|
+
|
|
117
|
+
Ticketwright also writes a small, auto-maintained graph layer under `tickets/` — `graph/<id>.md`
|
|
118
|
+
(a node per ticket) and `objects/<object>.md` (a node per data object) — so you can open the repo as
|
|
119
|
+
an [Obsidian](https://obsidian.md) vault and *browse* your work: open a table like `ANALYTICS.VW_LOAN`
|
|
120
|
+
and its local graph is every ticket that touched it; open a ticket and you see the objects it touched
|
|
121
|
+
plus the tickets it built on. Point Obsidian at the repo (or `tickets/`), open Graph view, and for the
|
|
122
|
+
cleanest picture set the filter to `-README -INDEX -OBJECTS -AGENTS -CLAUDE` and add a color Group on
|
|
123
|
+
`path:objects`. It's plain markdown (no plugins, no wikilinks) and renders on GitHub too. On by
|
|
124
|
+
default; set `project.graph_notes: false` in `stack.yaml` to turn it off.
|
|
125
|
+
|
|
95
126
|
## Safety rails (on by default)
|
|
96
127
|
|
|
97
128
|
- **DB writes ask first** — a hook inspects every warehouse command and prompts before anything
|
|
@@ -99,8 +130,9 @@ Details: [docs/ticket-index.md](docs/ticket-index.md).
|
|
|
99
130
|
- **External posts hard-halt** — `/ship` prints exactly what it's about to post (tracker comment,
|
|
100
131
|
chat message, PR) and waits for your explicit go.
|
|
101
132
|
- **Chat defaults to draft** — you click send.
|
|
102
|
-
- **
|
|
103
|
-
|
|
133
|
+
- **Deliverables commit with the ticket, PII opts out** — exports are committed by default so results
|
|
134
|
+
show in the PR; keep customer data out of git by naming it `*.private.csv` or dropping it in a
|
|
135
|
+
`private/` subfolder, and `/ship` lists what it's about to commit so nothing sensitive slips in.
|
|
104
136
|
- **Every assumption is written down** — the ticket README template enumerates them by category.
|
|
105
137
|
|
|
106
138
|
## Adopting an existing repo
|
|
@@ -131,7 +163,7 @@ ticketwright init # vendor the kit into a repo (plugin-fre
|
|
|
131
163
|
- [docs/ticket-index.md](docs/ticket-index.md) — the ticket catalog + recall engine in depth.
|
|
132
164
|
- [CONTRIBUTING.md](CONTRIBUTING.md) · [ROADMAP.md](ROADMAP.md) · [CHANGELOG.md](CHANGELOG.md)
|
|
133
165
|
|
|
134
|
-
CI runs the
|
|
166
|
+
CI runs the full self-test on every push; PyPI publishing is OIDC Trusted Publishing (no stored
|
|
135
167
|
tokens) — see [docs/pypi-setup.md](docs/pypi-setup.md).
|
|
136
168
|
|
|
137
169
|
## License
|