Skip to content

Commit c8f9133

Browse files
dwinter3ai-jerry-i-am-your-fatherclaude
authored
ci: add consolidated CodeBuild buildspec for lint+test (#265)
* ci: add consolidated CodeBuild buildspec for lint+test Adds ci/buildspecs/ci.yml — a single buildspec that runs lint + test for every component (cli, server, plugin, schema, sdk-go, sdk-python, install, licenses, prompts-sync) in sequence. This is the CodeBuild replacement for the nine .github/workflows/ci-*.yaml workflows, run by the new consolidated CodeBuild project `8th-layer-agent-ci`. One pass/fail PR check instead of nine. Granular per-component splitting is deliberately out of scope for a fork this size — on failure the build log names the failing component. Toolchains are installed explicitly in the install phase so the build is reproducible regardless of the base image: Go 1.26.1 (all go.mod files pin it, newer than amazonlinux-standard:5.0 ships), golangci-lint v2.10.1, uv, and Node 22 + pnpm 10. The 9 ci-*.yaml workflows are removed in a follow-up PR once a green CodeBuild run confirms parity. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * ci: quote buildspec command scalars to fix YAML parse error CodeBuild's buildspec YAML parser rejected the first build with "did not find expected key at line 65" — the `echo "::: [component] ..."` log markers start with a colon, which an unquoted YAML scalar reads as a mapping key. Single-quote every command list item so each is an unambiguous scalar, and drop the in-sequence divider comments (the echo markers already label each component). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: sync SDK prompt copies with canonical plugin sources The first consolidated CodeBuild CI run surfaced pre-existing drift: `sdk/go/prompts/{SKILL,reflect}.md` and `sdk/python/src/cq/prompts/{SKILL,reflect}.md` were out of sync with their canonical sources under `plugins/cq/`. The plugin sources document the `propose_batch` reflect flow but the SDK copies still described the older per-candidate `propose` flow — the `propose_batch` change updated the plugin without re-running `make sync-prompts` in the SDKs. The GHA `ci-prompts-sync.yaml` only fires on paths touching these files, so the drift slipped through. Ran `make sync-prompts` to bring all four copies back in line. This unblocks the consolidated CI build's first check (`check-prompts-sync`). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: David Winter <dw@choruscap.ai> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent a0c7eaa commit c8f9133

5 files changed

Lines changed: 181 additions & 4 deletions

File tree

ci/buildspecs/ci.yml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
version: 0.2
2+
3+
# Consolidated CI for 8th-layer-agent — runs lint + test for every
4+
# component in one CodeBuild project (`8th-layer-agent-ci`,
5+
# account 124074140789, us-east-1). One pass/fail PR check.
6+
#
7+
# This replaces the nine `.github/workflows/ci-*.yaml` GitHub Actions
8+
# workflows (ci-cli, ci-install, ci-licenses, ci-plugin, ci-prompts-sync,
9+
# ci-schema, ci-sdk-go, ci-sdk-python, ci-server). Granular 9-way
10+
# splitting is deliberately out of scope — for a fork this size one
11+
# consolidated check is simpler to operate. If a check fails, the build
12+
# log names the failing component.
13+
#
14+
# Triggers (PR events + push to main) are encoded on the CodeBuild
15+
# project webhook FilterGroups, not here.
16+
#
17+
# Toolchains installed explicitly so the build is reproducible regardless
18+
# of the base image:
19+
# - Go 1.26.1 — cli/go.mod, schema/go.mod, sdk/go/go.mod all pin
20+
# 1.26.1, newer than the amazonlinux-standard:5.0 image
21+
# ships. Same curl-tarball pattern as cli-release.yml.
22+
# - golangci-lint v2.10.1 — pinned to match the GHA golangci-lint-action.
23+
# - uv — Python toolchain (astral-sh/setup-uv@v7 analog).
24+
# - Node 22 + pnpm 10 — server frontend (setup-node + pnpm/action-setup).
25+
#
26+
# `set -e` is in effect for every build command (CodeBuild default), so a
27+
# failing check fails the build. Components run in sequence; the first
28+
# failure aborts the build and the rest are skipped — the log shows which
29+
# component failed.
30+
31+
env:
32+
variables:
33+
GO_VERSION: "1.26.1"
34+
GOLANGCI_LINT_VERSION: "v2.10.1"
35+
NODE_VERSION: "22"
36+
PNPM_VERSION: "10"
37+
UV_NO_PROGRESS: "1"
38+
UV_CACHE_DIR: /codebuild/output/.uv-cache
39+
40+
phases:
41+
install:
42+
commands:
43+
- 'echo "=== Installing Go ${GO_VERSION} ==="'
44+
- 'curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar -C /usr/local -xz'
45+
- 'export PATH="/usr/local/go/bin:$(go env GOPATH 2>/dev/null || echo $HOME/go)/bin:${PATH}"'
46+
- 'go version'
47+
- 'echo "=== Installing golangci-lint ${GOLANGCI_LINT_VERSION} ==="'
48+
- 'curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b /usr/local/bin "${GOLANGCI_LINT_VERSION}"'
49+
- 'golangci-lint --version'
50+
- 'echo "=== Installing uv (astral-sh/setup-uv@v7 analog) ==="'
51+
- 'curl -LsSf https://astral.sh/uv/install.sh | sh'
52+
- 'export PATH="$HOME/.local/bin:${PATH}"'
53+
- 'uv --version'
54+
- 'echo "=== Installing Node ${NODE_VERSION} + pnpm ${PNPM_VERSION} ==="'
55+
- 'n ${NODE_VERSION} || (curl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n | bash -s ${NODE_VERSION})'
56+
- 'node --version'
57+
- 'corepack enable'
58+
- 'corepack prepare "pnpm@${PNPM_VERSION}" --activate'
59+
- 'pnpm --version'
60+
61+
build:
62+
commands:
63+
- 'export PATH="/usr/local/go/bin:$(go env GOPATH 2>/dev/null || echo $HOME/go)/bin:$HOME/.local/bin:${PATH}"'
64+
- 'echo "::: [prompts-sync] verifying SDK prompt copies match plugin sources"'
65+
- 'make check-prompts-sync'
66+
- 'echo "::: [schema] validate JSON Schema fixtures + values"'
67+
- 'make validate-schema'
68+
- 'echo "::: [schema] lint + test (Go module + Python package)"'
69+
- 'make setup-schema'
70+
- 'make lint-schema'
71+
- 'make test-schema'
72+
- 'echo "::: [cli] setup + lint (golangci-lint, check-licenses, check-notice)"'
73+
- 'make setup-cli'
74+
- 'make lint-cli'
75+
- 'echo "::: [cli] test + build"'
76+
- 'make test-cli'
77+
- '(cd cli && make build)'
78+
- 'echo "::: [sdk-go] setup + lint (prompts-sync, golangci-lint, licenses, notice)"'
79+
- 'make setup-sdk-go'
80+
- 'make lint-sdk-go'
81+
- 'echo "::: [sdk-go] test"'
82+
- 'make test-sdk-go'
83+
- 'echo "::: [licenses] covered by lint-cli + lint-sdk-go (check-licenses/check-notice)"'
84+
- 'echo "::: [plugin] setup + lint"'
85+
- 'make setup-plugin'
86+
- 'make lint-plugin'
87+
- 'echo "::: [plugin] test (Cursor hook helper)"'
88+
- 'make test-plugin'
89+
- 'echo "::: [install] setup + lint + test (installer + plugin hook)"'
90+
- 'make setup-install'
91+
- 'make lint-install'
92+
- 'make test-install'
93+
- 'echo "::: [sdk-python] setup + lint + test"'
94+
- 'make setup-sdk-python'
95+
- 'make lint-sdk-python'
96+
- 'make test-sdk-python'
97+
- 'echo "::: [server] setup + lint + test (backend + frontend)"'
98+
- 'make setup-server'
99+
- 'make lint-server'
100+
- 'make test-server'
101+
- 'echo "=== All CI checks passed ==="'
102+
cache:
103+
paths:
104+
- '/codebuild/output/.uv-cache/**/*'
105+
- '/root/.cache/uv/**/*'
106+
- '/root/.cache/go-build/**/*'
107+
- '/root/go/pkg/mod/**/*'

sdk/go/prompts/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ If no coherent lesson survives sanitization across all affected fields, the cand
169169
#### Applying VIBE√
170170

171171
- **Direct `propose` calls** (outside `/cq:reflect`) — run the check on the single candidate. If a hard finding exists, present both the original and the sanitized rewrite to the user and let them pick (or skip). If only a soft concern exists, present the concern for awareness before proceeding.
172-
- **Batch proposals via `/cq:reflect`**see the `/cq:reflect` command for the batch presentation UX (three templates, provenance annotation). The underlying V/I/B/E classification rules are the same.
172+
- **Batch proposals via `/cq:reflect`**clean and soft candidates are sent in a single `propose_batch` call to keep the harness's tool-output noise bounded; hard findings still go through per-candidate `propose` after inline review. See the `/cq:reflect` command for the full batch presentation UX (three templates, provenance annotation). The underlying V/I/B/E classification rules are the same.
173173

174174
### Confirming Knowledge (`confirm`)
175175

sdk/go/prompts/reflect.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,33 @@ If a hard finding cannot be coherently sanitized, the candidate fails Step 2's g
7575

7676
#### Auto-propose phase (Step 3a — silent)
7777

78-
For each candidate classified as `clean` or `soft` in Step 2.5, call `propose` immediately without presenting. Soft concerns are not lost — they are listed in the Step 6 final summary so the operator can see what flags were raised.
78+
Collect every candidate classified as `clean` or `soft` in Step 2.5 into a single list, then call **`propose_batch`** ONCE with that list. The `propose_batch` tool stores all candidates in one MCP round-trip and returns one combined response, capping the harness's tool-call echo at a single tool invocation regardless of candidate count.
79+
80+
```
81+
propose_batch(
82+
candidates=[
83+
{summary: ..., detail: ..., action: ..., domains: [...], languages?, frameworks?, pattern?},
84+
...
85+
]
86+
)
87+
```
88+
89+
Use the per-candidate `propose` tool only for hard findings (Step 5), where each candidate needs human judgment and earns its own tool call.
90+
91+
The batch response shape:
92+
93+
```json
94+
{
95+
"stored": [{"index": 0, "id": "ku_...", "summary": "...", "tier": "private"}, ...],
96+
"errors": [{"index": 2, "summary": "...", "error": "..."}]
97+
}
98+
```
99+
100+
`index` is the candidate's original position in the input list. Carry the `clean`/`soft` classification per index from Step 2.5 so Step 6 can render the correct provenance annotation against each stored ID.
101+
102+
If `candidates` is empty (no clean or soft candidates this session), skip the `propose_batch` call entirely.
103+
104+
Soft concerns are not lost — they are listed in the Step 6 final summary so the operator can see what flags were raised.
79105

80106
Do NOT auto-propose hard findings, even with sanitization. Hard findings always require human judgment because the sanitized rewrite may have stripped content the operator cares about, or the operator may want to escalate the finding rather than store either form.
81107

@@ -186,6 +212,15 @@ IDs stored this session:
186212

187213
Always show the `{total} candidates identified.` line. Omit any line whose count is zero. Omit any VIBE√ findings bullet whose category has no entries.
188214

215+
Render the `IDs stored this session` list by combining the `propose_batch` response (auto-stored clean+soft candidates) with the per-candidate `propose` results from Step 5 (hard findings). For each entry in the batch response's `stored` array, look up the original `clean` or `soft` classification by `index` and use it as the bracketed annotation. If the batch response includes any `errors` entries, list them as a final bullet:
216+
217+
```
218+
Errors during batch store:
219+
- index {n} ("{summary}"): {error}
220+
```
221+
222+
Errors do not block the summary — surface them so the operator knows which candidates fell out, then move on.
223+
189224
The bracketed annotation on each stored ID records the VIBE√ provenance of what was stored:
190225

191226
- `clean` — no VIBE√ findings; auto-stored without prompting.

sdk/python/src/cq/prompts/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ If no coherent lesson survives sanitization across all affected fields, the cand
169169
#### Applying VIBE√
170170

171171
- **Direct `propose` calls** (outside `/cq:reflect`) — run the check on the single candidate. If a hard finding exists, present both the original and the sanitized rewrite to the user and let them pick (or skip). If only a soft concern exists, present the concern for awareness before proceeding.
172-
- **Batch proposals via `/cq:reflect`**see the `/cq:reflect` command for the batch presentation UX (three templates, provenance annotation). The underlying V/I/B/E classification rules are the same.
172+
- **Batch proposals via `/cq:reflect`**clean and soft candidates are sent in a single `propose_batch` call to keep the harness's tool-output noise bounded; hard findings still go through per-candidate `propose` after inline review. See the `/cq:reflect` command for the full batch presentation UX (three templates, provenance annotation). The underlying V/I/B/E classification rules are the same.
173173

174174
### Confirming Knowledge (`confirm`)
175175

sdk/python/src/cq/prompts/reflect.md

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,33 @@ If a hard finding cannot be coherently sanitized, the candidate fails Step 2's g
7575

7676
#### Auto-propose phase (Step 3a — silent)
7777

78-
For each candidate classified as `clean` or `soft` in Step 2.5, call `propose` immediately without presenting. Soft concerns are not lost — they are listed in the Step 6 final summary so the operator can see what flags were raised.
78+
Collect every candidate classified as `clean` or `soft` in Step 2.5 into a single list, then call **`propose_batch`** ONCE with that list. The `propose_batch` tool stores all candidates in one MCP round-trip and returns one combined response, capping the harness's tool-call echo at a single tool invocation regardless of candidate count.
79+
80+
```
81+
propose_batch(
82+
candidates=[
83+
{summary: ..., detail: ..., action: ..., domains: [...], languages?, frameworks?, pattern?},
84+
...
85+
]
86+
)
87+
```
88+
89+
Use the per-candidate `propose` tool only for hard findings (Step 5), where each candidate needs human judgment and earns its own tool call.
90+
91+
The batch response shape:
92+
93+
```json
94+
{
95+
"stored": [{"index": 0, "id": "ku_...", "summary": "...", "tier": "private"}, ...],
96+
"errors": [{"index": 2, "summary": "...", "error": "..."}]
97+
}
98+
```
99+
100+
`index` is the candidate's original position in the input list. Carry the `clean`/`soft` classification per index from Step 2.5 so Step 6 can render the correct provenance annotation against each stored ID.
101+
102+
If `candidates` is empty (no clean or soft candidates this session), skip the `propose_batch` call entirely.
103+
104+
Soft concerns are not lost — they are listed in the Step 6 final summary so the operator can see what flags were raised.
79105

80106
Do NOT auto-propose hard findings, even with sanitization. Hard findings always require human judgment because the sanitized rewrite may have stripped content the operator cares about, or the operator may want to escalate the finding rather than store either form.
81107

@@ -186,6 +212,15 @@ IDs stored this session:
186212

187213
Always show the `{total} candidates identified.` line. Omit any line whose count is zero. Omit any VIBE√ findings bullet whose category has no entries.
188214

215+
Render the `IDs stored this session` list by combining the `propose_batch` response (auto-stored clean+soft candidates) with the per-candidate `propose` results from Step 5 (hard findings). For each entry in the batch response's `stored` array, look up the original `clean` or `soft` classification by `index` and use it as the bracketed annotation. If the batch response includes any `errors` entries, list them as a final bullet:
216+
217+
```
218+
Errors during batch store:
219+
- index {n} ("{summary}"): {error}
220+
```
221+
222+
Errors do not block the summary — surface them so the operator knows which candidates fell out, then move on.
223+
189224
The bracketed annotation on each stored ID records the VIBE√ provenance of what was stored:
190225

191226
- `clean` — no VIBE√ findings; auto-stored without prompting.

0 commit comments

Comments
 (0)