@cleocode/core 2026.9.3 → 2026.9.4

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 (41) hide show
  1. package/dist/dispatch/suggested-next.d.ts.map +1 -1
  2. package/dist/dispatch/suggested-next.js +7 -1
  3. package/dist/dispatch/suggested-next.js.map +1 -1
  4. package/dist/docs/export-document.js +84 -0
  5. package/dist/docs/export-document.js.map +2 -2
  6. package/dist/hygiene/validate-spawn-readiness.d.ts +88 -4
  7. package/dist/hygiene/validate-spawn-readiness.d.ts.map +1 -1
  8. package/dist/hygiene/validate-spawn-readiness.js +138 -16
  9. package/dist/hygiene/validate-spawn-readiness.js.map +1 -1
  10. package/dist/injection.d.ts.map +1 -1
  11. package/dist/injection.js +10 -2
  12. package/dist/injection.js.map +1 -1
  13. package/dist/internal.d.ts +1 -1
  14. package/dist/internal.d.ts.map +1 -1
  15. package/dist/internal.js +1 -1
  16. package/dist/internal.js.map +1 -1
  17. package/dist/llm/plugin-facade.js +84 -0
  18. package/dist/llm/plugin-facade.js.map +2 -2
  19. package/dist/release/open.d.ts +11 -18
  20. package/dist/release/open.d.ts.map +1 -1
  21. package/dist/release/open.js +135 -7
  22. package/dist/release/open.js.map +1 -1
  23. package/dist/selfimprove/fix-gen.js +84 -0
  24. package/dist/selfimprove/fix-gen.js.map +2 -2
  25. package/dist/setup/sections/brain.d.ts +1 -1
  26. package/dist/setup/sections/brain.js +1 -1
  27. package/dist/system/bridge-mode.d.ts +1 -1
  28. package/dist/system/bridge-mode.js +1 -1
  29. package/dist/tasks/index.d.ts +1 -1
  30. package/dist/tasks/index.d.ts.map +1 -1
  31. package/dist/tasks/index.js +1 -1
  32. package/dist/tasks/index.js.map +1 -1
  33. package/dist/tasks/tool-cache.d.ts +134 -9
  34. package/dist/tasks/tool-cache.d.ts.map +1 -1
  35. package/dist/tasks/tool-cache.js +188 -68
  36. package/dist/tasks/tool-cache.js.map +1 -1
  37. package/package.json +13 -13
  38. package/templates/CLEO-INJECTION.md +6 -6
  39. package/templates/workflows/release-prepare.yml.tmpl +67 -2
  40. package/templates/workflows/release-publish.yml.tmpl +1 -1
  41. package/templates/workflows/release-rollback.yml.tmpl +18 -2
@@ -318,7 +318,55 @@ jobs:
318
318
  id: plan
319
319
  run: |
320
320
  mkdir -p .cleo/release
321
- PLAN_FILE=".cleo/release/${{ inputs.version }}.plan.json"
321
+
322
+ # gh#1370 (T12187): the plan file is NAMED BY THE COMMAND, not by us.
323
+ # `cleo release plan` resolves the version through `normalizeVersion`,
324
+ # which ADDS a leading `v` — every plan on disk is
325
+ # `v<version>.plan.json`. Building this path from the raw input meant
326
+ # a dispatch of `2026.9.1` had the command write `v2026.9.1.plan.json`
327
+ # while this step read `2026.9.1.plan.json`. Normalise identically.
328
+ RAW_VERSION="${{ inputs.version }}"
329
+ case "$RAW_VERSION" in
330
+ v*) PLAN_VERSION="$RAW_VERSION" ;;
331
+ *) PLAN_VERSION="v$RAW_VERSION" ;;
332
+ esac
333
+ PLAN_FILE=".cleo/release/${PLAN_VERSION}.plan.json"
334
+ PLAN_LOG="${RUNNER_TEMP:-/tmp}/${PLAN_VERSION}.plan.stdout.log"
335
+
336
+ # gh#1370: a release plan is a PLAN, not a summary envelope.
337
+ #
338
+ # `release plan` WRITES the plan file itself (R-030) and prints only a
339
+ # SUMMARY on stdout — version, taskCount, evidenceComplete,
340
+ # changelogWritten, planPath — with no `tasks` array. So anything that
341
+ # lands a stdout capture at the plan's path still parses as JSON and
342
+ # still hashes cleanly, and `plan-blob-sha256` downstream would then
343
+ # certify the integrity of the wrong artifact. Assert the SHAPE before
344
+ # any hash is taken over it, on BOTH branches.
345
+ ASSERT_PLAN="${RUNNER_TEMP:-/tmp}/assert-release-plan.cjs"
346
+ cat > "$ASSERT_PLAN" <<'ASSERT_PLAN_EOF'
347
+ const { readFileSync } = require('node:fs');
348
+ const target = process.argv[2];
349
+ let plan;
350
+ try {
351
+ plan = JSON.parse(readFileSync(target, 'utf8'));
352
+ } catch (err) {
353
+ console.error('::error::' + target + ' is not valid JSON: ' + err.message);
354
+ console.error('A prose-prefixed summary envelope is not a release plan (gh#1370).');
355
+ process.exit(1);
356
+ }
357
+ if (!Array.isArray(plan.tasks)) {
358
+ const keys = Object.keys(plan).join(', ');
359
+ console.error('::error::' + target + ' has no "tasks" array. Top-level keys: ' + keys);
360
+ console.error('A summary envelope is not a release plan (gh#1370).');
361
+ process.exit(1);
362
+ }
363
+ if (plan.tasks.length === 0) {
364
+ console.error('::error::' + target + ' has an EMPTY "tasks" array — refusing to release nothing.');
365
+ process.exit(1);
366
+ }
367
+ console.log('plan OK: ' + plan.tasks.length + ' tasks, resolvedVersion=' + (plan.resolvedVersion || '<absent>'));
368
+ ASSERT_PLAN_EOF
369
+
322
370
  if [ -z "${{ inputs.plan-blob-sha256 }}" ]; then
323
371
  echo "No plan-blob-sha256 input — generating fresh plan."
324
372
  # T12089: `cleo release plan` REQUIRES a scope (--saga | --epic |
@@ -338,7 +386,21 @@ jobs:
338
386
  echo "::error::No release scope supplied. Dispatch with 'epic' or 'tasks' (cleo release plan requires --saga, --epic or --tasks), or pass plan-blob-sha256 with a committed plan file."
339
387
  exit 1
340
388
  fi
341
- cleo release plan "${{ inputs.version }}" "${SCOPE_ARGS[@]}" --json > "$PLAN_FILE"
389
+ # gh#1370: do NOT redirect stdout onto "$PLAN_FILE". The command
390
+ # writes that file itself; the redirect aimed a second writer at the
391
+ # same path and whichever landed last became the artifact hashed
392
+ # below. Capture stdout to a log and surface it only on failure.
393
+ if ! cleo release plan "$RAW_VERSION" "${SCOPE_ARGS[@]}" --json > "$PLAN_LOG" 2>&1; then
394
+ echo "::error::cleo release plan failed — output follows"
395
+ cat "$PLAN_LOG"
396
+ exit 1
397
+ fi
398
+ if [ ! -f "$PLAN_FILE" ]; then
399
+ echo "::error::cleo release plan reported success but $PLAN_FILE does not exist. Plan command output:"
400
+ cat "$PLAN_LOG"
401
+ exit 1
402
+ fi
403
+ node "$ASSERT_PLAN" "$PLAN_FILE"
342
404
  else
343
405
  echo "Verifying pre-computed plan against sha256=${{ inputs.plan-blob-sha256 }}"
344
406
  if [ ! -f "$PLAN_FILE" ]; then
@@ -350,6 +412,9 @@ jobs:
350
412
  echo "::error::Plan sha256 mismatch — expected ${{ inputs.plan-blob-sha256 }}, got $ACTUAL"
351
413
  exit 1
352
414
  fi
415
+ # gh#1370: a matching sha proves the file is UNCHANGED, never that
416
+ # it is a plan. A committed summary envelope hashes just as well.
417
+ node "$ASSERT_PLAN" "$PLAN_FILE"
353
418
  fi
354
419
  echo "plan_file=$PLAN_FILE" >> "$GITHUB_OUTPUT"
355
420
  timeout-minutes: 5
@@ -382,7 +382,7 @@ jobs:
382
382
  gh issue create \
383
383
  --label release-incident \
384
384
  --title "Provenance backfill needed for ${VERSION}" \
385
- --body "release-publish.yml reconcile job failed for ${VERSION}. Tag and publish completed successfully; only the provenance graph is incomplete. To recover, run \`cleo release reconcile ${VERSION} --backfill\` from a local checkout. Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
385
+ --body "release-publish.yml reconcile job failed for ${VERSION}. Tag and publish completed successfully; only the provenance graph is incomplete. To recover, run \`cleo release reconcile ${VERSION}\` from a local checkout. Workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
386
386
  env:
387
387
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
388
388
  timeout-minutes: 5
@@ -298,8 +298,24 @@ been updated by the \`reconcile-rollback\` job."
298
298
  # R-256: reconcile MUST write rollback marker into `releases` +
299
299
  # `release_changes`. The CLI is the canonical writer — workflow
300
300
  # YAML never touches the DB directly.
301
+ #
302
+ # gh#1373: `--reason` was removed from this line. It is not declared in
303
+ # `release reconcile`'s args — the command's whole flag surface is
304
+ # `--from-workflow --rollback --dry-run --json`. citty parses with
305
+ # `strict: false`, so `--reason` was absorbed as an undeclared boolean and
306
+ # `"${REASON}"` fell through as a stray positional: the operator's reason
307
+ # has never reached the database, and the step reported success every
308
+ # time. Dropping the flag does not lose information that was being
309
+ # recorded; it stops the step from claiming to record it. The reason is
310
+ # echoed into the run log so it is at least retained where an operator
311
+ # reading this job can see it.
312
+ #
313
+ # Giving `reconcile --rollback` a real `--reason` is a CLI change, not a
314
+ # workflow change, and is tracked separately.
301
315
  - name: Record rollback
302
- run: cleo release reconcile "${VERSION}" --rollback --reason "${REASON}"
316
+ run: |
317
+ echo "Rollback reason (not persisted — see gh#1373): ${REASON}"
318
+ cleo release reconcile "${VERSION}" --rollback
303
319
  timeout-minutes: 5
304
320
 
305
321
  # Recovery hint in the job summary so an operator who sees a
@@ -316,7 +332,7 @@ been updated by the \`reconcile-rollback\` job."
316
332
  echo "**Recovery commands** (if anything went wrong):"
317
333
  echo ""
318
334
  echo "\`\`\`"
319
- echo "cleo release reconcile ${VERSION} --rollback --reason \"${REASON}\""
335
+ echo "cleo release reconcile ${VERSION} --rollback"
320
336
  echo "\`\`\`"
321
337
  } >> "$GITHUB_STEP_SUMMARY"
322
338
  timeout-minutes: 1