@kody-ade/kody-engine 0.4.13 → 0.4.14

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.
@@ -41,34 +41,13 @@ for state_file in "${state_files[@]}"; do
41
41
  active=$((active + 1))
42
42
  echo "[goal-scheduler] → tick $goal_id"
43
43
 
44
- # Ensure the shared goal branch exists on origin before we tick. This is
45
- # the integration target for every task PR under this goal. Idempotent:
46
- # if origin/goal-<id> already exists we skip. We deliberately create from
47
- # the latest origin/<defaultBranch> so the goal branch starts from a
48
- # known-clean base; subsequent task PRs build on top.
49
- goal_branch="goal-${goal_id}"
50
- default_branch="${KODY_CFG_GIT_DEFAULTBRANCH:-main}"
51
-
52
- # Best-effort fetch so origin refs are fresh.
53
- git fetch origin --quiet 2>/dev/null || true
54
-
55
- if git rev-parse --verify --quiet "refs/remotes/origin/${goal_branch}" >/dev/null 2>&1; then
56
- echo "[goal-scheduler] origin/${goal_branch} already exists — leaving as-is"
57
- else
58
- if ! git rev-parse --verify --quiet "refs/remotes/origin/${default_branch}" >/dev/null 2>&1; then
59
- echo "[goal-scheduler] cannot create goal branch: origin/${default_branch} missing"
60
- else
61
- echo "[goal-scheduler] creating origin/${goal_branch} from origin/${default_branch}"
62
- # Push a new ref directly without checking it out — avoids touching the
63
- # working tree, which other ticks/scripts in this same scheduler run
64
- # may rely on. Failures here are logged and we proceed; goal-tick will
65
- # still dispatch task issues, and ensureFeatureBranch will fall back to
66
- # forking from defaultBranch when origin/goal-<id> is absent.
67
- if ! git push origin "refs/remotes/origin/${default_branch}:refs/heads/${goal_branch}" --quiet 2>&1; then
68
- echo "[goal-scheduler] push of ${goal_branch} failed (will retry next tick)"
69
- fi
70
- fi
71
- fi
44
+ # NOTE: the shared goal branch is created lazily by goal-tick at the moment
45
+ # it's about to dispatch the first task. Goals whose ticks never dispatch
46
+ # (e.g. all tasks closed as won't-fix, or every task carries goal-runner:failed)
47
+ # never spawn an orphan goal-<id> ref on origin. The trade-off vs. the prior
48
+ # eager creation: the goal branch's base is "origin/<defaultBranch> at first
49
+ # dispatch" rather than "origin/<defaultBranch> at goal activation", which
50
+ # is better for short-lived QA-style goals where main may have moved on.
72
51
 
73
52
  # Run the tick. Top-level kody invocation is `kody <executable>` —
74
53
  # there's no `dispatch` subcommand. A non-zero exit logs and continues
@@ -266,6 +266,26 @@ if [ -z "$next_issue" ]; then
266
266
  exit 0
267
267
  fi
268
268
 
269
+ # Lazy goal-branch creation: only spin up origin/goal-<id> at the moment we're
270
+ # about to dispatch the first task. Goals whose ticks never dispatch (every
271
+ # task closed as won't-fix, or every open task already has goal-runner:failed)
272
+ # never produce an orphan branch on origin. Idempotent: if origin/goal-<id>
273
+ # already exists we skip. Failure here is non-fatal — ensureFeatureBranch in
274
+ # the dispatched run falls back to forking from defaultBranch.
275
+ git fetch origin --quiet 2>/dev/null || true
276
+ if git rev-parse --verify --quiet "refs/remotes/origin/${goal_branch}" >/dev/null 2>&1; then
277
+ echo "[goal-tick] origin/${goal_branch} already exists — leaving as-is"
278
+ else
279
+ if ! git rev-parse --verify --quiet "refs/remotes/origin/${default_branch}" >/dev/null 2>&1; then
280
+ echo "[goal-tick] cannot create goal branch: origin/${default_branch} missing"
281
+ else
282
+ echo "[goal-tick] creating origin/${goal_branch} from origin/${default_branch}"
283
+ if ! git push origin "refs/remotes/origin/${default_branch}:refs/heads/${goal_branch}" --quiet 2>&1; then
284
+ echo "[goal-tick] push of ${goal_branch} failed — task dispatch will fall back to defaultBranch"
285
+ fi
286
+ fi
287
+ fi
288
+
269
289
  echo "[goal-tick] dispatching @kody on task #$next_issue (--base $goal_branch)"
270
290
  gh issue comment "$next_issue" --body "@kody --base ${goal_branch}"
271
291
  gh issue edit "$next_issue" --add-label "$dispatched_label"
@@ -8,8 +8,8 @@
8
8
  "name": "url",
9
9
  "flag": "--url",
10
10
  "type": "string",
11
- "required": true,
12
- "describe": "Base URL the agent should browse (e.g. http://localhost:3000)."
11
+ "required": false,
12
+ "describe": "Base URL the agent should browse. Optional — resolveQaUrl preflight falls back to the goal-branch Vercel deployment (when --goal is set), then $PREVIEW_URL, then qa.fallbackUrl from kody.config.json. Errors if none resolve."
13
13
  },
14
14
  {
15
15
  "name": "scope",
@@ -18,6 +18,13 @@
18
18
  "required": false,
19
19
  "describe": "Optional feature focus (e.g. 'admin chat memory recall'). Without a scope the agent does a broad smoke pass over discovered routes."
20
20
  },
21
+ {
22
+ "name": "goal",
23
+ "flag": "--goal",
24
+ "type": "string",
25
+ "required": false,
26
+ "describe": "Optional kody goal id to attach findings to. When set: (1) resolveQaUrl looks up the goal-branch's latest Vercel deployment and uses its URL, (2) createQaGoal skips manifest creation and labels finding issues `goal:<id>` directly."
27
+ },
21
28
  {
22
29
  "name": "issue",
23
30
  "flag": "--issue",
@@ -57,7 +64,7 @@
57
64
  {
58
65
  "name": "playwright",
59
66
  "command": "npx",
60
- "args": ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp"]
67
+ "args": ["-y", "--package=@playwright/mcp@latest", "--", "playwright-mcp", "--headless"]
61
68
  }
62
69
  ]
63
70
  },
@@ -78,6 +85,7 @@
78
85
  "outputArtifacts": [],
79
86
  "scripts": {
80
87
  "preflight": [
88
+ { "script": "resolveQaUrl" },
81
89
  { "script": "discoverQaContext" },
82
90
  { "script": "loadQaGuide" },
83
91
  { "script": "loadConventions" },
@@ -85,7 +93,7 @@
85
93
  { "script": "composePrompt" }
86
94
  ],
87
95
  "postflight": [
88
- { "script": "openQaIssue" }
96
+ { "script": "createQaGoal" }
89
97
  ]
90
98
  }
91
99
  }
@@ -4,13 +4,13 @@ You may write throwaway artifacts (screenshots, ad-hoc Playwright specs) under `
4
4
 
5
5
  # Target
6
6
 
7
- Base URL: `{{args.url}}`
7
+ Base URL: `{{previewUrl}}` (resolved from: {{previewUrlSource}})
8
8
  {{#args.scope}}Focus: **{{args.scope}}**{{/args.scope}}
9
9
  {{^args.scope}}Focus: broad smoke across discovered routes.{{/args.scope}}
10
10
  {{#args.authProfile}}Auth: a saved Playwright `storageState.json` is available at `{{args.authProfile}}`. Pass it to `mcp__playwright__browser_navigate` via the `storageState` parameter so the session starts pre-authenticated.{{/args.authProfile}}
11
11
  {{^args.authProfile}}Auth: log in fresh using credentials from the QA guide if needed.{{/args.authProfile}}
12
12
 
13
- Report destination: {{#args.issue}}existing issue #{{args.issue}} (postflight will comment on it){{/args.issue}}{{^args.issue}}a new issue (postflight will open one and label it `kody:qa-report`){{/args.issue}}.
13
+ Report destination: {{#args.goal}}existing kody goal `{{args.goal}}` (each finding becomes a `goal:{{args.goal}}` task issue){{/args.goal}}{{^args.goal}}{{#args.issue}}existing issue #{{args.issue}} (postflight will comment on it){{/args.issue}}{{^args.issue}}a new kody goal (postflight will append to the goals manifest and open one task issue per finding){{/args.issue}}{{/args.goal}}.
14
14
 
15
15
  # How to browse
16
16
 
@@ -19,7 +19,7 @@ You have the **Playwright MCP** tools (`mcp__playwright__browser_navigate`, `mcp
19
19
  Before anything else, navigate to the base URL:
20
20
 
21
21
  ```
22
- mcp__playwright__browser_navigate({ url: "{{args.url}}" })
22
+ mcp__playwright__browser_navigate({ url: "{{previewUrl}}" })
23
23
  ```
24
24
 
25
25
  If that errors (timeout, DNS, connection refused), the app is unreachable. STOP browsing, write a short report explaining the failure, and exit. Don't fabricate findings.
@@ -63,7 +63,7 @@ If that errors (timeout, DNS, connection refused), the app is unreachable. STOP
63
63
  ```
64
64
  ## Verdict: PASS | CONCERNS | FAIL
65
65
 
66
- _QA by kody — browsed `{{args.url}}`{{#args.scope}} (focus: {{args.scope}}){{/args.scope}}_
66
+ _QA by kody — browsed `{{previewUrl}}`{{#args.scope}} (focus: {{args.scope}}){{/args.scope}}_
67
67
 
68
68
  ### Summary
69
69
  <2–3 sentences: what you covered and what the running app actually does>
@@ -86,8 +86,40 @@ _QA by kody — browsed `{{args.url}}`{{#args.scope}} (focus: {{args.scope}}){{/
86
86
 
87
87
  ### Bottom line
88
88
  <one sentence>
89
+
90
+ <!-- KODY_QA_REPORT_JSON
91
+ ```json
92
+ {
93
+ "findings": [
94
+ {
95
+ "severity": "P1",
96
+ "title": "Short imperative title — what's broken",
97
+ "route": "/admin/...",
98
+ "steps": "1. Step one\n2. Step two\n3. Step three",
99
+ "expected": "What should happen",
100
+ "actual": "What actually happens. Cite console errors / API responses / screenshots.",
101
+ "evidence": ".kody/qa-reports/<scope>/<finding>.png"
102
+ }
103
+ ]
104
+ }
105
+ ```
106
+ -->
89
107
  ```
90
108
 
109
+ # Required: structured findings block
110
+
111
+ After the "Bottom line" section, you MUST emit one machine-readable block exactly as shown in the template above. The postflight uses it to open one severity-labelled GitHub issue per finding.
112
+
113
+ Rules for the JSON block:
114
+ - Every finding listed in the human-readable "Findings" section above MUST appear in the JSON `findings` array. No more, no fewer.
115
+ - `severity` is one of `"P0"`, `"P1"`, `"P2"`, `"P3"` — must match the prefix in the markdown.
116
+ - `title` is a concise imperative (5–12 words). It becomes the issue title — no `[Pn]` prefix here, the postflight adds it.
117
+ - `steps`, `expected`, `actual` are required. `route` and `evidence` are optional but include them when applicable.
118
+ - Use `\n` literal newlines inside string values (the JSON parser will handle them).
119
+ - If you found zero defects (verdict PASS), emit `{"findings": []}`.
120
+
121
+ If you don't include this block, the postflight falls back to opening a single record-style issue. That's acceptable when there are zero findings, but for any run with defects the block is mandatory — without it, individual findings won't get triageable tickets.
122
+
91
123
  # Severity rubric
92
124
 
93
125
  - **P0** — blocks core flow, data loss, security exposure, total breakage on a critical path. Verdict must be FAIL if any P0 lands.
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -396,6 +396,17 @@
396
396
  },
397
397
  "additionalProperties": false
398
398
  },
399
+ "qa": {
400
+ "type": "object",
401
+ "description": "qa-engineer defaults. Used by the resolveQaUrl preflight when no explicit --url is passed and no $PREVIEW_URL env var is set.",
402
+ "properties": {
403
+ "fallbackUrl": {
404
+ "type": "string",
405
+ "description": "Fallback URL the qa-engineer should target. Typically the project's stable dev environment (e.g. 'https://dev.example.com'). No localhost defaults — CI has no localhost to fall back to."
406
+ }
407
+ },
408
+ "additionalProperties": false
409
+ },
399
410
  "devServer": {
400
411
  "type": "object",
401
412
  "description": "Dev server configuration for browser tool verification. Works with any provider (MCP or CLI-based).",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.13",
3
+ "version": "0.4.14",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,6 +12,18 @@
12
12
  "templates",
13
13
  "kody.config.schema.json"
14
14
  ],
15
+ "scripts": {
16
+ "kody": "tsx bin/kody.ts",
17
+ "build": "tsup && node scripts/copy-assets.cjs",
18
+ "test": "vitest run tests/unit tests/int --no-coverage",
19
+ "test:e2e": "vitest run tests/e2e --no-coverage",
20
+ "test:all": "vitest run tests --no-coverage",
21
+ "typecheck": "tsc --noEmit",
22
+ "lint": "biome check",
23
+ "lint:fix": "biome check --write",
24
+ "format": "biome format --write",
25
+ "prepublishOnly": "pnpm build"
26
+ },
15
27
  "dependencies": {
16
28
  "@actions/cache": "^6.0.0",
17
29
  "@anthropic-ai/claude-agent-sdk": "0.2.119"
@@ -32,16 +44,5 @@
32
44
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
33
45
  },
34
46
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
35
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
36
- "scripts": {
37
- "kody": "tsx bin/kody.ts",
38
- "build": "tsup && node scripts/copy-assets.cjs",
39
- "test": "vitest run tests/unit tests/int --no-coverage",
40
- "test:e2e": "vitest run tests/e2e --no-coverage",
41
- "test:all": "vitest run tests --no-coverage",
42
- "typecheck": "tsc --noEmit",
43
- "lint": "biome check",
44
- "lint:fix": "biome check --write",
45
- "format": "biome format --write"
46
- }
47
- }
47
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
48
+ }