codex-harness-engineering 0.1.5 → 0.1.6
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.md +18 -6
- package/LICENSE +21 -0
- package/README.md +69 -6
- package/docs/harness-engineering/implementation-playbook.md +232 -286
- package/docs/harness-engineering/index.md +7 -4
- package/docs/harness-engineering/research-note.md +294 -274
- package/docs/harness-engineering/sources.md +166 -72
- package/package.json +5 -4
- package/scripts/install-skills.mjs +73 -15
- package/scripts/publish.sh +2 -2
- package/scripts/verify-harness.mjs +61 -4
- package/skills/acceptance-contract/SKILL.md +39 -49
- package/skills/acceptance-contract/agents/openai.yaml +2 -2
- package/skills/cleanup-harness/SKILL.md +48 -59
- package/skills/cleanup-harness/agents/openai.yaml +2 -2
- package/skills/creator-harness/SKILL.md +79 -95
- package/skills/creator-harness/agents/openai.yaml +2 -2
- package/skills/creator-harness/references/harness-artifacts.md +63 -62
- package/skills/lessons-harness/SKILL.md +68 -0
- package/skills/lessons-harness/agents/openai.yaml +4 -0
- package/templates/harness/AGENTS.md +77 -0
- package/templates/harness/feature_list.json +16 -0
- package/templates/harness/init.sh +15 -0
- package/templates/harness/lessons.md +18 -0
- package/templates/harness/memory/README.md +22 -0
- package/templates/harness/progress.md +33 -0
- package/templates/harness/rotate-state.mjs +131 -0
- package/templates/harness/verify-state.mjs +117 -0
- package/templates/team/roles/evaluator.md +43 -0
- package/templates/team/roles/implementer.md +29 -0
- package/templates/team/roles/planner.md +28 -0
- package/templates/team/sprint-template.md +36 -0
- package/templates/team/verify-team.mjs +71 -0
- package/templates/team/workflow.md +62 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Nguồn
|
|
2
2
|
|
|
3
|
-
File này
|
|
4
|
-
Không thêm claim vào tài liệu nếu claim đó không ánh xạ tới một trong
|
|
3
|
+
File này theo dõi năm nguồn được phép dùng trong bản nghiên cứu hiện tại.
|
|
4
|
+
Không thêm claim vào tài liệu nếu claim đó không ánh xạ tới một trong năm nguồn
|
|
5
5
|
này hoặc không được đánh dấu rõ là diễn giải.
|
|
6
6
|
|
|
7
7
|
## Danh mục nguồn
|
|
@@ -9,23 +9,75 @@ này hoặc không được đánh dấu rõ là diễn giải.
|
|
|
9
9
|
### [S1] OpenAI
|
|
10
10
|
|
|
11
11
|
- Tiêu đề: "Harness engineering: leveraging Codex in an agent-first world"
|
|
12
|
-
- Tác giả: Ryan Lopopolo
|
|
13
|
-
- Ngày xuất bản: 11 tháng 2, 2026
|
|
12
|
+
- Tác giả: Ryan Lopopolo (Member of the Technical Staff)
|
|
13
|
+
- Ngày xuất bản: 11 tháng 2, 2026 (bản clipping nội bộ ghi published 2026-05-27;
|
|
14
|
+
giữ Feb 2026 theo trích dẫn gốc — xem ghi chú cuối mục)
|
|
14
15
|
- URL: https://openai.com/index/harness-engineering/
|
|
16
|
+
- Bối cảnh: thí nghiệm 5 tháng (commit đầu cuối tháng 8/2025), team lái Codex để
|
|
17
|
+
ship một sản phẩm beta nội bộ ~1 triệu dòng code với **0 dòng code viết tay**;
|
|
18
|
+
~1.500 PR đã merge; team từ 3 lên 7 kỹ sư, throughput ~3.5 PR/kỹ sư/ngày và
|
|
19
|
+
*tăng* khi team lớn lên; ước tính nhanh hơn ~10 lần so với viết tay.
|
|
15
20
|
- Dùng cho:
|
|
16
|
-
- framing "humans steer, agents execute"
|
|
21
|
+
- framing "humans steer, agents execute": con người prioritize, dịch feedback
|
|
22
|
+
thành acceptance criteria, validate kết quả; khi agent vướng, hỏi "thiếu
|
|
23
|
+
capability nào và làm sao để agent thấy được + cưỡng chế được", rồi để chính
|
|
24
|
+
Codex viết bản sửa — không bao giờ tự sửa code tay;
|
|
17
25
|
- harness engineering như thiết kế environment, intent specification, và
|
|
18
|
-
feedback loop quanh Codex;
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- application
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
26
|
+
feedback loop quanh Codex; làm depth-first: chẻ goal lớn thành building block
|
|
27
|
+
(design, code, review, test) để mở khóa task phức tạp hơn;
|
|
28
|
+
- vòng lặp PR kiểu "Ralph Wiggum": Codex tự review local, xin thêm agent review
|
|
29
|
+
local + cloud, phản hồi feedback, lặp tới khi mọi reviewer hài lòng; review
|
|
30
|
+
đẩy gần như hoàn toàn sang agent-to-agent, human review không bắt buộc;
|
|
31
|
+
- application legibility: app bootable theo từng git worktree; nối Chrome
|
|
32
|
+
DevTools Protocol vào agent runtime + skill cho DOM snapshot/screenshot/
|
|
33
|
+
navigation để Codex tái hiện bug và validate fix; observability stack ephemeral
|
|
34
|
+
theo worktree, query log bằng LogQL và metric bằng PromQL, làm prompt kiểu
|
|
35
|
+
"service startup < 800ms" hay "không span nào > 2s" trở nên tractable;
|
|
36
|
+
- repository knowledge base có cấu trúc (`docs/` với design-docs, exec-plans,
|
|
37
|
+
product-specs, references llms.txt, ARCHITECTURE/QUALITY_SCORE/RELIABILITY…)
|
|
38
|
+
là system of record; plan là artifact hạng nhất, exec-plan kèm progress +
|
|
39
|
+
decision log được check-in;
|
|
40
|
+
- `AGENTS.md` như bản đồ / mục lục ngắn (~100 dòng) inject vào context, trỏ tới
|
|
41
|
+
nguồn sự thật sâu hơn — không phải encyclopedia một-file (one-big-AGENTS.md
|
|
42
|
+
thất bại vì: chiếm context, "everything important = nothing important", rot
|
|
43
|
+
nhanh, khó verify cơ học);
|
|
44
|
+
- progressive disclosure: agent bắt đầu từ entry point nhỏ ổn định rồi được chỉ
|
|
45
|
+
nơi xem tiếp; linter + CI validate knowledge base up-to-date/cross-linked,
|
|
46
|
+
cùng agent "doc-gardening" định kỳ quét doc cũ và mở PR sửa;
|
|
47
|
+
- agent legibility là mục tiêu: "thứ agent không truy cập được in-context coi
|
|
48
|
+
như không tồn tại" — phải đẩy context (kể cả thảo luận Slack) vào repo dạng
|
|
49
|
+
versioned artifact;
|
|
50
|
+
- ưu tiên dependency và abstraction có thể nội-hóa và lập luận in-repo; công
|
|
51
|
+
nghệ "boring" dễ model hơn; đôi khi rẻ hơn khi để agent tự reimplement một
|
|
52
|
+
phần thay vì lệ thuộc hành vi opaque upstream (vd tự viết map-with-concurrency
|
|
53
|
+
thay cho `p-limit`, tích hợp OpenTelemetry, 100% test coverage);
|
|
54
|
+
- cưỡng chế architecture và taste bằng invariant, không micromanage cài đặt:
|
|
55
|
+
mỗi business domain chia layer cố định, dependency chỉ đi "tiến"
|
|
56
|
+
`Types → Config → Repo → Service → Runtime → UI`, cross-cutting concern (auth,
|
|
57
|
+
connector, telemetry, feature flag) vào qua một interface duy nhất Providers;
|
|
58
|
+
enforce bằng custom linter (do Codex sinh) + structural test, error message
|
|
59
|
+
chèn luôn hướng dẫn remediation vào context; enforce boundary tập trung, cho
|
|
60
|
+
tự do cục bộ;
|
|
61
|
+
- throughput đổi merge philosophy: merge gate tối thiểu, PR ngắn hạn, test flake
|
|
62
|
+
xử lý bằng re-run thay vì block — correction rẻ, chờ đợi mới đắt;
|
|
63
|
+
- autonomy: repo đã vượt ngưỡng để Codex end-to-end một feature (validate state,
|
|
64
|
+
reproduce bug, quay video lỗi, fix, validate bằng cách drive app, quay video
|
|
65
|
+
fix, mở PR, phản hồi feedback, sửa build fail, escalate khi cần judgment,
|
|
66
|
+
merge) — nhưng *phụ thuộc nặng vào structure/tooling của repo này, chưa nên
|
|
67
|
+
giả định generalize nếu không đầu tư tương tự*;
|
|
68
|
+
- entropy và "garbage collection": Codex nhân bản cả pattern dở → drift; thay vì
|
|
69
|
+
dọn "AI slop" thủ công mỗi thứ Sáu (20% tuần, không scale), encode "golden
|
|
70
|
+
principle" + background task định kỳ quét deviation, cập nhật quality grade,
|
|
71
|
+
mở PR refactor nhỏ (review < 1 phút, automerge); tech debt như khoản vay lãi
|
|
72
|
+
cao, trả dần liên tục;
|
|
73
|
+
- frontier kế tiếp: thiết kế environment, feedback loop, và control system để
|
|
74
|
+
agent xây + bảo trì phần mềm phức tạp, tin cậy ở quy mô lớn; chưa rõ
|
|
75
|
+
coherence kiến trúc tiến hóa thế nào qua nhiều năm trong hệ thuần agent.
|
|
76
|
+
|
|
77
|
+
Ghi chú ngày: bài mô tả commit đầu "late August 2025" + "five months later", và
|
|
78
|
+
được InfoQ đưa tin tháng 2/2026, nên dùng 11/2/2026. Bản clipping nội bộ ghi
|
|
79
|
+
`published: 2026-05-27` (nhiều khả năng là metadata clip sai); nếu cần con số
|
|
80
|
+
chính thức, xác minh lại trên trang OpenAI.
|
|
29
81
|
|
|
30
82
|
### [S2] Anthropic
|
|
31
83
|
|
|
@@ -34,13 +86,21 @@ này hoặc không được đánh dấu rõ là diễn giải.
|
|
|
34
86
|
- Ngày xuất bản: 26 tháng 11, 2025
|
|
35
87
|
- URL: https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents
|
|
36
88
|
- Dùng cho:
|
|
37
|
-
- failure mode của agent qua nhiều context window
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
-
|
|
89
|
+
- failure mode của agent qua nhiều context window (như kỹ sư làm theo ca mà
|
|
90
|
+
không nhớ ca trước), cần externalize state;
|
|
91
|
+
- tách initializer agent (dựng `init.sh`, file progress, git commit đầu) khỏi
|
|
92
|
+
coding agent (làm tăng tiến từng phần, để lại code sạch mergeable);
|
|
93
|
+
- feature list dạng JSON (vài trăm feature) với category, mô tả kèm bước kiểm
|
|
94
|
+
chứng, và trường boolean `passes`; không được xóa/sửa test;
|
|
95
|
+
- tiến triển từng feature một thay vì làm quá rộng để tránh cạn context giữa
|
|
96
|
+
chừng;
|
|
97
|
+
- progress file, git commit mô tả rõ (cho phép revert/khôi phục), và `init.sh`
|
|
98
|
+
để khỏi tốn thời gian setup;
|
|
99
|
+
- kiểm thử đầu-cuối bằng browser automation (Puppeteer MCP), kiểm như người
|
|
100
|
+
dùng thay vì chỉ unit test/API;
|
|
101
|
+
- vòng bắt đầu session: `pwd`, đọc git log + progress, đọc feature list và chọn
|
|
102
|
+
feature ưu tiên cao chưa xong, chạy `init.sh`, smoke test, rồi mới code;
|
|
103
|
+
- hướng mở rộng: multi-agent chuyên cho test, QA, và cleanup.
|
|
44
104
|
|
|
45
105
|
### [S3] Anthropic
|
|
46
106
|
|
|
@@ -49,13 +109,22 @@ này hoặc không được đánh dấu rõ là diễn giải.
|
|
|
49
109
|
- Ngày xuất bản: 19 tháng 12, 2024
|
|
50
110
|
- URL: https://www.anthropic.com/engineering/building-effective-agents
|
|
51
111
|
- Dùng cho:
|
|
52
|
-
- phân biệt workflow
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
|
|
56
|
-
-
|
|
57
|
-
|
|
58
|
-
-
|
|
112
|
+
- phân biệt workflow (LLM + tool đi theo predefined code path) và agent (LLM tự
|
|
113
|
+
điều khiển process và tool usage);
|
|
114
|
+
- nguyên tắc bắt đầu bằng giải pháp đơn giản nhất, chỉ tăng complexity khi cần;
|
|
115
|
+
thường tối ưu single LLM call với retrieval + in-context example là đủ;
|
|
116
|
+
- tradeoff: agentic system đánh đổi latency và cost để có task performance tốt
|
|
117
|
+
hơn, phải cân nhắc khi nào đáng;
|
|
118
|
+
- building block "augmented LLM" với retrieval, tool, và memory, kèm interface
|
|
119
|
+
dễ dùng và tài liệu tốt;
|
|
120
|
+
- workflow pattern: prompt chaining, routing, parallelization (sectioning +
|
|
121
|
+
voting), orchestrator-workers, evaluator-optimizer, kèm khi nào dùng;
|
|
122
|
+
- agent như LLM dùng tool dựa trên feedback môi trường trong vòng lặp, cần
|
|
123
|
+
sandbox + guardrail + human oversight tại checkpoint;
|
|
124
|
+
- thiết kế agent-computer interface (ACI) như đầu tư ngang HCI: đặt mình vào vị
|
|
125
|
+
trí model, đặt tên tham số rõ, test nhiều input, áp dụng poka-yoke; ví dụ
|
|
126
|
+
SWE-bench dùng absolute filepath để loại lỗi đường dẫn tương đối;
|
|
127
|
+
- ba nguyên tắc lõi: simplicity, transparency, và tool documentation kỹ.
|
|
59
128
|
|
|
60
129
|
### [S4] Anthropic
|
|
61
130
|
|
|
@@ -64,58 +133,83 @@ này hoặc không được đánh dấu rõ là diễn giải.
|
|
|
64
133
|
- Ngày xuất bản: 24 tháng 3, 2026
|
|
65
134
|
- URL: https://www.anthropic.com/engineering/harness-design-long-running-apps
|
|
66
135
|
- Dùng cho:
|
|
67
|
-
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
-
|
|
71
|
-
|
|
72
|
-
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
-
|
|
81
|
-
-
|
|
82
|
-
|
|
136
|
+
- context anxiety (model "wrap up" sớm khi tưởng sắp hết context) và self-
|
|
137
|
+
evaluation bias (agent khen chính sản phẩm của mình dù chất lượng tầm
|
|
138
|
+
thường), nặng nhất với việc mang tính chủ quan;
|
|
139
|
+
- tách generator khỏi evaluator: tinh chỉnh một evaluator độc lập dễ hơn nhiều
|
|
140
|
+
so với bắt generator tự phê phán công việc của chính nó;
|
|
141
|
+
- grading criteria để làm chất lượng chủ quan dễ chấm hơn (ví dụ frontend:
|
|
142
|
+
design quality, originality, craft, functionality, nhấn mạnh design và
|
|
143
|
+
originality nơi model thường yếu);
|
|
144
|
+
- kiến trúc planner-generator-evaluator cho phát triển ứng dụng dài hạn;
|
|
145
|
+
- sprint contract: thỏa thuận về phạm vi và định nghĩa "done" trước khi
|
|
146
|
+
implement;
|
|
147
|
+
- QA bằng Playwright MCP qua UI, API, database state, và hành vi runtime (ví dụ
|
|
148
|
+
bắt lỗi route-matching FastAPI, lỗi đồng bộ state);
|
|
149
|
+
- chi phí/độ trễ/token của harness phức tạp như tradeoff thực (so sánh
|
|
150
|
+
single-agent ~20 phút/$9 cho ra sản phẩm hỏng vs full harness ~6 giờ/$200 cho
|
|
151
|
+
ra sản phẩm hoàn chỉnh; V2 ~3h50/$124.70);
|
|
152
|
+
- nguyên tắc "every component in a harness encodes an assumption about what the
|
|
153
|
+
model can't do on its own": khi Opus 4.6 xử lý long-context và planning tốt
|
|
154
|
+
hơn, sprint decomposition trở thành overhead và bị bỏ, giữ lại planner +
|
|
155
|
+
evaluator — harness phải được xem lại khi model mới xuất hiện.
|
|
156
|
+
|
|
157
|
+
### [S5] Google DeepMind
|
|
158
|
+
|
|
159
|
+
- Tiêu đề: "AutoHarness: improving LLM agents by automatically synthesizing a code harness"
|
|
160
|
+
- Tác giả: Xinghua Lou, Miguel Lázaro-Gredilla, Antoine Dedieu, Carter Wendelken,
|
|
161
|
+
Wolfgang Lehrach, Kevin P. Murphy
|
|
162
|
+
- Ngày xuất bản: 10 tháng 2, 2026
|
|
163
|
+
- URL: https://arxiv.org/abs/2603.03329
|
|
83
164
|
- Dùng cho:
|
|
84
|
-
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
-
|
|
165
|
+
- vấn đề agent thường thử hành động không hợp lệ trong môi trường (ví dụ 78% số
|
|
166
|
+
ván thua của Gemini-2.5-Flash trong Kaggle GameArena cờ vua là do nước đi
|
|
167
|
+
không hợp lệ);
|
|
168
|
+
- AutoHarness: model tự tổng hợp lớp bọc thực thi (code harness/wrapper) bằng
|
|
169
|
+
một số ít vòng iterative code refinement dựa trên feedback từ môi trường, để
|
|
170
|
+
chặn hành động không hợp lệ trước khi chúng tác động tới môi trường;
|
|
171
|
+
- harness sinh tự động giúp model nhỏ hơn (Gemini-2.5-Flash) vượt model lớn hơn
|
|
172
|
+
(Gemini-2.5-Pro) — ngăn nước đi không hợp lệ qua 145 ván TextArena;
|
|
173
|
+
- code-as-policy: sinh toàn bộ policy thành code tĩnh để loại bỏ LLM call ở thời
|
|
174
|
+
điểm ra quyết định, giảm chi phí và độ trễ runtime; code-policy đạt average
|
|
175
|
+
reward cao hơn cả Gemini-2.5-Pro và GPT-5.2-High trên 16 game single-player.
|
|
88
176
|
|
|
89
177
|
## Bản đồ bằng chứng
|
|
90
178
|
|
|
91
179
|
| Nhận định | Nguồn |
|
|
92
180
|
| --- | --- |
|
|
93
181
|
| Harness engineering chuyển trọng tâm kỹ sư từ viết code tay sang thiết kế môi trường, intent, và feedback loop. | [S1] |
|
|
94
|
-
|
|
|
95
|
-
| `
|
|
96
|
-
|
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
|
|
|
100
|
-
|
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
|
|
|
104
|
-
|
|
|
105
|
-
|
|
|
106
|
-
|
|
|
107
|
-
|
|
|
182
|
+
| Khi agent gặp lỗi, phản xạ đúng là hỏi "thiếu capability nào và làm sao agent thấy + cưỡng chế được", không phải tự sửa tay. | [S1] |
|
|
183
|
+
| Repository-local knowledge có cấu trúc (`docs/`) là system of record, giúp agent truy cập quyết định/quy tắc/trạng thái mà không phụ thuộc chat history và tránh lệch doc–implementation. | [S1] |
|
|
184
|
+
| `AGENTS.md` nên là bản đồ ngắn (~100 dòng) trỏ tới nguồn sự thật sâu hơn, không phải manual khổng lồ một-file. | [S1] |
|
|
185
|
+
| Kiến trúc phân lớp với dependency chỉ đi tiến (`Types → Config → Repo → Service → Runtime → UI`) là ràng buộc cho phép tốc độ mà không bị architectural drift. | [S1] |
|
|
186
|
+
| Chọn dependency và abstraction nên tính đến việc agent có thể inspect, validate, và modify trực tiếp; hành vi opaque ở upstream làm giảm leverage. | [S1] |
|
|
187
|
+
| Agent cần application legibility: log, metric, span/trace để giám sát hiệu năng và tái hiện bug qua môi trường dev cô lập. | [S1] |
|
|
188
|
+
| Repository knowledge nên theo progressive disclosure và có kiểm tra cơ học cho freshness/cross-link (linter + CI) để giảm drift. | [S1] |
|
|
189
|
+
| Documentation đơn thuần không đủ; architecture và taste nên được cưỡng chế bằng custom linter, structural test, và mechanical rule. | [S1] |
|
|
190
|
+
| Review có thể đẩy gần hết sang agent-to-agent qua vòng lặp tự-review + xin agent review tới khi reviewer hài lòng; human review không bắt buộc. | [S1] |
|
|
191
|
+
| Throughput cao đổi merge philosophy: merge gate tối thiểu, PR ngắn hạn, test flake re-run thay vì block — correction rẻ, chờ đợi đắt. | [S1] |
|
|
192
|
+
| Throughput cao làm entropy tích lũy nhanh; encode "golden principle" + background task định kỳ mở PR refactor nhỏ (automerge) thay cho dọn slop thủ công là một phần của harness. | [S1] |
|
|
193
|
+
| Mức autonomy end-to-end của agent phụ thuộc nặng vào structure/tooling cụ thể của repo, không nên giả định generalize nếu chưa đầu tư tương đương. | [S1] |
|
|
194
|
+
| Agent dài hạn dễ mất context giữa các session (như làm theo ca không nhớ ca trước) và cần externalized state. | [S2] |
|
|
195
|
+
| Tách initializer agent (dựng `init.sh`, progress, git) khỏi coding agent (làm tăng tiến, để lại code mergeable) giúp session mới phục hồi trạng thái. | [S2] |
|
|
196
|
+
| Feature list dạng JSON với category, bước kiểm chứng, và trường `passes` giữ trạng thái feature có cấu trúc và nhất quán qua nhiều session; không được xóa/sửa test. | [S2] |
|
|
197
|
+
| Git commit mô tả rõ và progress update tạo checkpoint để agent revert thay đổi xấu và khôi phục working state sạch. | [S2] |
|
|
198
|
+
| Làm từng feature và chỉ đánh dấu pass sau kiểm thử đầu-cuối giúp giảm tuyên bố hoàn thành sớm. | [S2] |
|
|
199
|
+
| Browser automation (Puppeteer MCP) giúp agent phát hiện lỗi không thấy được từ code hoặc unit test đơn lẻ. | [S2] |
|
|
200
|
+
| Agentic system nên bắt đầu bằng giải pháp đơn giản nhất và chỉ tăng complexity khi cần; thường single LLM call + retrieval/example là đủ. | [S3] |
|
|
108
201
|
| Workflow phù hợp với task có đường đi định sẵn; agent phù hợp khi cần model tự quyết định tool/process. | [S3] |
|
|
109
|
-
|
|
|
202
|
+
| Agentic system đánh đổi latency và cost để lấy task performance, phải cân nhắc khi nào đáng. | [S3] |
|
|
203
|
+
| Tool nên được thiết kế như agent-computer interface với mô tả, tham số, ranh giới, ví dụ rõ, và áp dụng poka-yoke để chống lạm dụng. | [S3] |
|
|
110
204
|
| Generator tự đánh giá thường quá tích cực; evaluator riêng dễ được chỉnh thành hoài nghi hơn. | [S4] |
|
|
111
|
-
|
|
|
112
|
-
|
|
|
205
|
+
| Grading criteria rõ (vd design quality, originality, craft, functionality) làm chất lượng chủ quan dễ chấm hơn. | [S4] |
|
|
206
|
+
| Sprint contract giúp generator và evaluator đồng thuận về phạm vi, tiêu chí done, và phương thức QA trước khi implement. | [S4] |
|
|
207
|
+
| QA bằng Playwright qua UI, API, database state, và hành vi runtime bắt được lỗi mà self-eval bỏ sót. | [S4] |
|
|
113
208
|
| Harness phức tạp có thể tốn nhiều giờ và token, nên phải được xem là tradeoff thay vì mặc định. | [S4] |
|
|
114
|
-
|
|
|
115
|
-
|
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
| LLM-as-a-Judge tự động giúp đo lường độ an toàn và hiệu năng của agent liên tục, và Meta-Evaluation (VeRO) tối ưu hóa cấu trúc của harness dựa trên phản hồi. | [S5] |
|
|
209
|
+
| Mỗi thành phần harness mã hóa một giả định về thứ model chưa tự làm được; khi model mới mạnh hơn, nên xem lại và loại bỏ phần không còn tạo giá trị (vd bỏ sprint khi Opus 4.6 tự planning tốt). | [S4] |
|
|
210
|
+
| Agent thường thử hành động không hợp lệ trong môi trường (vd 78% ván thua của Gemini-2.5-Flash do nước đi sai luật). | [S5] |
|
|
211
|
+
| Thay vì tự viết thủ công mọi ràng buộc, model có thể tự tổng hợp lớp bọc thực thi (AutoHarness) bằng iterative code refinement để lọc hành động không hợp lệ, giúp model nhỏ vượt model lớn. | [S5] |
|
|
212
|
+
| Sinh toàn bộ policy thành code tĩnh (code-as-policy) loại bỏ LLM call lúc ra quyết định, tối ưu chi phí/độ trễ và có thể vượt cả model lớn trên các môi trường single-player. | [S5] |
|
|
119
213
|
|
|
120
214
|
## Chính sách citation
|
|
121
215
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codex-harness-engineering",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Codex harness engineering docs and installable agent skills.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,14 +23,15 @@
|
|
|
23
23
|
"README.md",
|
|
24
24
|
"docs",
|
|
25
25
|
"scripts",
|
|
26
|
-
"skills"
|
|
26
|
+
"skills",
|
|
27
|
+
"templates"
|
|
27
28
|
],
|
|
28
29
|
"scripts": {
|
|
29
30
|
"test": "node --test tests/*.test.mjs",
|
|
30
31
|
"verify": "node scripts/verify-harness.mjs && npm test",
|
|
31
32
|
"pack:dry": "npm pack --dry-run",
|
|
32
33
|
"release": "./scripts/publish.sh",
|
|
33
|
-
"prepublishOnly": "npm
|
|
34
|
+
"prepublishOnly": "npm run verify"
|
|
34
35
|
},
|
|
35
36
|
"engines": {
|
|
36
37
|
"node": ">=18.17"
|
|
@@ -38,5 +39,5 @@
|
|
|
38
39
|
"publishConfig": {
|
|
39
40
|
"access": "public"
|
|
40
41
|
},
|
|
41
|
-
"license": "
|
|
42
|
+
"license": "MIT"
|
|
42
43
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { realpathSync } from "node:fs";
|
|
4
|
-
import { access, cp, mkdir, rm } from "node:fs/promises";
|
|
4
|
+
import { access, chmod, cp, mkdir, rm } from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
7
|
|
|
@@ -12,6 +12,18 @@ export const SKILL_NAMES = [
|
|
|
12
12
|
"acceptance-contract",
|
|
13
13
|
"cleanup-harness",
|
|
14
14
|
"creator-harness",
|
|
15
|
+
"lessons-harness",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export const HARNESS_FILES = [
|
|
19
|
+
"AGENTS.md",
|
|
20
|
+
"progress.md",
|
|
21
|
+
"feature_list.json",
|
|
22
|
+
"lessons.md",
|
|
23
|
+
"memory/README.md",
|
|
24
|
+
"init.sh",
|
|
25
|
+
"verify-state.mjs",
|
|
26
|
+
"rotate-state.mjs",
|
|
15
27
|
];
|
|
16
28
|
|
|
17
29
|
async function exists(filePath) {
|
|
@@ -33,49 +45,91 @@ export async function installSkills({
|
|
|
33
45
|
packageRoot = PACKAGE_ROOT,
|
|
34
46
|
projectRoot = process.cwd(),
|
|
35
47
|
force = false,
|
|
48
|
+
harness = false,
|
|
49
|
+
team = false,
|
|
36
50
|
} = {}) {
|
|
37
51
|
const sourceRoot = path.join(packageRoot, "skills");
|
|
38
52
|
const targetRoot = path.join(projectRoot, ".agents", "skills");
|
|
39
53
|
const docsSource = path.join(packageRoot, "docs", "harness-engineering");
|
|
40
54
|
const docsTarget = path.join(projectRoot, "docs", "harness-engineering");
|
|
55
|
+
const harnessSource = path.join(packageRoot, "templates", "harness");
|
|
56
|
+
const teamSource = path.join(packageRoot, "templates", "team");
|
|
57
|
+
const teamTarget = path.join(projectRoot, "team");
|
|
58
|
+
const scaffoldHarness = harness || team;
|
|
41
59
|
const installed = [];
|
|
42
|
-
|
|
43
|
-
|
|
60
|
+
const scaffolded = [];
|
|
61
|
+
const copies = [];
|
|
44
62
|
|
|
45
63
|
for (const skillName of SKILL_NAMES) {
|
|
46
64
|
const source = path.join(sourceRoot, skillName);
|
|
47
65
|
const target = path.join(targetRoot, skillName);
|
|
48
66
|
|
|
49
67
|
if (path.resolve(source) !== path.resolve(target)) {
|
|
50
|
-
|
|
51
|
-
await rm(target, { recursive: true, force: true });
|
|
52
|
-
await cp(source, target, { recursive: true, force: true });
|
|
68
|
+
copies.push({ source, target });
|
|
53
69
|
}
|
|
54
70
|
installed.push(skillName);
|
|
55
71
|
}
|
|
56
72
|
|
|
57
73
|
if (path.resolve(docsSource) !== path.resolve(docsTarget)) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
copies.push({ source: docsSource, target: docsTarget });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (scaffoldHarness) {
|
|
78
|
+
for (const fileName of HARNESS_FILES) {
|
|
79
|
+
const source = path.join(harnessSource, fileName);
|
|
80
|
+
const target = path.join(projectRoot, fileName);
|
|
81
|
+
|
|
82
|
+
if (path.resolve(source) !== path.resolve(target)) {
|
|
83
|
+
copies.push({ source, target });
|
|
84
|
+
scaffolded.push(fileName);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (team && path.resolve(teamSource) !== path.resolve(teamTarget)) {
|
|
90
|
+
copies.push({ source: teamSource, target: teamTarget });
|
|
91
|
+
scaffolded.push("team/");
|
|
61
92
|
}
|
|
62
93
|
|
|
63
|
-
|
|
94
|
+
// Check every target before copying anything so a conflict cannot leave a
|
|
95
|
+
// partial install behind.
|
|
96
|
+
for (const { target } of copies) {
|
|
97
|
+
await assertCanWrite(target, force);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await mkdir(targetRoot, { recursive: true });
|
|
101
|
+
|
|
102
|
+
for (const { source, target } of copies) {
|
|
103
|
+
await rm(target, { recursive: true, force: true });
|
|
104
|
+
await cp(source, target, { recursive: true, force: true });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (scaffolded.includes("init.sh")) {
|
|
108
|
+
await chmod(path.join(projectRoot, "init.sh"), 0o755);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { targetRoot, docsTarget, installed, scaffolded };
|
|
64
112
|
}
|
|
65
113
|
|
|
66
114
|
export function parseArgs(args) {
|
|
67
115
|
const [command, ...flags] = args;
|
|
68
116
|
|
|
69
117
|
if (command !== "init") {
|
|
70
|
-
throw new Error("Usage: codex-harness-engineering init [--force]");
|
|
118
|
+
throw new Error("Usage: codex-harness-engineering init [--force] [--team]");
|
|
71
119
|
}
|
|
72
120
|
|
|
73
|
-
const
|
|
121
|
+
const knownFlags = ["--force", "--harness", "--starter-kit", "--team"];
|
|
122
|
+
const unknownFlag = flags.find((flag) => !knownFlags.includes(flag));
|
|
74
123
|
if (unknownFlag) {
|
|
75
124
|
throw new Error(`Unknown option: ${unknownFlag}`);
|
|
76
125
|
}
|
|
77
126
|
|
|
78
|
-
return {
|
|
127
|
+
return {
|
|
128
|
+
command,
|
|
129
|
+
force: flags.includes("--force"),
|
|
130
|
+
harness: true,
|
|
131
|
+
team: flags.includes("--team"),
|
|
132
|
+
};
|
|
79
133
|
}
|
|
80
134
|
|
|
81
135
|
function isDirectRun() {
|
|
@@ -89,14 +143,18 @@ function isDirectRun() {
|
|
|
89
143
|
|
|
90
144
|
if (isDirectRun()) {
|
|
91
145
|
try {
|
|
92
|
-
const { force } = parseArgs(process.argv.slice(2));
|
|
93
|
-
const { targetRoot, docsTarget, installed } = await installSkills({ force });
|
|
146
|
+
const { force, harness, team } = parseArgs(process.argv.slice(2));
|
|
147
|
+
const { targetRoot, docsTarget, installed, scaffolded } = await installSkills({ force, harness, team });
|
|
94
148
|
|
|
95
149
|
console.log(`Installed ${installed.length} skills to ${targetRoot}`);
|
|
96
150
|
for (const skillName of installed) {
|
|
97
151
|
console.log(`- ${skillName}`);
|
|
98
152
|
}
|
|
99
153
|
console.log(`Copied docs to ${docsTarget}`);
|
|
154
|
+
if (scaffolded.length > 0) {
|
|
155
|
+
console.log(`Scaffolded harness files: ${scaffolded.join(", ")}`);
|
|
156
|
+
console.log("Next: edit init.sh and the Commands section of AGENTS.md.");
|
|
157
|
+
}
|
|
100
158
|
} catch (error) {
|
|
101
159
|
console.error(error instanceof Error ? error.message : error);
|
|
102
160
|
process.exitCode = 1;
|
package/scripts/publish.sh
CHANGED
|
@@ -11,6 +11,8 @@ const REQUIRED_FILES = [
|
|
|
11
11
|
"README.md",
|
|
12
12
|
"progress.md",
|
|
13
13
|
"feature_list.json",
|
|
14
|
+
"lessons.md",
|
|
15
|
+
"memory/README.md",
|
|
14
16
|
"init.sh",
|
|
15
17
|
"docs/harness-engineering/index.md",
|
|
16
18
|
"docs/harness-engineering/research-note.md",
|
|
@@ -19,13 +21,18 @@ const REQUIRED_FILES = [
|
|
|
19
21
|
"skills/creator-harness/SKILL.md",
|
|
20
22
|
"skills/acceptance-contract/SKILL.md",
|
|
21
23
|
"skills/cleanup-harness/SKILL.md",
|
|
24
|
+
"skills/lessons-harness/SKILL.md",
|
|
22
25
|
];
|
|
23
26
|
const README_MAPPED_FILES = REQUIRED_FILES.filter(
|
|
24
27
|
(relativePath) => relativePath !== "AGENTS.md" && relativePath !== "README.md"
|
|
25
28
|
);
|
|
26
29
|
const STATE_FILES = ["feature_list.json", "progress.md"];
|
|
30
|
+
const LINE_BUDGETS = {
|
|
31
|
+
"progress.md": 400,
|
|
32
|
+
"lessons.md": 400,
|
|
33
|
+
};
|
|
27
34
|
const BEHAVIOR_CHANGE_PATTERNS = [
|
|
28
|
-
/^(scripts|tests|skills)\//,
|
|
35
|
+
/^(scripts|tests|skills|templates)\//,
|
|
29
36
|
/^package(?:-lock)?\.json$/,
|
|
30
37
|
/^init\.sh$/,
|
|
31
38
|
/^AGENTS\.md$/,
|
|
@@ -40,7 +47,7 @@ async function exists(filePath) {
|
|
|
40
47
|
}
|
|
41
48
|
}
|
|
42
49
|
|
|
43
|
-
async function
|
|
50
|
+
async function listFiles(directory, relativeRoot, matches) {
|
|
44
51
|
if (!await exists(directory)) {
|
|
45
52
|
return [];
|
|
46
53
|
}
|
|
@@ -53,8 +60,8 @@ async function markdownFiles(directory, relativeRoot) {
|
|
|
53
60
|
const relativePath = path.join(relativeRoot, entry.name);
|
|
54
61
|
|
|
55
62
|
if (entry.isDirectory()) {
|
|
56
|
-
found.push(...await
|
|
57
|
-
} else if (entry.isFile() && entry.name.
|
|
63
|
+
found.push(...await listFiles(absolutePath, relativePath, matches));
|
|
64
|
+
} else if (entry.isFile() && entry.name !== ".DS_Store" && matches(entry.name)) {
|
|
58
65
|
found.push(relativePath);
|
|
59
66
|
}
|
|
60
67
|
}
|
|
@@ -62,6 +69,43 @@ async function markdownFiles(directory, relativeRoot) {
|
|
|
62
69
|
return found;
|
|
63
70
|
}
|
|
64
71
|
|
|
72
|
+
function markdownFiles(directory, relativeRoot) {
|
|
73
|
+
return listFiles(directory, relativeRoot, (name) => name.endsWith(".md"));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function skillsMirrorErrors(root) {
|
|
77
|
+
const mirrorRoot = path.join(root, ".agents", "skills");
|
|
78
|
+
if (!await exists(mirrorRoot)) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const errors = [];
|
|
83
|
+
const sourceFiles = await listFiles(path.join(root, "skills"), "", () => true);
|
|
84
|
+
const mirrorFiles = await listFiles(mirrorRoot, "", () => true);
|
|
85
|
+
|
|
86
|
+
for (const relativePath of sourceFiles) {
|
|
87
|
+
const mirrorPath = path.join(mirrorRoot, relativePath);
|
|
88
|
+
if (!await exists(mirrorPath)) {
|
|
89
|
+
errors.push(`.agents/skills/${relativePath}: mirror copy of skills/${relativePath} is missing`);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const source = await readFile(path.join(root, "skills", relativePath), "utf8");
|
|
94
|
+
const mirror = await readFile(mirrorPath, "utf8");
|
|
95
|
+
if (source !== mirror) {
|
|
96
|
+
errors.push(`.agents/skills/${relativePath}: mirror copy is out of sync with skills/${relativePath}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
for (const relativePath of mirrorFiles) {
|
|
101
|
+
if (!sourceFiles.includes(relativePath)) {
|
|
102
|
+
errors.push(`.agents/skills/${relativePath}: has no source file under skills/`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return errors;
|
|
107
|
+
}
|
|
108
|
+
|
|
65
109
|
function changesHarnessBehavior(relativePath) {
|
|
66
110
|
return BEHAVIOR_CHANGE_PATTERNS.some((pattern) => pattern.test(relativePath));
|
|
67
111
|
}
|
|
@@ -128,6 +172,19 @@ export async function verifyHarness(root = PACKAGE_ROOT, { changedFiles = [] } =
|
|
|
128
172
|
}
|
|
129
173
|
}
|
|
130
174
|
|
|
175
|
+
errors.push(...await skillsMirrorErrors(root));
|
|
176
|
+
|
|
177
|
+
for (const [relativePath, budget] of Object.entries(LINE_BUDGETS)) {
|
|
178
|
+
if (await exists(path.join(root, relativePath))) {
|
|
179
|
+
const lineCount = (await readFile(path.join(root, relativePath), "utf8")).split("\n").length;
|
|
180
|
+
if (lineCount > budget) {
|
|
181
|
+
errors.push(
|
|
182
|
+
`${relativePath}: ${lineCount} lines exceeds the ${budget}-line budget; archive older entries with node templates/harness/rotate-state.mjs .`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
131
188
|
const behaviorChanges = changedFiles.filter(changesHarnessBehavior);
|
|
132
189
|
if (behaviorChanges.length > 0) {
|
|
133
190
|
for (const stateFile of STATE_FILES) {
|