@ikon85/agent-workflow-kit 0.43.0 → 0.44.0
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.
- package/.agents/skills/grill-me/SKILL.md +3 -1
- package/.agents/skills/grill-with-docs/SKILL.md +3 -1
- package/.agents/skills/kit-update/SKILL.md +9 -11
- package/.agents/skills/orchestrate-wave/SKILL.md +8 -8
- package/.agents/skills/setup-workflow/SKILL.md +20 -18
- package/.agents/skills/setup-workflow/board-sync.md +4 -1
- package/.agents/skills/setup-workflow/workflow-advisories.md +3 -2
- package/.agents/skills/setup-workflow/worktree-lifecycle.md +36 -31
- package/.agents/skills/to-issues/SKILL.md +43 -0
- package/.agents/skills/wrapup/SKILL.md +98 -54
- package/.claude/skills/grill-me/SKILL.md +3 -1
- package/.claude/skills/grill-me-codex/SKILL.md +3 -1
- package/.claude/skills/grill-with-docs/SKILL.md +3 -1
- package/.claude/skills/grill-with-docs-codex/SKILL.md +3 -1
- package/.claude/skills/kit-update/SKILL.md +9 -11
- package/.claude/skills/orchestrate-wave/SKILL.md +8 -8
- package/.claude/skills/setup-workflow/SKILL.md +20 -18
- package/.claude/skills/setup-workflow/board-sync.md +4 -1
- package/.claude/skills/setup-workflow/workflow-advisories.md +3 -2
- package/.claude/skills/setup-workflow/worktree-lifecycle.md +36 -31
- package/.claude/skills/to-issues/SKILL.md +43 -0
- package/.claude/skills/wrapup/SKILL.md +98 -54
- package/README.md +249 -7
- package/agent-workflow-kit.package.json +42 -42
- package/package.json +16 -12
- package/scripts/marker_lib.py +3 -0
- package/scripts/profile_globs.py +26 -45
- package/scripts/release-delta-guard.mjs +26 -9
- package/scripts/worktree-lifecycle/README.md +57 -160
- package/scripts/worktree-lifecycle/capabilities.json +2 -3
- package/scripts/worktree-lifecycle/classify.py +480 -0
- package/scripts/worktree-lifecycle/cleanup.py +9 -29
- package/scripts/worktree-lifecycle/core.py +68 -1429
- package/scripts/worktree-lifecycle/ignore_seed.py +1 -1
- package/scripts/worktree-lifecycle/profile.py +46 -29
- package/scripts/worktree-lifecycle/setup.py +7 -19
- package/scripts/wrapup-land.py +1292 -545
- package/src/consumer-migrations.json +1 -16
- package/src/lib/bundle.mjs +31 -1
- package/docs/adr/0001-consumer-divergence-policy.md +0 -53
- package/docs/adr/0002-capability-gated-orchestration.md +0 -70
- package/docs/adr/0003-kit-core-and-project-extension-lifecycle.md +0 -63
- package/docs/adr/0004-release-intent-is-a-version-tag.md +0 -64
- package/docs/adr/0005-to-issues-is-the-planning-facade.md +0 -42
- package/docs/adr/0006-routing-knowledge-access-and-policy-are-separate.md +0 -95
- package/docs/adr/0007-session-teardown-requires-provenance-bound-ownership.md +0 -88
- package/docs/adr/0008-planning-ignore-rules-are-offered-never-installed.md +0 -84
- package/docs/adr/0009-teardown-authority-is-stateless-repository-classification.md +0 -101
- package/docs/adr/0010-model-roster-replaces-the-optimization-dial.md +0 -87
- package/docs/agents/board-sync.md +0 -74
- package/docs/agents/code-review.md +0 -14
- package/docs/agents/workflow-capabilities.json +0 -28
- package/docs/research/agent-task-taxonomy-benchmark-coverage.md +0 -451
- package/docs/research/benchlm-routing-source.md +0 -198
- package/docs/research/consumer-owned-protocol-files.md +0 -238
- package/docs/research/frontend-agent-benchmarks.md +0 -282
- package/docs/research/model-effort-routing-benchmarks.md +0 -261
- package/docs/research/provider-neutral-agent-routing.md +0 -207
- package/docs/research/wave-152-consumer-acceptance.md +0 -98
- package/docs/research/wave-43-script-hook-census.md +0 -128
- package/scripts/build-kit.mjs +0 -117
- package/scripts/build-kit.test.mjs +0 -207
- package/scripts/census/census.test.mjs +0 -252
- package/scripts/census/state.test.mjs +0 -15
- package/scripts/census/transaction.test.mjs +0 -103
- package/scripts/census-contract.test.mjs +0 -227
- package/scripts/check-kit-staleness.mjs +0 -45
- package/scripts/check-kit-staleness.test.mjs +0 -19
- package/scripts/codex-exec-scenarios/fake-codex.mjs +0 -165
- package/scripts/codex-exec.test.mjs +0 -871
- package/scripts/grill-census-wiring-guard.mjs +0 -87
- package/scripts/grill-census-wiring-guard.test.mjs +0 -107
- package/scripts/kit-release.test.mjs +0 -273
- package/scripts/kit-update-pr.test.mjs +0 -151
- package/scripts/lib/audit-refs.mjs +0 -146
- package/scripts/lib/scrub.mjs +0 -113
- package/scripts/lib/scrub.test.mjs +0 -187
- package/scripts/memory-lifecycle/memory-lifecycle.test.mjs +0 -170
- package/scripts/portability_profile_scan.py +0 -53
- package/scripts/release-delta-guard.test.mjs +0 -174
- package/scripts/release-parity.test.mjs +0 -90
- package/scripts/release-state.test.mjs +0 -360
- package/scripts/test_anchor_table.py +0 -69
- package/scripts/test_board_bootstrap.py +0 -348
- package/scripts/test_board_sync.py +0 -208
- package/scripts/test_board_sync_create_idempotency.py +0 -117
- package/scripts/test_board_sync_wave_title.py +0 -120
- package/scripts/test_census_backstop.py +0 -712
- package/scripts/test_census_forward_contract.py +0 -91
- package/scripts/test_census_update_contract.test.mjs +0 -454
- package/scripts/test_codex_adapter_sync_contract.py +0 -169
- package/scripts/test_dist_kit_smoke.py +0 -220
- package/scripts/test_drift_guard_diagnostics.py +0 -295
- package/scripts/test_issue_claim_contract.py +0 -169
- package/scripts/test_kit_docs_language_census.py +0 -178
- package/scripts/test_marker_lib.py +0 -162
- package/scripts/test_orchestrate_wave_contract.py +0 -381
- package/scripts/test_pr_body_check.py +0 -264
- package/scripts/test_profile_globs.py +0 -280
- package/scripts/test_program_planning_contract.py +0 -133
- package/scripts/test_release_authorization_contract.py +0 -101
- package/scripts/test_render_anchor.py +0 -267
- package/scripts/test_retro_wrapup_contract.py +0 -153
- package/scripts/test_skill_code_review_seed.py +0 -89
- package/scripts/test_skill_codex_exec_lifecycle.py +0 -123
- package/scripts/test_skill_frontmatter_lint.py +0 -234
- package/scripts/test_skill_gh_lint.py +0 -124
- package/scripts/test_skill_language_census.py +0 -254
- package/scripts/test_skill_optional_readiness.py +0 -171
- package/scripts/test_skill_portability_lint.py +0 -866
- package/scripts/test_skill_precommit_template.py +0 -30
- package/scripts/test_skill_publish_audit.py +0 -358
- package/scripts/test_skill_readiness_contract.py +0 -264
- package/scripts/test_skill_readiness_preflight.py +0 -180
- package/scripts/test_skill_required_readiness.py +0 -233
- package/scripts/test_skill_selfcontainment_lint.py +0 -255
- package/scripts/test_skill_setup_workflow_seeds.py +0 -691
- package/scripts/test_skill_stale_name_lint.py +0 -126
- package/scripts/test_skill_surface_refs.py +0 -300
- package/scripts/test_skill_trailing_artifact_lint.py +0 -71
- package/scripts/test_tdd_contract.py +0 -78
- package/scripts/test_worktree_ignore_seed.py +0 -320
- package/scripts/test_worktree_setup_base_guard.py +0 -140
- package/scripts/test_worktree_wrapup_contract.py +0 -1737
- package/scripts/test_wrapup_land.py +0 -428
- package/scripts/worktree-lifecycle/session.py +0 -1857
|
@@ -5,7 +5,7 @@ The shipped skills write `PLAN.md`, `PLAN-REVIEW-LOG.md`, and `ANNAHMEN.md`
|
|
|
5
5
|
into a session worktree, but `.gitignore` is a consumer file the kit does not
|
|
6
6
|
own: `init` and `update` never touch it. This helper is the one place that
|
|
7
7
|
closes that gap, and it closes it only when a user explicitly approves the
|
|
8
|
-
offer inside `/setup-workflow
|
|
8
|
+
offer inside `/setup-workflow`.
|
|
9
9
|
|
|
10
10
|
Contract:
|
|
11
11
|
|
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
"""Worktree Lifecycle profile loading and low-level git operations.
|
|
1
|
+
"""Worktree Lifecycle profile loading and low-level git operations.
|
|
2
|
+
|
|
3
|
+
The profile carries **structural facts only** — worktree root, naming
|
|
4
|
+
templates, the protected branches, the setup sequence. Deletion policy has
|
|
5
|
+
exactly one configuration surface, the ignore mechanism, so no pattern list is
|
|
6
|
+
read here. Keys this loader does not know are ignored in silence: a profile
|
|
7
|
+
written for an older kit keeps working, and an obsolete key produces no warning
|
|
8
|
+
noise.
|
|
9
|
+
|
|
10
|
+
Two branch templates exist because two kinds of work land. `branchTemplate`
|
|
11
|
+
names the branch of an issue-anchored slice; `contentBranchTemplate` names the
|
|
12
|
+
issue-less branch a session cuts for durable content, so it renders `{type}`
|
|
13
|
+
and `{slug}` only and refuses `{issue}` outright rather than inventing a number.
|
|
14
|
+
|
|
15
|
+
`DEFAULT_MAIN_BRANCHES` is the single place in the kit that names an
|
|
16
|
+
integration branch at all; every command, test, and message resolves the name
|
|
17
|
+
through the profile instead of assuming it.
|
|
18
|
+
"""
|
|
2
19
|
|
|
3
20
|
from __future__ import annotations
|
|
4
21
|
|
|
@@ -9,6 +26,9 @@ from dataclasses import dataclass
|
|
|
9
26
|
from pathlib import Path
|
|
10
27
|
from typing import Any
|
|
11
28
|
|
|
29
|
+
DEFAULT_MAIN_BRANCHES = ("main", "master")
|
|
30
|
+
DEFAULT_CONTENT_BRANCH_TEMPLATE = "{type}/{slug}"
|
|
31
|
+
|
|
12
32
|
|
|
13
33
|
class LifecycleError(RuntimeError):
|
|
14
34
|
"""A safe, user-visible lifecycle refusal."""
|
|
@@ -18,6 +38,7 @@ class LifecycleError(RuntimeError):
|
|
|
18
38
|
class WorktreeProfile:
|
|
19
39
|
root: str
|
|
20
40
|
branch_template: str
|
|
41
|
+
content_branch_template: str
|
|
21
42
|
path_template: str
|
|
22
43
|
main_branches: tuple[str, ...]
|
|
23
44
|
protected_branches: tuple[str, ...]
|
|
@@ -25,15 +46,16 @@ class WorktreeProfile:
|
|
|
25
46
|
branch_regex: str
|
|
26
47
|
setup_entry: str
|
|
27
48
|
risky_command_patterns: tuple[str, ...]
|
|
28
|
-
scratch_patterns: tuple[str, ...]
|
|
29
|
-
landing_generated_artifact_policy_configured: bool
|
|
30
|
-
landing_generated_artifact_patterns: tuple[str, ...]
|
|
31
49
|
|
|
32
50
|
def branch_name(self, issue: str, slug: str, branch_type: str) -> str:
|
|
33
|
-
return _render(self.branch_template, issue, slug, branch_type)
|
|
51
|
+
return _render(self.branch_template, issue=issue, slug=slug, type=branch_type)
|
|
52
|
+
|
|
53
|
+
def content_branch_name(self, slug: str, branch_type: str) -> str:
|
|
54
|
+
"""Name the issue-less branch of a Content-route session."""
|
|
55
|
+
return render_content_branch(self.content_branch_template, slug, branch_type)
|
|
34
56
|
|
|
35
57
|
def relative_path(self, issue: str, slug: str, branch_type: str) -> Path:
|
|
36
|
-
name = _render(self.path_template, issue, slug, branch_type)
|
|
58
|
+
name = _render(self.path_template, issue=issue, slug=slug, type=branch_type)
|
|
37
59
|
return Path(self.root) / name
|
|
38
60
|
|
|
39
61
|
def issue_from_branch(self, branch: str) -> str | None:
|
|
@@ -41,14 +63,25 @@ class WorktreeProfile:
|
|
|
41
63
|
return match.groupdict().get("issue") if match else None
|
|
42
64
|
|
|
43
65
|
|
|
44
|
-
def _render(template: str,
|
|
45
|
-
|
|
66
|
+
def _render(template: str, **values: str) -> str:
|
|
67
|
+
"""Render a profile template from exactly the placeholders it may use."""
|
|
46
68
|
try:
|
|
47
69
|
return template.format(**values)
|
|
48
|
-
except (KeyError, ValueError) as error:
|
|
70
|
+
except (KeyError, IndexError, ValueError) as error:
|
|
49
71
|
raise LifecycleError(f"invalid worktree template: {error}") from error
|
|
50
72
|
|
|
51
73
|
|
|
74
|
+
def render_content_branch(template: str, slug: str, branch_type: str) -> str:
|
|
75
|
+
"""Render one issue-less content branch name — the placeholder policy's home.
|
|
76
|
+
|
|
77
|
+
A caller that reads the template out of a raw profile document (wrapup's
|
|
78
|
+
Content route does, because it must work whether or not the worktree
|
|
79
|
+
lifecycle itself is enabled) renders it here, so `{issue}` is refused in
|
|
80
|
+
exactly one place instead of two.
|
|
81
|
+
"""
|
|
82
|
+
return _render(template, slug=slug, type=branch_type)
|
|
83
|
+
|
|
84
|
+
|
|
52
85
|
def _load_profile_document(document: Any) -> WorktreeProfile:
|
|
53
86
|
try:
|
|
54
87
|
raw = document["worktreeLifecycle"]
|
|
@@ -56,26 +89,13 @@ def _load_profile_document(document: Any) -> WorktreeProfile:
|
|
|
56
89
|
raise LifecycleError(f"cannot load worktree lifecycle profile: {error}") from error
|
|
57
90
|
if raw.get("enabled") is not True:
|
|
58
91
|
raise LifecycleError("worktree lifecycle is not enabled")
|
|
59
|
-
|
|
60
|
-
if wrapup is None:
|
|
61
|
-
wrapup = {}
|
|
62
|
-
if not isinstance(wrapup, dict):
|
|
63
|
-
raise LifecycleError("invalid wrapup profile")
|
|
64
|
-
generated_policy_configured = "landingGeneratedArtifactPatterns" in wrapup
|
|
65
|
-
generated_patterns = (
|
|
66
|
-
wrapup["landingGeneratedArtifactPatterns"]
|
|
67
|
-
if generated_policy_configured
|
|
68
|
-
else ()
|
|
69
|
-
)
|
|
70
|
-
if (
|
|
71
|
-
not isinstance(generated_patterns, (list, tuple))
|
|
72
|
-
or not all(isinstance(pattern, str) and pattern for pattern in generated_patterns)
|
|
73
|
-
):
|
|
74
|
-
raise LifecycleError("invalid wrapup landingGeneratedArtifactPatterns")
|
|
75
|
-
main = tuple(raw.get("mainBranches") or ("main", "master"))
|
|
92
|
+
main = tuple(raw.get("mainBranches") or DEFAULT_MAIN_BRANCHES)
|
|
76
93
|
return WorktreeProfile(
|
|
77
94
|
root=raw.get("worktreeRoot", ".worktrees"),
|
|
78
95
|
branch_template=raw.get("branchTemplate", "{type}/{issue}-{slug}"),
|
|
96
|
+
content_branch_template=raw.get(
|
|
97
|
+
"contentBranchTemplate", DEFAULT_CONTENT_BRANCH_TEMPLATE,
|
|
98
|
+
),
|
|
79
99
|
path_template=raw.get("pathTemplate", "{type}-{issue}-{slug}"),
|
|
80
100
|
main_branches=main,
|
|
81
101
|
protected_branches=tuple(raw.get("protectedBranches") or main),
|
|
@@ -92,9 +112,6 @@ def _load_profile_document(document: Any) -> WorktreeProfile:
|
|
|
92
112
|
r"\b(?:npm|pnpm|yarn)\s+(?:run\s+)?(?:test|typecheck|build)\b",
|
|
93
113
|
r"\bgit\s+(?:commit|push)\b",
|
|
94
114
|
)),
|
|
95
|
-
scratch_patterns=tuple(raw.get("scratchPatterns") or ()),
|
|
96
|
-
landing_generated_artifact_policy_configured=generated_policy_configured,
|
|
97
|
-
landing_generated_artifact_patterns=tuple(generated_patterns),
|
|
98
115
|
)
|
|
99
116
|
|
|
100
117
|
|
|
@@ -10,9 +10,6 @@ import zlib
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
12
|
from core import (
|
|
13
|
-
BaselineBackfillDeferred,
|
|
14
|
-
capture_artifact_baseline,
|
|
15
|
-
ensure_artifact_baseline,
|
|
16
13
|
LifecycleError,
|
|
17
14
|
load_profile,
|
|
18
15
|
local_branch_exists,
|
|
@@ -25,7 +22,9 @@ from core import (
|
|
|
25
22
|
def parse_args() -> argparse.Namespace:
|
|
26
23
|
parser = argparse.ArgumentParser()
|
|
27
24
|
parser.add_argument("--profile", default="docs/agents/workflow-capabilities.json")
|
|
28
|
-
|
|
25
|
+
# Resolved from the consumer profile when omitted — the integration
|
|
26
|
+
# branch is never named inline.
|
|
27
|
+
parser.add_argument("--base")
|
|
29
28
|
parser.add_argument("issue")
|
|
30
29
|
parser.add_argument("slug")
|
|
31
30
|
parser.add_argument("branch_type", nargs="?", default="feat")
|
|
@@ -133,34 +132,24 @@ def create(args: argparse.Namespace) -> Path:
|
|
|
133
132
|
if not profile_path.is_absolute():
|
|
134
133
|
profile_path = main / profile_path
|
|
135
134
|
profile = load_profile(profile_path)
|
|
135
|
+
base = args.base or f"origin/{profile.main_branches[0]}"
|
|
136
136
|
branch = profile.branch_name(args.issue, args.slug, args.branch_type)
|
|
137
137
|
target = (main / profile.relative_path(args.issue, args.slug, args.branch_type)).resolve()
|
|
138
138
|
|
|
139
139
|
if target in registered_worktrees(main):
|
|
140
140
|
ensure_reusable_base(
|
|
141
|
-
main, repo=target, rev="HEAD", base=
|
|
141
|
+
main, repo=target, rev="HEAD", base=base, label=f"worktree {target}"
|
|
142
142
|
)
|
|
143
|
-
try:
|
|
144
|
-
ensure_artifact_baseline(
|
|
145
|
-
target,
|
|
146
|
-
reject_ignored_patterns=profile.landing_generated_artifact_patterns,
|
|
147
|
-
)
|
|
148
|
-
except BaselineBackfillDeferred as error:
|
|
149
|
-
print(
|
|
150
|
-
"Baseline backfill deferred; preserve the worktree, remove generated "
|
|
151
|
-
f"blockers or commit/stash tracked work, then retry: {error}",
|
|
152
|
-
file=sys.stderr,
|
|
153
|
-
)
|
|
154
143
|
print(f"Worktree already exists: {target} ({branch})")
|
|
155
144
|
return target
|
|
156
145
|
|
|
157
146
|
branch_existed = local_branch_exists(main, branch)
|
|
158
147
|
if branch_existed:
|
|
159
148
|
ensure_reusable_base(
|
|
160
|
-
main, repo=main, rev=branch, base=
|
|
149
|
+
main, repo=main, rev=branch, base=base, label=f"branch {branch}"
|
|
161
150
|
)
|
|
162
151
|
command = ["git", "worktree", "add", str(target)]
|
|
163
|
-
command += [branch] if branch_existed else ["-b", branch,
|
|
152
|
+
command += [branch] if branch_existed else ["-b", branch, base]
|
|
164
153
|
run(command, cwd=main)
|
|
165
154
|
try:
|
|
166
155
|
for step in profile.setup_steps:
|
|
@@ -171,7 +160,6 @@ def create(args: argparse.Namespace) -> Path:
|
|
|
171
160
|
issue=args.issue,
|
|
172
161
|
branch=branch,
|
|
173
162
|
)
|
|
174
|
-
capture_artifact_baseline(target)
|
|
175
163
|
except Exception:
|
|
176
164
|
remove_failed_worktree(main, target, branch, not branch_existed)
|
|
177
165
|
raise
|