opencode-skills-collection 3.0.45 → 3.0.47

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.
Files changed (71) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +10 -1
  2. package/bundled-skills/2slides-ppt-generator/SKILL.md +1 -1
  3. package/bundled-skills/2slides-ppt-generator/scripts/create_pdf_slides.py +2 -1
  4. package/bundled-skills/2slides-ppt-generator/scripts/generate_narration.py +2 -1
  5. package/bundled-skills/2slides-ppt-generator/scripts/generate_slides.py +13 -7
  6. package/bundled-skills/android-dev/references/hybrid.md +7 -4
  7. package/bundled-skills/android-dev/references/react-native.md +5 -2
  8. package/bundled-skills/atlas-contract/SKILL.md +4 -4
  9. package/bundled-skills/atlas-ledger/SKILL.md +10 -7
  10. package/bundled-skills/bun-development/SKILL.md +1 -1
  11. package/bundled-skills/cloud-penetration-testing/SKILL.md +1 -1
  12. package/bundled-skills/codebase-to-wordpress-converter/SKILL.md +1 -0
  13. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  14. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  15. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  16. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  17. package/bundled-skills/docs/users/bundles.md +1 -1
  18. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  19. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  20. package/bundled-skills/docs/users/getting-started.md +1 -1
  21. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  22. package/bundled-skills/docs/users/usage.md +4 -4
  23. package/bundled-skills/docs/users/visual-guide.md +4 -4
  24. package/bundled-skills/dos-verify-done-claims/SKILL.md +173 -0
  25. package/bundled-skills/ecl-harness-engineer/LICENSE +21 -0
  26. package/bundled-skills/ecl-harness-engineer/SKILL.md +714 -0
  27. package/bundled-skills/ecl-harness-engineer/agents/analyzer.md +119 -0
  28. package/bundled-skills/ecl-harness-engineer/agents/auditor.md +212 -0
  29. package/bundled-skills/ecl-harness-engineer/agents/creator-config.md +343 -0
  30. package/bundled-skills/ecl-harness-engineer/agents/creator-docs.md +201 -0
  31. package/bundled-skills/ecl-harness-engineer/agents/creator-linters.md +123 -0
  32. package/bundled-skills/ecl-harness-engineer/references/adapters/adapter-schema.md +204 -0
  33. package/bundled-skills/ecl-harness-engineer/references/adapters/generic.md +156 -0
  34. package/bundled-skills/ecl-harness-engineer/references/adapters/go.md +212 -0
  35. package/bundled-skills/ecl-harness-engineer/references/adapters/java.md +205 -0
  36. package/bundled-skills/ecl-harness-engineer/references/adapters/python.md +225 -0
  37. package/bundled-skills/ecl-harness-engineer/references/adapters/rust.md +220 -0
  38. package/bundled-skills/ecl-harness-engineer/references/adapters/typescript.md +245 -0
  39. package/bundled-skills/ecl-harness-engineer/references/architecture-diagrams.md +420 -0
  40. package/bundled-skills/ecl-harness-engineer/references/audit-templates.md +649 -0
  41. package/bundled-skills/ecl-harness-engineer/references/capability-registry.md +485 -0
  42. package/bundled-skills/ecl-harness-engineer/references/darwin-eval-prompts.md +373 -0
  43. package/bundled-skills/ecl-harness-engineer/references/documentation-templates.md +741 -0
  44. package/bundled-skills/ecl-harness-engineer/references/durability-patterns.md +423 -0
  45. package/bundled-skills/ecl-harness-engineer/references/ecl-harness.md +1431 -0
  46. package/bundled-skills/ecl-harness-engineer/references/environment-config-guide.md +534 -0
  47. package/bundled-skills/ecl-harness-engineer/references/environment-detection-guide.md +751 -0
  48. package/bundled-skills/ecl-harness-engineer/references/eval-templates.md +377 -0
  49. package/bundled-skills/ecl-harness-engineer/references/gc-templates.md +798 -0
  50. package/bundled-skills/ecl-harness-engineer/references/greenfield-templates.md +1385 -0
  51. package/bundled-skills/ecl-harness-engineer/references/linter-templates.md +448 -0
  52. package/bundled-skills/ecl-harness-engineer/references/observability-templates.md +315 -0
  53. package/bundled-skills/environment-setup-guide/SKILL.md +2 -2
  54. package/bundled-skills/evolution/SKILL.md +1 -1
  55. package/bundled-skills/gitops-workflow/SKILL.md +1 -1
  56. package/bundled-skills/linkerd-patterns/SKILL.md +1 -1
  57. package/bundled-skills/loki-mode/examples/todo-app-generated/frontend/package-lock.json +504 -1317
  58. package/bundled-skills/loki-mode/examples/todo-app-generated/frontend/package.json +2 -2
  59. package/bundled-skills/lovable-cleanup/SKILL.md +416 -0
  60. package/bundled-skills/monopoly/SKILL.md +397 -0
  61. package/bundled-skills/monopoly/patterns/SKILL.md +331 -0
  62. package/bundled-skills/monopoly/scale-benchmarks/SKILL.md +174 -0
  63. package/bundled-skills/monopoly/security-checklist/SKILL.md +69 -0
  64. package/bundled-skills/monopoly/tech-matrix/SKILL.md +268 -0
  65. package/bundled-skills/pagespeed-enhancer/SKILL.md +579 -0
  66. package/bundled-skills/polis-protocol/SKILL.md +6 -3
  67. package/bundled-skills/unship/SKILL.md +11 -5
  68. package/bundled-skills/uv-package-manager/resources/implementation-playbook.md +1 -1
  69. package/bundled-skills/varlock/SKILL.md +2 -2
  70. package/package.json +1 -1
  71. package/skills_index.json +204 -4
@@ -0,0 +1,1431 @@
1
+ # ECL-aware Harness Reference
2
+
3
+ Use this reference when creating `docs/ECL.md`, `harness/changes`, change templates,
4
+ `harness/evolution`, `scripts/harness-change.*`, `scripts/harness-evolve.*`,
5
+ `scripts/lint-ecl.*`, and `scripts/lint-encoding.*`.
6
+
7
+ Choose the command surface from the target project before wiring docs, Makefile/package scripts, or
8
+ CI. Bash, PowerShell, Node, and Python implementations are equivalent profiles when they preserve the
9
+ same command set and invariants. For Windows projects that select Bash, document Git Bash, WSL,
10
+ MSYS2, or CI Linux shell as a prerequisite. PowerShell templates remain available for projects that
11
+ accept `.ps1`; they must support Windows PowerShell 5.1 as well as PowerShell 7.
12
+
13
+ Command surface selection is automatic. Ask the user only when project evidence conflicts, when no
14
+ reasonable runtime or shell can be inferred, or when the user has already stated a hard constraint
15
+ such as "no ps1" or "bash only".
16
+
17
+ ## 1. Design Rules
18
+
19
+ ### 1.1 Roles
20
+
21
+ | Artifact | Role |
22
+ |----------|------|
23
+ | `AGENTS.md` | Navigation map for agents; keep it short and link to detailed docs |
24
+ | `docs/ECL.md` | Project operating manual for change lifecycle and context loading |
25
+ | `docs/STATUS.md` | Lightweight handoff summary used only when no active change exists |
26
+ | `harness/changes/active/` | Current task context; personal development allows only one active task |
27
+ | `harness/changes/parking/` | Paused tasks that may resume later |
28
+ | `harness/changes/archive/` | Closed tasks: completed, blocked, or abandoned |
29
+ | `harness/changes/INDEX.json` | Generated search index; never hand-edit |
30
+ | `harness/evolution/state.json` | Auto-evolve threshold and last-run state |
31
+ | `harness/evolution/pending.md` | Generated instruction when enough archived evidence exists |
32
+ | `harness/evolution/results.tsv` | Darwin-style keep/revert score log for harness evolution |
33
+ | `scripts/harness-change.*` | State transition and index generation tool |
34
+ | `scripts/harness-evolve.*` | Mechanical threshold check and pending-context generator |
35
+ | `scripts/lint-ecl.*` | Mechanical ECL integrity gate |
36
+
37
+ Before implementation starts, require an approved plan review. Record it as
38
+ `plan_review: "approved"` in `summary.md` front matter or as an approved plan review in
39
+ `reviews/`. This gate prevents raw requirements from moving directly into coding.
40
+
41
+ Structured changes use plan-aware intake. If the user provides only a requirement, ask the smallest
42
+ set of high-impact questions needed to draft `spec.md`. If the user already provides a plan, review
43
+ that plan as a draft, split WHAT/WHY into `spec.md` and HOW into `plan.md`, and ask only about gaps
44
+ that affect implementation direction or acceptance. If the plan is complete and does not conflict
45
+ with repository evidence, do not restart the interview. If it conflicts with code, docs, commands,
46
+ or existing harness constraints, record the conflict and return to Intake Review. Each intake round
47
+ asks at most three questions.
48
+ Low-risk unknowns become assumptions; high-impact unknowns become `[NEEDS CLARIFICATION: ...]` and
49
+ block implementation.
50
+
51
+ ### 1.2 When to create a change
52
+
53
+ Create a change when any condition applies:
54
+
55
+ - The work spans more than two files.
56
+ - The work touches APIs, databases, permissions, architecture, or cross-client behavior.
57
+ - The work needs user confirmation or plan review.
58
+ - The work needs multi-step tests or regression checks.
59
+ - The work is likely to take more than 20 minutes.
60
+ - A failure needs to be captured as a new constraint, task, or regression.
61
+
62
+ Small, local fixes can skip `harness/changes` if the final response records validation.
63
+ Evidence, non-goals, options, and plan review are mandatory for non-trivial active changes. Small
64
+ local fixes that skip a change do not need the full template, but the final response must still
65
+ record assumptions and validation.
66
+
67
+ Decision tree:
68
+
69
+ 1. Existing active change: keep using it; do not create another active context.
70
+ 2. Small Change: copy, comments, README text, formatting, or an obviously local single-file fix with
71
+ no runtime, API, data, permission, architecture, or validation-chain impact.
72
+ 3. Structured Change: APIs, data, permissions, architecture, multiple modules, release/runtime
73
+ behavior, unclear requirements, or work likely to exceed 20 minutes.
74
+ 4. Unclear impact: inspect read-only first. If uncertainty remains, ask one high-impact question or
75
+ upgrade to Structured Change; do not assume Small Change.
76
+
77
+ ### 1.3 Change lifecycle
78
+
79
+ ```text
80
+ new -> active/in_progress
81
+ active -> close completed -> archive/YYYY-MM-DD-slug
82
+ active -> close abandoned -> archive/YYYY-MM-DD-slug
83
+ active -> close blocked -> archive/YYYY-MM-DD-slug
84
+ active -> park -> parking/YYYY-MM-DD-slug
85
+ parking/YYYY-MM-DD-slug -> resume -> active
86
+ ```
87
+
88
+ Rules:
89
+
90
+ - `active/` has no persistent change id. The id is created only when parking or closing.
91
+ - Starting a new task must never overwrite an existing active task.
92
+ - `INDEX.json` is derived from `parking/*/summary.md` and `archive/*/summary.md`.
93
+ - `harness/evolution/pending.md` is generated only after enough new archived changes accumulate.
94
+ - Hook and CI may validate, but must not auto-write docs or move changes.
95
+ - `docs/STATUS.md` is a handoff summary, not a change record. Active change files always
96
+ override it for the current task.
97
+
98
+ Close-change STATUS protocol:
99
+
100
+ 1. Before closing active work, update `docs/STATUS.md` from active `summary.md`, `spec.md`,
101
+ `plan.md`, `tasks.md`, and relevant `reviews/` with completed work, validation, residual risks, and
102
+ the next resume point.
103
+ 2. Run the generated `scripts/harness-change.* close <completed|blocked|abandoned>` command so the active change
104
+ moves to `archive/` and `INDEX.json` is rebuilt.
105
+ 3. After close, update `docs/STATUS.md` with the final archive `summary.md` path.
106
+ 4. Run `scripts/harness-evolve.* check` through `harness-change.* close`; if the threshold is
107
+ reached, it creates `harness/evolution/pending.md` but does not edit docs or scripts.
108
+ 5. Run the generated harness lint command. CI and hooks may validate STATUS, but must not
109
+ update it automatically.
110
+
111
+ ### 1.4 Auto-evolve lifecycle
112
+
113
+ Core ECL includes auto-evolve threshold checking by default. This is not the advanced eval,
114
+ observability, or long-term memory profile. It is a small loop that learns from closed changes:
115
+
116
+ ```text
117
+ close/reindex -> count eligible archive evidence -> generate pending.md when threshold reached
118
+ pending.md + no active change -> Codex asks whether to handle pending maintenance now
119
+ started pending evolution -> proposal -> independent scoring or dry_run -> verify -> results.tsv -> mark-complete
120
+ ```
121
+
122
+ Rules:
123
+
124
+ - `scripts/harness-evolve.*` only performs mechanical counting and pending generation.
125
+ - `harness/evolution/pending.md` is a maintenance reminder, not a hard lock. Reading it for context
126
+ does not start pending evolution and must not block ordinary user work.
127
+ - When no active change exists and pending maintenance is present, Codex should ask the user whether
128
+ to handle it now unless the user already prioritized the current task. A user yes starts the
129
+ pending evolution flow; a no leaves pending in place and ordinary work continues.
130
+ - Pending evolution starts when Codex creates or uses an `auto-evolve-harness-*` change, writes an
131
+ evolution proposal/result, or edits Harness files based on pending evidence.
132
+ - Generated scripts do not call subagents. They create pending context; the Codex run that handles
133
+ pending evolution requests an independent auditor/subagent when available. User approval to handle
134
+ pending implies permission to request independent review; if the environment still requires
135
+ explicit authorization, ask once before falling back.
136
+ - Codex performs semantic extraction and file edits, inside a dedicated active change.
137
+ - Once pending evolution starts, Codex must finish with a proposal, one `results.tsv` row, and
138
+ `harness-evolve mark-complete`; otherwise park or close blocked, not completed.
139
+ - Codex must write a proposal before editing harness files. Rejected or no-op candidates stay in
140
+ the proposal and must not enter AGENTS.md, ECL, STATUS, lint, or CI.
141
+ - Auto-apply requires independent auditor/subagent scoring. If independent scoring is unavailable,
142
+ declined, or still unauthorized after asking, record `noop` with `eval_mode=dry_run`, keep the
143
+ proposal, run `mark-complete`, and do not auto-apply the delta.
144
+ - No independent scorer = no auto-apply.
145
+ - Machinery repair (`harness-evolve`, pending templates, lint) is allowed as a prerequisite but does
146
+ not complete pending evolution by itself. After repair, evaluate candidate archives or park/block.
147
+ - Before evaluating candidates, rebuild `harness/changes/INDEX.json` and use the current eligible
148
+ archive window. Candidate Archives inside an older pending file are a trigger snapshot, not the
149
+ only allowed evidence.
150
+ - Complexity budget: prefer clarifying or replacing existing rules over adding new sections. If an
151
+ idea can be expressed by editing an existing paragraph, do not create a new document, directory,
152
+ script, or workflow.
153
+ - AGENTS.md must not carry auto-evolve details; it only points to the pending context when present.
154
+ - If active work exists, finish or park it before executing pending evolution.
155
+ - Every durable new rule must cite archived change evidence.
156
+ - One-off business bugs are not enough to create permanent harness rules.
157
+ - The default auto-evolve path must not create `harness/eval`, `harness/trace`, `harness/state`,
158
+ `harness/checkpoints`, `harness/memory`, or `harness/metrics`.
159
+ - A delta is kept only when hard gates pass, score is at least 80, independent review passes, and
160
+ validation passes; otherwise log `noop` for reviewed evidence with no durable rule, `rejected`
161
+ for proposals that fail hard gates before apply, or revert the failed applied delta and log
162
+ `revert` in `harness/evolution/results.tsv`. Every started pending evolution ends with
163
+ `harness-evolve mark-complete` or stays parked/blocked.
164
+
165
+ Auto-evolve scoring:
166
+
167
+ | Dimension | Weight |
168
+ |-----------|-------:|
169
+ | Evidence grounding | 30 |
170
+ | Project relevance | 25 |
171
+ | Mechanical enforceability | 15 |
172
+ | Regression safety | 20 |
173
+ | Context cost | 10 |
174
+
175
+ `results.tsv` status values are `keep`, `revert`, `rejected`, and `noop`. Prefer `noop` for
176
+ reviewed candidates that yield no durable harness rule. Use
177
+ `eval_mode=independent_review` when a separate auditor/subagent scored the proposal and
178
+ `eval_mode=dry_run` when independent scoring was unavailable, declined, or still unauthorized after
179
+ asking. The `note` field must explain why a candidate was rejected, no-op'd, or reverted.
180
+
181
+ Minimal proposal template:
182
+
183
+ ```markdown
184
+ # Harness Auto-Evolve Proposal
185
+
186
+ ## Accepted Candidates
187
+
188
+ - [candidate] — evidence: `harness/changes/archive/.../summary.md`
189
+
190
+ ## Rejected Candidates
191
+
192
+ - [candidate] — reason: [no archive evidence / not project-relevant / too broad / too costly]
193
+
194
+ ## Evidence
195
+
196
+ - `harness/changes/archive/.../summary.md`
197
+
198
+ ## Project Relevance
199
+
200
+ - Files/modules/commands/failures/user corrections:
201
+
202
+ ## Target Files
203
+
204
+ - [existing harness file to edit]
205
+
206
+ ## Score
207
+
208
+ | Dimension | Score | Notes |
209
+ |-----------|------:|-------|
210
+ | Evidence grounding /30 | | |
211
+ | Project relevance /25 | | |
212
+ | Mechanical enforceability /15 | | |
213
+ | Regression safety /20 | | |
214
+ | Context cost /10 | | |
215
+
216
+ ## Validation
217
+
218
+ - Harness checks:
219
+ - Relevant business gates:
220
+
221
+ ## Decision
222
+
223
+ - keep / revert / rejected / noop:
224
+ - eval_mode:
225
+ - results.tsv note:
226
+ ```
227
+
228
+ ## 2. Default Change Template
229
+
230
+ Use the 5-file template by default for new active changes. Old archived changes that only have the
231
+ previous 4-file template remain valid and must not fail compatibility checks solely because
232
+ `plan.md` is absent.
233
+
234
+ ```text
235
+ harness/changes/active/
236
+ summary.md
237
+ spec.md
238
+ plan.md
239
+ tasks.md
240
+ reviews/
241
+ ```
242
+
243
+ ### 2.1 `summary.md`
244
+
245
+ ```markdown
246
+ ---
247
+ title: "{{TITLE}}"
248
+ slug: "{{SLUG}}"
249
+ status: "in_progress"
250
+ location: "active"
251
+ phase: "intake"
252
+ intake_status: "pending"
253
+ spec_review: "pending"
254
+ plan_review: "pending"
255
+ modules: []
256
+ files: []
257
+ tags: []
258
+ validation_status: "unknown"
259
+ created_at: "{{DATE}}"
260
+ updated_at: "{{DATE}}"
261
+ ---
262
+
263
+ # Summary
264
+
265
+ ## Outcome
266
+
267
+ Pending.
268
+
269
+ ## Decisions
270
+
271
+ - Pending.
272
+
273
+ ## Validation
274
+
275
+ - Pending.
276
+
277
+ ## Next Step
278
+
279
+ - Run Intake Review, then update `spec.md` and `plan.md`.
280
+ ```
281
+
282
+ ### 2.2 `spec.md`
283
+
284
+ ```markdown
285
+ # Spec
286
+
287
+ ## Intake Review
288
+
289
+ - Intake type: Small Change | Structured Change
290
+ - Input shape: requirement-first | plan-first | mixed
291
+ - Questions asked this round: 0
292
+
293
+ ## Goal And Evidence
294
+
295
+ - Real problem or user request:
296
+ - Current behavior:
297
+ - Source of evidence:
298
+
299
+ ## User Scenarios And Success
300
+
301
+ - Primary user/system scenario:
302
+ - Success criteria:
303
+ - Acceptance criteria:
304
+
305
+ ## Non-Goals
306
+
307
+ - Pending.
308
+
309
+ ## Constraints
310
+
311
+ - Pending.
312
+
313
+ ## Assumptions
314
+
315
+ - Pending.
316
+
317
+ ## Open Questions
318
+
319
+ - [NEEDS CLARIFICATION: Replace with a specific high-impact question, or remove before implementation.]
320
+
321
+ ## Resolved Clarifications
322
+
323
+ - Pending.
324
+ ```
325
+
326
+ ### 2.3 `plan.md`
327
+
328
+ ```markdown
329
+ # Plan
330
+
331
+ ## Technical Approach
332
+
333
+ - Pending.
334
+
335
+ ## Impacted Modules And Files
336
+
337
+ - Pending.
338
+
339
+ ## Interfaces, Data, Permissions
340
+
341
+ - Pending.
342
+
343
+ ## Spec Gaps Found From Planning
344
+
345
+ - Pending.
346
+
347
+ ## Risks And Mitigations
348
+
349
+ - Pending.
350
+
351
+ ## Verification Plan
352
+
353
+ - Pending.
354
+ ```
355
+
356
+ ### 2.4 `tasks.md`
357
+
358
+ ```markdown
359
+ # Tasks
360
+
361
+ ## Format
362
+
363
+ - `- [ ] T001 [P?] [US?] Action with target path and validation note`
364
+ - `[P]` means parallel-safe. `[US1]` maps to a user story when stories exist.
365
+
366
+ ## Setup / Intake
367
+
368
+ - [ ] T001 Review `spec.md` and `plan.md` gates before implementation.
369
+
370
+ ## Implementation
371
+
372
+ - [ ] T002 Pending implementation task with target path.
373
+
374
+ ## Validation
375
+
376
+ - [ ] T003 Pending validation task with command or scenario.
377
+
378
+ ## Deferred Tasks
379
+
380
+ - None.
381
+ ```
382
+
383
+ ### 2.5 `reviews/review.md`
384
+
385
+ ```markdown
386
+ # Review
387
+
388
+ ## Intake Review
389
+
390
+ - Status: pending
391
+ - Notes:
392
+
393
+ ## Spec Review
394
+
395
+ - Status: pending
396
+ - Open high-impact clarifications:
397
+ - WHAT/HOW separation:
398
+
399
+ ## Plan Review
400
+
401
+ - Status: pending
402
+ - Spec gaps found from planning:
403
+
404
+ ## Code Review
405
+
406
+ - Status: pending
407
+
408
+ ## Validation Review
409
+
410
+ - Status: pending
411
+ ```
412
+
413
+ Complex tasks may split `spec.md`, `plan.md`, or `tasks.md` into focused supporting files only when
414
+ the single files become hard to navigate. Do not split small or ordinary changes by default.
415
+
416
+ ### 2.6 Short response examples
417
+
418
+ Small Change final response:
419
+
420
+ ```markdown
421
+ Updated the README typo. I treated this as a Small Change because it only touched documentation copy
422
+ and had no runtime, API, data, permission, or architecture impact.
423
+
424
+ Verification: reviewed the README diff.
425
+ ```
426
+
427
+ Structured blocker response:
428
+
429
+ ```markdown
430
+ I cannot safely move this to implementation yet. The draft plan leaves high-impact gaps:
431
+
432
+ 1. Which roles or actors must be supported?
433
+ 2. Which resources/actions require enforcement?
434
+ 3. What acceptance scenario proves the change works?
435
+
436
+ I would record these in `spec.md` as `[NEEDS CLARIFICATION: ...]` and keep `plan_review: pending`.
437
+ ```
438
+
439
+ Complete plan accepted response:
440
+
441
+ ```markdown
442
+ The provided plan includes goal, acceptance criteria, non-goals, constraints, verification commands,
443
+ and no conflicts with repository evidence. I would split it into `spec.md` for WHAT/WHY, `plan.md`
444
+ for HOW, generate `tasks.md`, and proceed without another intake interview.
445
+ ```
446
+
447
+ ## 3. Context Loading
448
+
449
+ Load context progressively:
450
+
451
+ 1. `AGENTS.md`
452
+ 2. `docs/ECL.md`
453
+ 3. If active exists: `harness/changes/active/summary.md`
454
+ 4. If active exists: `harness/changes/active/spec.md`, `plan.md`, `tasks.md`, and relevant `reviews/`
455
+ 5. If no active exists and `harness/evolution/pending.md` exists: read it before `docs/STATUS.md`,
456
+ mention it as pending maintenance, and ask whether to handle it now unless the user already
457
+ prioritized the current task. Asking or reading does not start auto-evolve.
458
+ 6. If no active exists and no pending evolution exists: `docs/STATUS.md`
459
+ 7. Relevant architecture, design, API, or reference docs for the task
460
+ 8. `harness/changes/INDEX.json` only when selecting historical context
461
+ 9. Selected archived `summary.md` files only
462
+ 10. Selected archived spec/plan/tasks/reviews only for explicit resume, review, failure debugging, or auto-evolve evidence extraction
463
+
464
+ The normal path reads current task context and stable docs. If active change exists, do not use
465
+ `docs/STATUS.md` as authority. Archive history is never loaded wholesale. Search or read history
466
+ only when the user asks to continue prior work, STATUS points to an archive path, the current task
467
+ matches INDEX modules/files/tags, a current failure matches historical validation notes, or
468
+ `harness/evolution/pending.md` records a bounded trigger snapshot. When processing starts, refresh
469
+ `harness/changes/INDEX.json` and use the current eligible archive window.
470
+
471
+ ## 4. `docs/ECL.md` Required Contents
472
+
473
+ Create `docs/ECL.md` with these sections:
474
+
475
+ 1. Purpose and scope
476
+ 2. When to create a change
477
+ 3. Change lifecycle and state transitions
478
+ 4. Stage-boundary update protocol
479
+ 5. Plan review gate before implementation
480
+ 6. Context loading order
481
+ 7. STATUS handoff update before and after closing active work
482
+ 8. Selective archive loading through STATUS and `INDEX.json`
483
+ 9. Failure feedback and regression capture
484
+ 10. Auto-evolve threshold checking and pending handling
485
+ 11. Script command reference
486
+ 12. Rule: `INDEX.json` is generated by script only
487
+
488
+ Keep philosophy brief. This is an operating manual.
489
+
490
+ ## 5. Command Surface Profiles
491
+
492
+ Select one script profile and wire all docs, CI, Makefile targets, and package-manager scripts to
493
+ that profile. Do not mix profiles unless the project deliberately exposes aliases to the same
494
+ implementation. Choose automatically from project evidence; do not stop for human selection unless
495
+ the evidence is contradictory or no supported profile is available.
496
+
497
+ Supported profiles:
498
+
499
+ - **Bash profile**: use `.sh` scripts and commands such as `bash scripts/lint-ecl.sh`. On Windows,
500
+ document Git Bash, WSL, MSYS2, or CI Linux shell as a prerequisite.
501
+ - **PowerShell profile**: use `.ps1` scripts. Detect whether `pwsh` is available before wiring
502
+ commands; if it is not, use `powershell -NoProfile -ExecutionPolicy Bypass`.
503
+ - **Node profile**: use `.mjs` scripts when TypeScript/Node is already the primary project runtime.
504
+ - **Python profile**: use `.py` scripts when Python is already a primary project runtime.
505
+
506
+ Every profile must implement the same commands and invariants:
507
+
508
+ - `harness-change`: `new`, `status`, `validate`, `park`, `resume`, `close`, `search`, `context`,
509
+ `reindex`, and `index-json`.
510
+ - `harness-evolve`: `check`, `collect`, and `mark-complete`.
511
+ - `lint-ecl`: active change structure, STATUS presence, completed validation, pending task
512
+ consistency, generated index freshness, and auto-evolve state presence.
513
+ - `lint-encoding`: UTF-8 and mojibake-risk checks for source and docs.
514
+ - `close` and `reindex` must trigger `harness-evolve check`; hooks and CI may validate but must not
515
+ rewrite docs, move changes, or update STATUS.
516
+
517
+ ### PowerShell Profile Templates
518
+
519
+ Use these templates only when the selected command surface is PowerShell. Keep them compatible with
520
+ Windows PowerShell 5.1 and PowerShell 7; avoid non-ASCII marker literals and ambiguous method
521
+ overloads in `.ps1` templates.
522
+
523
+ ### 5.1 `scripts/harness-change.ps1`
524
+
525
+ ```powershell
526
+ param(
527
+ [Parameter(Position = 0)]
528
+ [ValidateSet("new", "status", "validate", "park", "resume", "close", "search", "context", "reindex", "index-json")]
529
+ [string]$Command,
530
+
531
+ [Parameter(Position = 1, ValueFromRemainingArguments = $true)]
532
+ [string[]]$Args
533
+ )
534
+
535
+ $ErrorActionPreference = "Stop"
536
+
537
+ $Root = (Get-Location).Path
538
+ $Changes = Join-Path $Root "harness/changes"
539
+ $Active = Join-Path $Changes "active"
540
+ $Parking = Join-Path $Changes "parking"
541
+ $Archive = Join-Path $Changes "archive"
542
+ $IndexPath = Join-Path $Changes "INDEX.json"
543
+ $Template = Join-Path $Root "harness/templates/change"
544
+ $Evolution = Join-Path $Root "harness/evolution"
545
+ $EvolutionPending = Join-Path $Evolution "pending.md"
546
+ $HarnessEvolve = Join-Path $Root "scripts/harness-evolve.ps1"
547
+
548
+ function Ensure-Dirs {
549
+ foreach ($dir in @($Changes, $Active, $Parking, $Archive, $Template, (Join-Path $Template "reviews"), $Evolution, (Join-Path $Evolution "proposals"))) {
550
+ if (-not (Test-Path -LiteralPath $dir)) {
551
+ New-Item -ItemType Directory -Path $dir | Out-Null
552
+ }
553
+ }
554
+ }
555
+
556
+ function Get-DateText {
557
+ return (Get-Date).ToString("yyyy-MM-dd")
558
+ }
559
+
560
+ function ConvertTo-Slug([string]$Text) {
561
+ $slug = $Text.ToLowerInvariant() -replace "[^a-z0-9]+", "-"
562
+ $slug = $slug.Trim("-")
563
+ if ([string]::IsNullOrWhiteSpace($slug)) { $slug = "change" }
564
+ return $slug
565
+ }
566
+
567
+ function Read-Text([string]$Path) {
568
+ if (-not (Test-Path -LiteralPath $Path)) { return "" }
569
+ return Get-Content -Encoding UTF8 -Raw -LiteralPath $Path
570
+ }
571
+
572
+ function Write-Text([string]$Path, [string]$Content) {
573
+ $parent = Split-Path -Parent $Path
574
+ if ($parent -and -not (Test-Path -LiteralPath $parent)) {
575
+ New-Item -ItemType Directory -Path $parent | Out-Null
576
+ }
577
+ Set-Content -Encoding UTF8 -LiteralPath $Path -Value $Content
578
+ }
579
+
580
+ function Parse-FrontMatter([string]$Path) {
581
+ $text = Read-Text $Path
582
+ $result = @{}
583
+ if (-not $text.StartsWith("---")) { return $result }
584
+ $lines = $text -split "`r?`n"
585
+ for ($i = 1; $i -lt $lines.Length; $i++) {
586
+ if ($lines[$i] -eq "---") { break }
587
+ if ($lines[$i] -match "^\s*([^:#]+):\s*(.*)\s*$") {
588
+ $key = $Matches[1].Trim()
589
+ $value = $Matches[2].Trim().Trim('"')
590
+ if ($value -match "^\[(.*)\]$") {
591
+ $items = $Matches[1].Split(",") | ForEach-Object { $_.Trim().Trim('"') } | Where-Object { $_ }
592
+ $result[$key] = @($items)
593
+ } else {
594
+ $result[$key] = $value
595
+ }
596
+ }
597
+ }
598
+ return $result
599
+ }
600
+
601
+ function Set-FrontMatterValues([string]$Path, [hashtable]$Values) {
602
+ $text = Read-Text $Path
603
+ if (-not $text.StartsWith("---")) { throw "$Path has no front matter." }
604
+ foreach ($key in $Values.Keys) {
605
+ $value = $Values[$key]
606
+ $line = if ($value -is [array]) {
607
+ $quoted = $value | ForEach-Object { '"' + ($_ -replace '"', '\"') + '"' }
608
+ "${key}: [" + ($quoted -join ", ") + "]"
609
+ } else {
610
+ "${key}: `"$value`""
611
+ }
612
+ if ($text -match "(?m)^$([regex]::Escape($key)):\s*.*$") {
613
+ $text = [regex]::Replace($text, "(?m)^$([regex]::Escape($key)):\s*.*$", [System.Text.RegularExpressions.MatchEvaluator]{ param($m) $line }, 1)
614
+ } else {
615
+ $lines = $text -split "`r?`n"
616
+ for ($i = 1; $i -lt $lines.Length; $i++) {
617
+ if ($lines[$i] -eq "---") {
618
+ $before = @($lines[0..($i - 1)])
619
+ $after = @($lines[$i..($lines.Length - 1)])
620
+ $text = (@($before + $line + $after) -join "`n")
621
+ break
622
+ }
623
+ }
624
+ }
625
+ }
626
+ Write-Text $Path $text
627
+ }
628
+
629
+ function Get-SectionLines([string]$Path, [string]$Heading) {
630
+ $text = Read-Text $Path
631
+ if (-not $text) { return @() }
632
+ $lines = $text -split "`r?`n"
633
+ $found = $false
634
+ $items = New-Object System.Collections.Generic.List[string]
635
+ foreach ($line in $lines) {
636
+ if ($line -match "^##\s+$([regex]::Escape($Heading))\s*$") {
637
+ $found = $true
638
+ continue
639
+ }
640
+ if ($found -and $line -match "^##\s+") { break }
641
+ if ($found -and $line.Trim()) {
642
+ $items.Add($line.Trim())
643
+ }
644
+ }
645
+ return @($items)
646
+ }
647
+
648
+ function Get-ValidationStatus([string]$SummaryPath, [hashtable]$Meta) {
649
+ if ($Meta.ContainsKey("validation_status") -and $Meta["validation_status"]) {
650
+ return $Meta["validation_status"]
651
+ }
652
+ $validation = (Get-SectionLines $SummaryPath "Validation") -join " "
653
+ if ($validation -match "(?i)\bpass(ed)?\b|success|ok") { return "pass" }
654
+ if ($validation -match "(?i)\bfail(ed)?\b|error|blocked") { return "fail" }
655
+ return "unknown"
656
+ }
657
+
658
+ function Get-IndexEntries {
659
+ $entries = New-Object System.Collections.Generic.List[object]
660
+ foreach ($pair in @(@("parking", $Parking), @("archive", $Archive))) {
661
+ $location = $pair[0]
662
+ $base = $pair[1]
663
+ if (-not (Test-Path -LiteralPath $base)) { continue }
664
+ Get-ChildItem -LiteralPath $base -Directory | ForEach-Object {
665
+ $summary = Join-Path $_.FullName "summary.md"
666
+ if (-not (Test-Path -LiteralPath $summary)) { return }
667
+ $meta = Parse-FrontMatter $summary
668
+ $decisions = Get-SectionLines $summary "Decisions" | Where-Object { $_ -notmatch "Pending" }
669
+ $relativePath = (Resolve-Path -LiteralPath $_.FullName -Relative).TrimStart([char[]]@(".", "\"))
670
+ $entries.Add([ordered]@{
671
+ id = $_.Name
672
+ title = $meta["title"]
673
+ status = $meta["status"]
674
+ location = $location
675
+ modules = @($meta["modules"])
676
+ files = @($meta["files"])
677
+ tags = @($meta["tags"])
678
+ decisions = @($decisions)
679
+ validation_status = Get-ValidationStatus $summary $meta
680
+ path = ($relativePath -replace "\\", "/")
681
+ updated_at = $meta["updated_at"]
682
+ })
683
+ }
684
+ }
685
+ return $entries.ToArray()
686
+ }
687
+
688
+ function Get-IndexJson {
689
+ $entries = Get-IndexEntries
690
+ if ($entries.Count -eq 0) { return "[]`n" }
691
+ $json = $entries | ConvertTo-Json -Depth 8
692
+ return ($json + "`n")
693
+ }
694
+
695
+ function Reindex {
696
+ Ensure-Dirs
697
+ $entries = Get-IndexEntries
698
+ Write-Text $IndexPath (Get-IndexJson)
699
+ Write-Output "Rebuilt harness/changes/INDEX.json ($($entries.Count) entries)."
700
+ }
701
+
702
+ function Invoke-EvolutionCheck([string]$Reason) {
703
+ if (-not (Test-Path -LiteralPath $HarnessEvolve)) { return }
704
+ try {
705
+ & $HarnessEvolve check -Reason $Reason
706
+ } catch {
707
+ Write-Warning "Auto-evolve check failed: $($_.Exception.Message)"
708
+ }
709
+ }
710
+
711
+ function Show-EvolutionReminder {
712
+ if (Test-Path -LiteralPath $EvolutionPending) {
713
+ Write-Warning "Harness evolution is pending: harness/evolution/pending.md"
714
+ }
715
+ }
716
+
717
+ function Assert-NoActive {
718
+ $summary = Join-Path $Active "summary.md"
719
+ if (Test-Path -LiteralPath $summary) {
720
+ throw "Active change exists. Run 'status', then 'park', 'close', or finish it before starting a new change."
721
+ }
722
+ }
723
+
724
+ function New-Change([string]$Title) {
725
+ Ensure-Dirs
726
+ if ([string]::IsNullOrWhiteSpace($Title)) { throw "Missing title." }
727
+ Assert-NoActive
728
+ $date = Get-DateText
729
+ $slug = ConvertTo-Slug $Title
730
+ Write-Text (Join-Path $Active "summary.md") @"
731
+ ---
732
+ title: "$Title"
733
+ slug: "$slug"
734
+ status: "in_progress"
735
+ location: "active"
736
+ phase: "intake"
737
+ intake_status: "pending"
738
+ spec_review: "pending"
739
+ plan_review: "pending"
740
+ modules: []
741
+ files: []
742
+ tags: []
743
+ validation_status: "unknown"
744
+ created_at: "$date"
745
+ updated_at: "$date"
746
+ ---
747
+
748
+ # Summary
749
+
750
+ ## Outcome
751
+
752
+ Pending.
753
+
754
+ ## Decisions
755
+
756
+ - Pending.
757
+
758
+ ## Validation
759
+
760
+ - Pending.
761
+
762
+ ## Next Step
763
+
764
+ - Run Intake Review, then update ``spec.md`` and ``plan.md``.
765
+ "@
766
+ Write-Text (Join-Path $Active "spec.md") @"
767
+ # Spec
768
+
769
+ ## Intake Review
770
+
771
+ - Intake type: Small Change | Structured Change
772
+ - Input shape: requirement-first | plan-first | mixed
773
+ - Questions asked this round: 0
774
+
775
+ ## Goal And Evidence
776
+
777
+ - Real problem or user request:
778
+ - Current behavior:
779
+ - Source of evidence:
780
+
781
+ ## User Scenarios And Success
782
+
783
+ - Primary user/system scenario:
784
+ - Success criteria:
785
+ - Acceptance criteria:
786
+
787
+ ## Non-Goals
788
+
789
+ - Pending.
790
+
791
+ ## Constraints
792
+
793
+ - Pending.
794
+
795
+ ## Assumptions
796
+
797
+ - Pending.
798
+
799
+ ## Open Questions
800
+
801
+ - [NEEDS CLARIFICATION: Replace with a specific high-impact question, or remove before implementation.]
802
+
803
+ ## Resolved Clarifications
804
+
805
+ - Pending.
806
+ "@
807
+ Write-Text (Join-Path $Active "plan.md") @"
808
+ # Plan
809
+
810
+ ## Technical Approach
811
+
812
+ - Pending.
813
+
814
+ ## Impacted Modules And Files
815
+
816
+ - Pending.
817
+
818
+ ## Interfaces, Data, Permissions
819
+
820
+ - Pending.
821
+
822
+ ## Spec Gaps Found From Planning
823
+
824
+ - Pending.
825
+
826
+ ## Risks And Mitigations
827
+
828
+ - Pending.
829
+
830
+ ## Verification Plan
831
+
832
+ - Pending.
833
+ "@
834
+ Write-Text (Join-Path $Active "tasks.md") @"
835
+ # Tasks
836
+
837
+ ## Format
838
+
839
+ - ``- [ ] T001 [P?] [US?] Action with target path and validation note``
840
+ - ``[P]`` means parallel-safe. ``[US1]`` maps to a user story when stories exist.
841
+
842
+ ## Setup / Intake
843
+
844
+ - [ ] T001 Review ``spec.md`` and ``plan.md`` gates before implementation.
845
+
846
+ ## Implementation
847
+
848
+ - [ ] T002 Pending implementation task with target path.
849
+
850
+ ## Validation
851
+
852
+ - [ ] T003 Pending validation task with command or scenario.
853
+
854
+ ## Deferred Tasks
855
+
856
+ - None.
857
+ "@
858
+ Write-Text (Join-Path $Active "reviews/review.md") @"
859
+ # Review
860
+
861
+ ## Intake Review
862
+
863
+ - Status: pending
864
+ - Notes:
865
+
866
+ ## Spec Review
867
+
868
+ - Status: pending
869
+ - Open high-impact clarifications:
870
+ - WHAT/HOW separation:
871
+
872
+ ## Plan Review
873
+
874
+ - Status: pending
875
+ - Spec gaps found from planning:
876
+
877
+ ## Code Review
878
+
879
+ - Status: pending
880
+
881
+ ## Validation Review
882
+
883
+ - Status: pending
884
+ "@
885
+ Write-Output "Created active change: $Title"
886
+ Show-EvolutionReminder
887
+ }
888
+
889
+ function Validate-Change([string]$Dir) {
890
+ $required = @("summary.md", "spec.md", "tasks.md")
891
+ $isActive = ((Resolve-Path -LiteralPath $Dir).Path -eq (Resolve-Path -LiteralPath $Active).Path)
892
+ if ($isActive) {
893
+ $required = @("summary.md", "spec.md", "plan.md", "tasks.md")
894
+ }
895
+ foreach ($file in $required) {
896
+ if (-not (Test-Path -LiteralPath (Join-Path $Dir $file))) {
897
+ throw "Missing $file in $Dir"
898
+ }
899
+ }
900
+ if (-not (Test-Path -LiteralPath (Join-Path $Dir "reviews"))) {
901
+ throw "Missing reviews/ in $Dir"
902
+ }
903
+ $summary = Join-Path $Dir "summary.md"
904
+ $meta = Parse-FrontMatter $summary
905
+ $status = $meta["status"]
906
+ if (-not $status) { throw "summary.md missing status front matter." }
907
+ $phase = $meta["phase"]
908
+ $planReview = $meta["plan_review"]
909
+ $spec = Read-Text (Join-Path $Dir "spec.md")
910
+ $reviewText = ""
911
+ $reviewPath = Join-Path $Dir "reviews/review.md"
912
+ if (Test-Path -LiteralPath $reviewPath) { $reviewText = Read-Text $reviewPath }
913
+ $tasks = Read-Text (Join-Path $Dir "tasks.md")
914
+ if ($isActive -and $phase -match "^(implement|validate|done)$" -and $spec -match "\[NEEDS CLARIFICATION:") {
915
+ throw "spec.md has high-impact [NEEDS CLARIFICATION] markers. Resolve them or move the change back to intake/plan before implementation."
916
+ }
917
+ if ($isActive -and $phase -match "^(implement|validate|done)$" -and $planReview -ne "approved" -and $reviewText -notmatch "(?is)Plan Review.*Status:\s*approved") {
918
+ throw "summary.md plan_review must be approved before implementation. Record plan approval in summary.md or reviews/review.md."
919
+ }
920
+ if ($isActive -and $tasks -match "(?m)^- \[[ xX]\] (?!T\d{3})" -and $tasks -notmatch "## Deferred Tasks") {
921
+ throw "tasks.md task lines must use T### ids with target paths and validation notes so agents can execute them predictably."
922
+ }
923
+ if ($status -eq "completed") {
924
+ $validation = Get-ValidationStatus $summary $meta
925
+ if ($validation -ne "pass") { throw "completed change must have validation_status: pass or a passing Validation section." }
926
+ if ($tasks -match "- \[ \] " -and $tasks -notmatch "## Deferred Tasks\s+(\r?\n)+-\s+(None|Deferred|Explained)") {
927
+ throw "completed change has pending tasks without a Deferred Tasks explanation."
928
+ }
929
+ }
930
+ }
931
+
932
+ function Show-Status {
933
+ Ensure-Dirs
934
+ $summary = Join-Path $Active "summary.md"
935
+ if (-not (Test-Path -LiteralPath $summary)) {
936
+ Write-Output "No active change."
937
+ return
938
+ }
939
+ $meta = Parse-FrontMatter $summary
940
+ Write-Output "Active: $($meta["title"])"
941
+ Write-Output "Status: $($meta["status"])"
942
+ Write-Output "Phase: $($meta["phase"])"
943
+ }
944
+
945
+ function New-ClosedId([hashtable]$Meta) {
946
+ $date = Get-DateText
947
+ if ($Meta["updated_at"] -match "^\d{4}-\d{2}-\d{2}$") { $date = $Meta["updated_at"] }
948
+ $slug = $Meta["slug"]
949
+ if (-not $slug) { $slug = ConvertTo-Slug $Meta["title"] }
950
+ $id = "$date-$slug"
951
+ return $id
952
+ }
953
+
954
+ function Move-Active([string]$TargetBase, [string]$Status, [string]$Reason) {
955
+ Ensure-Dirs
956
+ $summary = Join-Path $Active "summary.md"
957
+ if (-not (Test-Path -LiteralPath $summary)) { throw "No active change." }
958
+ $meta = Parse-FrontMatter $summary
959
+ if ($TargetBase -eq $Archive -and $Status -notin @("completed", "blocked", "abandoned")) {
960
+ throw "close status must be completed, blocked, or abandoned."
961
+ }
962
+ if ($Status -eq "completed") { Validate-Change $Active }
963
+ $targetLocation = if ($TargetBase -eq $Parking) { "parking" } else { "archive" }
964
+ $newStatus = if ($TargetBase -eq $Parking) { "parked" } else { $Status }
965
+ Set-FrontMatterValues $summary @{
966
+ status = $newStatus
967
+ location = $targetLocation
968
+ updated_at = (Get-DateText)
969
+ }
970
+ if ($Reason) {
971
+ Add-Content -Encoding UTF8 -LiteralPath $summary -Value "`n## Transition Note`n`n- $Reason`n"
972
+ }
973
+ $meta = Parse-FrontMatter $summary
974
+ $id = New-ClosedId $meta
975
+ $target = Join-Path $TargetBase $id
976
+ $n = 2
977
+ while (Test-Path -LiteralPath $target) {
978
+ $target = Join-Path $TargetBase "$id-$n"
979
+ $n++
980
+ }
981
+ Move-Item -LiteralPath $Active -Destination $target
982
+ New-Item -ItemType Directory -Path $Active | Out-Null
983
+ Reindex
984
+ if ($TargetBase -eq $Archive) { Invoke-EvolutionCheck "close" }
985
+ Write-Output "Moved active change to $target"
986
+ }
987
+
988
+ function Resume-Change([string]$Id) {
989
+ Ensure-Dirs
990
+ Assert-NoActive
991
+ $source = Join-Path $Parking $Id
992
+ if (-not (Test-Path -LiteralPath $source)) { throw "Parking change not found: $Id" }
993
+ $summary = Join-Path $source "summary.md"
994
+ if (Test-Path -LiteralPath $summary) {
995
+ Set-FrontMatterValues $summary @{
996
+ status = "in_progress"
997
+ location = "active"
998
+ updated_at = (Get-DateText)
999
+ }
1000
+ }
1001
+ Move-Item -LiteralPath $source -Destination $Active
1002
+ Reindex
1003
+ Write-Output "Resumed $Id into active. Run validate before continuing."
1004
+ }
1005
+
1006
+ function Search-Index([string]$Query) {
1007
+ if (-not (Test-Path -LiteralPath $IndexPath)) { Reindex }
1008
+ $items = Get-Content -Encoding UTF8 -Raw -LiteralPath $IndexPath | ConvertFrom-Json
1009
+ $items | Where-Object { ($_ | ConvertTo-Json -Depth 8) -match [regex]::Escape($Query) } |
1010
+ Select-Object id,title,status,location,path,validation_status | Format-Table -AutoSize
1011
+ }
1012
+
1013
+ function Show-Context {
1014
+ Write-Output "Required:"
1015
+ foreach ($p in @("AGENTS.md", "docs/ECL.md", "harness/changes/active/summary.md", "harness/changes/active/spec.md", "harness/changes/active/plan.md", "harness/changes/active/tasks.md")) {
1016
+ if (Test-Path -LiteralPath (Join-Path $Root $p)) { Write-Output "- $p" }
1017
+ }
1018
+ if (-not (Test-Path -LiteralPath (Join-Path $Root "harness/changes/active/summary.md")) -and
1019
+ (Test-Path -LiteralPath $EvolutionPending)) {
1020
+ Write-Output "- harness/evolution/pending.md"
1021
+ }
1022
+ if (-not (Test-Path -LiteralPath (Join-Path $Root "harness/changes/active/summary.md")) -and
1023
+ (Test-Path -LiteralPath (Join-Path $Root "docs/STATUS.md"))) {
1024
+ Write-Output "- docs/STATUS.md"
1025
+ }
1026
+ Write-Output ""
1027
+ Write-Output "History index:"
1028
+ if (Test-Path -LiteralPath $IndexPath) { Write-Output "- harness/changes/INDEX.json" } else { Write-Output "- Run scripts/harness-change.ps1 reindex" }
1029
+ }
1030
+
1031
+ Ensure-Dirs
1032
+ switch ($Command) {
1033
+ "new" { New-Change ($Args -join " ") }
1034
+ "status" { Show-Status }
1035
+ "validate" { Validate-Change $Active; Write-Output "ECL active change is valid." }
1036
+ "park" { Move-Active $Parking "parked" ($Args -join " ") }
1037
+ "resume" { Resume-Change $Args[0] }
1038
+ "close" { Move-Active $Archive $Args[0] "" }
1039
+ "search" { Search-Index ($Args -join " ") }
1040
+ "context" { Show-Context }
1041
+ "reindex" { Reindex; Invoke-EvolutionCheck "reindex" }
1042
+ "index-json" { Write-Output (Get-IndexJson) }
1043
+ default { throw "Command required: new/status/validate/park/resume/close/search/context/reindex" }
1044
+ }
1045
+ ```
1046
+
1047
+ ### 5.2 `scripts/harness-evolve.ps1`
1048
+
1049
+ ```powershell
1050
+ param(
1051
+ [Parameter(Position = 0)]
1052
+ [ValidateSet("check", "collect", "mark-complete")]
1053
+ [string]$Command = "check",
1054
+
1055
+ [int]$Threshold = 5,
1056
+ [int]$Window = 10,
1057
+ [string]$Reason = "manual"
1058
+ )
1059
+
1060
+ $ErrorActionPreference = "Stop"
1061
+
1062
+ $Root = (Get-Location).Path
1063
+ $Changes = Join-Path $Root "harness/changes"
1064
+ $IndexPath = Join-Path $Changes "INDEX.json"
1065
+ $Evolution = Join-Path $Root "harness/evolution"
1066
+ $StatePath = Join-Path $Evolution "state.json"
1067
+ $PendingPath = Join-Path $Evolution "pending.md"
1068
+ $ResultsPath = Join-Path $Evolution "results.tsv"
1069
+ $Proposals = Join-Path $Evolution "proposals"
1070
+
1071
+ function Ensure-EvolutionDirs {
1072
+ foreach ($dir in @($Evolution, $Proposals)) {
1073
+ if (-not (Test-Path -LiteralPath $dir)) {
1074
+ New-Item -ItemType Directory -Path $dir | Out-Null
1075
+ }
1076
+ }
1077
+ }
1078
+
1079
+ function Write-Text([string]$Path, [string]$Content) {
1080
+ $parent = Split-Path -Parent $Path
1081
+ if ($parent -and -not (Test-Path -LiteralPath $parent)) {
1082
+ New-Item -ItemType Directory -Path $parent | Out-Null
1083
+ }
1084
+ Set-Content -Encoding UTF8 -LiteralPath $Path -Value $Content
1085
+ }
1086
+
1087
+ function Read-State {
1088
+ Ensure-EvolutionDirs
1089
+ if (-not (Test-Path -LiteralPath $StatePath)) {
1090
+ $initial = [ordered]@{
1091
+ enabled = $true
1092
+ threshold = $Threshold
1093
+ window = $Window
1094
+ last_evolved_archive_count = 0
1095
+ last_evolved_change_id = $null
1096
+ last_score = $null
1097
+ last_run_at = $null
1098
+ pending = $false
1099
+ }
1100
+ Write-Text $StatePath (($initial | ConvertTo-Json -Depth 5) + "`n")
1101
+ }
1102
+ return Get-Content -Encoding UTF8 -Raw -LiteralPath $StatePath | ConvertFrom-Json
1103
+ }
1104
+
1105
+ function Write-State($State) {
1106
+ Write-Text $StatePath (($State | ConvertTo-Json -Depth 8) + "`n")
1107
+ }
1108
+
1109
+ function Test-AutoEvolveArchive($Item) {
1110
+ $id = [string]$Item.id
1111
+ if ($id -match "^auto-evolve-harness-") { return $true }
1112
+ foreach ($tag in @($Item.tags)) {
1113
+ if ([string]$tag -eq "auto-evolve") { return $true }
1114
+ }
1115
+ return $false
1116
+ }
1117
+
1118
+ function Get-ArchiveItems {
1119
+ if (-not (Test-Path -LiteralPath $IndexPath)) {
1120
+ throw "Missing harness/changes/INDEX.json. Run scripts/harness-change.ps1 reindex first."
1121
+ }
1122
+ $raw = Get-Content -Encoding UTF8 -Raw -LiteralPath $IndexPath
1123
+ if ([string]::IsNullOrWhiteSpace($raw)) { return @() }
1124
+ $parsed = $raw | ConvertFrom-Json
1125
+ $items = @($parsed | ForEach-Object { $_ })
1126
+ return @($items | Where-Object {
1127
+ $_.location -eq "archive" -and -not (Test-AutoEvolveArchive $_)
1128
+ } | Sort-Object updated_at, id)
1129
+ }
1130
+
1131
+ function Ensure-ResultsHeader {
1132
+ if (-not (Test-Path -LiteralPath $ResultsPath)) {
1133
+ Write-Text $ResultsPath "timestamp`tchange_id`told_score`tnew_score`tstatus`tdimension`tnote`teval_mode`n"
1134
+ }
1135
+ }
1136
+
1137
+ function Add-ResultRow([string]$ChangeId, [string]$OldScore, [string]$NewScore, [string]$Status, [string]$Dimension, [string]$Note, [string]$EvalMode) {
1138
+ Ensure-ResultsHeader
1139
+ if ($Status -notin @("keep", "revert", "rejected", "noop")) {
1140
+ throw "status must be keep, revert, rejected, or noop."
1141
+ }
1142
+ if ($EvalMode -notin @("independent_review", "dry_run", "full_test")) {
1143
+ throw "eval_mode must be independent_review, dry_run, or full_test."
1144
+ }
1145
+ $timestamp = (Get-Date).ToString("s")
1146
+ $line = @($timestamp, $ChangeId, $OldScore, $NewScore, $Status, $Dimension, ($Note -replace "`t", " "), $EvalMode) -join "`t"
1147
+ Add-Content -Encoding UTF8 -LiteralPath $ResultsPath -Value $line
1148
+ }
1149
+
1150
+ function New-Pending([object[]]$ArchiveItems, $State, [string]$TriggerReason) {
1151
+ if (Test-Path -LiteralPath $PendingPath) {
1152
+ Write-Output "Harness evolution already pending: harness/evolution/pending.md"
1153
+ return
1154
+ }
1155
+ $candidateItems = @($ArchiveItems | Select-Object -Last $State.window)
1156
+ $candidateLines = @()
1157
+ foreach ($item in $candidateItems) {
1158
+ $path = [string]$item.path
1159
+ if (-not $path) { $path = "harness/changes/archive/$($item.id)" }
1160
+ $candidateLines += "- $path/summary.md"
1161
+ }
1162
+ $now = (Get-Date).ToString("s")
1163
+ $content = @'
1164
+ # Harness Evolution Pending
1165
+
1166
+ Generated at: {{NOW}}
1167
+
1168
+ ## Trigger
1169
+
1170
+ - Reason: {{TRIGGER_REASON}}
1171
+ - Eligible archived changes since last evolution: {{DELTA}}
1172
+ - Threshold: {{THRESHOLD}}
1173
+ - Scan window: {{WINDOW}}
1174
+ - INDEX source: harness/changes/INDEX.json
1175
+ - Excludes: archive ids beginning with auto-evolve-harness- and archives tagged auto-evolve
1176
+
1177
+ ## Candidate Archives
1178
+
1179
+ {{CANDIDATE_LINES}}
1180
+
1181
+ These candidates are the trigger snapshot. Before processing, rebuild `harness/changes/INDEX.json`
1182
+ and use the current eligible archive window so changes closed after this file was generated are not
1183
+ missed.
1184
+
1185
+ ## Instruction For Codex
1186
+
1187
+ Run harness auto-evolve:
1188
+ 1. Read docs/ECL.md and this pending file.
1189
+ 2. Rebuild `harness/changes/INDEX.json`, then inspect the current eligible archive window first.
1190
+ 3. Read spec/plan/tasks/reviews only when evidence requires it.
1191
+ 4. Extract repeated failures, verification gaps, user corrections, and reusable constraints.
1192
+ 5. Generate `harness/evolution/proposals/YYYY-MM-DD-auto-evolve.md` from the proposal template in docs/ECL.md or references/ecl-harness.md before editing harness files.
1193
+ 6. Request one independent auditor/subagent score before applying. User approval to handle pending
1194
+ implies permission to request independent review when available. If the environment still
1195
+ requires explicit authorization, ask once. If scoring is unavailable, declined, or still
1196
+ unauthorized after asking, record `status=noop` with `eval_mode=dry_run` and do not auto-apply.
1197
+ 7. Apply only accepted candidates with archive evidence, project relevance, score >= 80, and independent approval.
1198
+ 8. Prefer clarifying existing rules over adding new sections, documents, scripts, or workflows.
1199
+ 9. Run harness checks and relevant business gates.
1200
+ 10. Record one terminal result in `harness/evolution/results.tsv`: `keep` for accepted deltas,
1201
+ `noop` for reviewed evidence with no durable rule, `rejected` for pre-apply hard-gate
1202
+ failures, or `revert` if an applied delta fails validation.
1203
+ 11. Run `harness-evolve mark-complete` after writing the result. If you cannot complete these
1204
+ steps, park or close blocked; do not close completed while leaving the same pending file.
1205
+ '@
1206
+ $content = $content.Replace("{{NOW}}", $now)
1207
+ $content = $content.Replace("{{TRIGGER_REASON}}", $TriggerReason)
1208
+ $content = $content.Replace("{{DELTA}}", [string]($ArchiveItems.Count - [int]$State.last_evolved_archive_count))
1209
+ $content = $content.Replace("{{THRESHOLD}}", [string]$State.threshold)
1210
+ $content = $content.Replace("{{WINDOW}}", [string]$State.window)
1211
+ $content = $content.Replace("{{CANDIDATE_LINES}}", ($candidateLines -join "`n"))
1212
+ Write-Text $PendingPath $content
1213
+ $State.pending = $true
1214
+ Write-State $State
1215
+ Write-Output "Created harness/evolution/pending.md"
1216
+ }
1217
+
1218
+ function Check-Evolution {
1219
+ Ensure-EvolutionDirs
1220
+ Ensure-ResultsHeader
1221
+ $state = Read-State
1222
+ if (-not $state.enabled) {
1223
+ Write-Output "Harness auto-evolve disabled in harness/evolution/state.json."
1224
+ return
1225
+ }
1226
+ if (-not $state.threshold) { $state.threshold = $Threshold }
1227
+ if (-not $state.window) { $state.window = $Window }
1228
+ $archives = @(Get-ArchiveItems)
1229
+ if ([int]$state.last_evolved_archive_count -gt $archives.Count) {
1230
+ $state.last_evolved_archive_count = $archives.Count
1231
+ Write-State $state
1232
+ }
1233
+ $delta = [Math]::Max(0, $archives.Count - [int]$state.last_evolved_archive_count)
1234
+ if ($delta -lt [int]$state.threshold) {
1235
+ Write-Output "Harness evolution not due ($delta/$($state.threshold) new archived changes)."
1236
+ return
1237
+ }
1238
+ New-Pending $archives $state $Reason
1239
+ }
1240
+
1241
+ function Collect-EvolutionInput {
1242
+ Check-Evolution
1243
+ if (Test-Path -LiteralPath $PendingPath) {
1244
+ Write-Output "Read: harness/evolution/pending.md"
1245
+ }
1246
+ }
1247
+
1248
+ function Mark-Complete {
1249
+ $state = Read-State
1250
+ $archives = @(Get-ArchiveItems)
1251
+ $state.last_evolved_archive_count = $archives.Count
1252
+ $last = @($archives | Select-Object -Last 1)
1253
+ $state.last_evolved_change_id = if ($last.Count -gt 0) { $last[0].id } else { $null }
1254
+ $state.last_run_at = (Get-Date).ToString("s")
1255
+ $state.pending = $false
1256
+ Write-State $state
1257
+ if (Test-Path -LiteralPath $PendingPath) {
1258
+ Remove-Item -LiteralPath $PendingPath
1259
+ }
1260
+ Ensure-ResultsHeader
1261
+ Write-Output "Marked harness evolution complete."
1262
+ }
1263
+
1264
+ switch ($Command) {
1265
+ "check" { Check-Evolution }
1266
+ "collect" { Collect-EvolutionInput }
1267
+ "mark-complete" { Mark-Complete }
1268
+ default { throw "Command required: check/collect/mark-complete" }
1269
+ }
1270
+ ```
1271
+
1272
+ ### 5.3 `scripts/lint-ecl.ps1`
1273
+
1274
+ ```powershell
1275
+ $ErrorActionPreference = "Stop"
1276
+
1277
+ $Root = (Get-Location).Path
1278
+ $Changes = Join-Path $Root "harness/changes"
1279
+ $Active = Join-Path $Changes "active"
1280
+ $IndexPath = Join-Path $Changes "INDEX.json"
1281
+ $HarnessChange = Join-Path $Root "scripts/harness-change.ps1"
1282
+ $HarnessEvolve = Join-Path $Root "scripts/harness-evolve.ps1"
1283
+ $StatusPath = Join-Path $Root "docs/STATUS.md"
1284
+ $EvolutionState = Join-Path $Root "harness/evolution/state.json"
1285
+
1286
+ function Fail([string]$Message) {
1287
+ Write-Error $Message
1288
+ exit 1
1289
+ }
1290
+
1291
+ if (-not (Test-Path -LiteralPath $Changes)) {
1292
+ Fail "Missing harness/changes. Run ecl-harness-engineer or create ECL harness structure."
1293
+ }
1294
+
1295
+ foreach ($dir in @("active", "parking", "archive")) {
1296
+ if (-not (Test-Path -LiteralPath (Join-Path $Changes $dir))) {
1297
+ Fail "Missing harness/changes/$dir."
1298
+ }
1299
+ }
1300
+
1301
+ if (-not (Test-Path -LiteralPath $HarnessChange)) {
1302
+ Fail "Missing scripts/harness-change.ps1."
1303
+ }
1304
+
1305
+ if (-not (Test-Path -LiteralPath $HarnessEvolve)) {
1306
+ Fail "Missing scripts/harness-evolve.ps1."
1307
+ }
1308
+
1309
+ if (-not (Test-Path -LiteralPath $EvolutionState)) {
1310
+ Fail "Missing harness/evolution/state.json."
1311
+ }
1312
+
1313
+ if (-not (Test-Path -LiteralPath $StatusPath)) {
1314
+ Fail "Missing docs/STATUS.md. Create a lightweight handoff summary; active change files override it when present."
1315
+ }
1316
+
1317
+ if (Test-Path -LiteralPath (Join-Path $Active "summary.md")) {
1318
+ foreach ($file in @("summary.md", "spec.md", "plan.md", "tasks.md")) {
1319
+ if (-not (Test-Path -LiteralPath (Join-Path $Active $file))) {
1320
+ Fail "Active change missing $file."
1321
+ }
1322
+ }
1323
+ if (-not (Test-Path -LiteralPath (Join-Path $Active "reviews"))) {
1324
+ Fail "Active change missing reviews/."
1325
+ }
1326
+ $summary = Get-Content -Encoding UTF8 -Raw -LiteralPath (Join-Path $Active "summary.md")
1327
+ $spec = Get-Content -Encoding UTF8 -Raw -LiteralPath (Join-Path $Active "spec.md")
1328
+ $tasks = Get-Content -Encoding UTF8 -Raw -LiteralPath (Join-Path $Active "tasks.md")
1329
+ $review = ""
1330
+ $reviewPath = Join-Path $Active "reviews/review.md"
1331
+ if (Test-Path -LiteralPath $reviewPath) { $review = Get-Content -Encoding UTF8 -Raw -LiteralPath $reviewPath }
1332
+ $phase = [regex]::Match($summary, '(?m)^phase:\s*"?([^"\r\n]+)"?').Groups[1].Value
1333
+ $planReview = [regex]::Match($summary, '(?m)^plan_review:\s*"?([^"\r\n]+)"?').Groups[1].Value
1334
+ if ($phase -match "^(implement|validate|done)$" -and $spec -match "\[NEEDS CLARIFICATION:") {
1335
+ Fail "Active spec.md still has high-impact [NEEDS CLARIFICATION] markers. Resolve them or move phase back to intake/plan."
1336
+ }
1337
+ if ($phase -match "^(implement|validate|done)$" -and $planReview -ne "approved" -and $review -notmatch "(?is)Plan Review.*Status:\s*approved") {
1338
+ Fail "Active change cannot enter implementation until plan_review is approved in summary.md or an equivalent approved Plan Review is recorded."
1339
+ }
1340
+ if ($tasks -match "(?m)^- \[[ xX]\] (?!T\d{3})") {
1341
+ Fail "tasks.md contains executable task lines without T### ids. Use '- [ ] T001 [P?] [US?] Action with target path and validation note'."
1342
+ }
1343
+ }
1344
+
1345
+ if (-not (Test-Path -LiteralPath $IndexPath)) {
1346
+ Fail "Missing harness/changes/INDEX.json. Run: powershell -NoProfile -ExecutionPolicy Bypass -File scripts/harness-change.ps1 reindex"
1347
+ }
1348
+ $actual = Get-Content -Encoding UTF8 -Raw -LiteralPath $IndexPath
1349
+ $expected = (& $HarnessChange index-json) -join "`n"
1350
+ if ($actual.Trim() -ne $expected.Trim()) {
1351
+ Fail "harness/changes/INDEX.json is stale. Run: powershell -NoProfile -ExecutionPolicy Bypass -File scripts/harness-change.ps1 reindex"
1352
+ }
1353
+
1354
+ Write-Output "ECL lint passed."
1355
+ ```
1356
+
1357
+ ### 5.4 `scripts/lint-encoding.ps1`
1358
+
1359
+ ```powershell
1360
+ $ErrorActionPreference = "Stop"
1361
+
1362
+ $markers = @(
1363
+ ([char]0x9505),
1364
+ ([char]0x951B),
1365
+ ([char]0x9286),
1366
+ ([char]0x9983),
1367
+ ([char]0x8133),
1368
+ ([char]0x7459),
1369
+ ([char]0x8930),
1370
+ ([char]0x95C6),
1371
+ ([char]0x9365),
1372
+ ([char]0x9359),
1373
+ ([char]0x9366),
1374
+ (([char]0x93C8).ToString() + ([char]0xE047).ToString())
1375
+ )
1376
+ $exclude = "\\(node_modules|vendor|\.git|dist|build|coverage|target|\.cache)\\"
1377
+ $extensions = @(".ts", ".tsx", ".js", ".jsx", ".json", ".md", ".ps1", ".py", ".go", ".rs", ".java", ".cs", ".yml", ".yaml")
1378
+ $violations = New-Object System.Collections.Generic.List[string]
1379
+
1380
+ Get-ChildItem -Recurse -File | Where-Object {
1381
+ $_.FullName -notmatch $exclude -and $extensions -contains $_.Extension
1382
+ } | ForEach-Object {
1383
+ $path = $_.FullName
1384
+ $lines = @(Get-Content -Encoding UTF8 -LiteralPath $path)
1385
+ for ($i = 0; $i -lt $lines.Count; $i++) {
1386
+ foreach ($marker in $markers) {
1387
+ if ($lines[$i].Contains($marker)) {
1388
+ $rel = Resolve-Path -LiteralPath $path -Relative
1389
+ $violations.Add("${rel}:$($i + 1): possible mojibake marker '$marker'")
1390
+ }
1391
+ }
1392
+ }
1393
+ }
1394
+
1395
+ if ($violations.Count -gt 0) {
1396
+ $violations | ForEach-Object { Write-Error $_ }
1397
+ exit 1
1398
+ }
1399
+
1400
+ Write-Output "Encoding lint passed."
1401
+ ```
1402
+
1403
+ ## 6. Validation Expectations
1404
+
1405
+ After creating ECL harness files, run the selected profile's equivalent commands. For the Bash
1406
+ profile:
1407
+
1408
+ ```bash
1409
+ bash scripts/harness-change.sh reindex
1410
+ bash scripts/harness-evolve.sh check
1411
+ bash scripts/lint-ecl.sh
1412
+ bash scripts/lint-encoding.sh
1413
+ ```
1414
+
1415
+ For the PowerShell profile:
1416
+
1417
+ ```powershell
1418
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts/harness-change.ps1 reindex
1419
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts/harness-evolve.ps1 check
1420
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts/lint-ecl.ps1
1421
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts/lint-encoding.ps1
1422
+ ```
1423
+
1424
+ If `lint-ecl` reports stale `INDEX.json`, run the generated `harness-change reindex` equivalent,
1425
+ then rerun `lint-ecl`.
1426
+ If `lint-ecl` reports missing `docs/STATUS.md`, create it from the STATUS template. Do not let
1427
+ CI or hooks generate it automatically.
1428
+ If `harness/evolution/pending.md` exists and no active change exists, read it as pending
1429
+ maintenance before `docs/STATUS.md`. Reading it does not start auto-evolve or block ordinary user
1430
+ work. Once Codex starts acting on pending evidence, finish with proposal + results.tsv +
1431
+ `harness-evolve mark-complete`, or park/block. Do not let hooks or CI apply pending changes.