diff --git a/.github/workflows/weekly-release.yml b/.github/workflows/weekly-release.yml index bb4566aef..a5453cb62 100644 --- a/.github/workflows/weekly-release.yml +++ b/.github/workflows/weekly-release.yml @@ -1,9 +1,15 @@ -name: Weekly release +name: Stable release # Tags the newest green commit on main as v, publishes the GitHub # Release, and fast-forwards refs/heads/stable to it (docs/releases.md). Team # installs follow stable, so this is the only thing that moves them. Runs -# every Monday morning Pacific; run it by hand with a ref for a hotfix, or -# with dry_run to see the plan and change nothing. A pull request that touches +# twice a week, Monday and Thursday at 14:00 UTC (the file keeps its +# weekly-release.yml name so its run history and every `gh workflow run` +# reference stay intact). stable moves at most once per 72 hours: a cut inside +# that floor is refused with REFUSED_FLOOR and NEXT_ALLOWED; a scheduled run +# gets 3 hours of grace for cron start jitter, a hand-run cut none. The only +# exception is a security cut: the security input with a ref, naming a GHSA, +# CVE or # the script ties to the released commits. Run it by hand with a +# ref for a hotfix, or with dry_run to see the plan and change nothing. A pull request that touches # the release tooling runs the dry run against main with a read-only token, # which is the only pre-merge exercise of the real workflow. The candidate's # gate must already be green (free-tests and main-backstop check runs @@ -12,8 +18,9 @@ name: Weekly release # runs only there, with main's tooling. on: schedule: - # Monday 14:00 UTC: 07:00 PDT, 06:00 PST. - - cron: '0 14 * * 1' + # Monday and Thursday 14:00 UTC (07:00 PDT, 06:00 PST): exactly 72 h, then + # 96 h, apart; the floor's scheduled grace covers a late Monday start. + - cron: '0 14 * * 1,4' workflow_dispatch: inputs: dry_run: @@ -24,6 +31,10 @@ on: description: 'Hotfix: release this commit of main instead of the newest green one (full 40-hex SHA)' type: string default: '' + security: + description: 'Security cut (needs ref): the reason, naming the GHSA-…, CVE-… or # it fixes; passes the 72-hour floor once verified and lands in the release notes' + type: string + default: '' pull_request: paths: - '.github/workflows/weekly-release.yml' @@ -48,6 +59,9 @@ jobs: contents: write # Reads the candidate's check runs. checks: read + # A security cut reads the security label on the # its reason names. + issues: read + pull-requests: read steps: # The dispatched ref supplies the tooling; the release candidate always # comes from origin/main, which fetch-depth 0 brings along with every @@ -73,16 +87,22 @@ jobs: GH_TOKEN: ${{ github.token }} DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry_run && 'true' || 'false' }} RELEASE_REF: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || '' }} + SECURITY_REASON: ${{ github.event_name == 'workflow_dispatch' && inputs.security || '' }} run: | set -euo pipefail if [ -n "$RELEASE_REF" ] && ! [[ "$RELEASE_REF" =~ ^[0-9a-f]{40}$ ]]; then - echo "::error::ref must be the full 40-hex SHA of a commit on main, not '$RELEASE_REF'" + echo "::error::ref must be the full 40-hex SHA of a commit on main" + exit 1 + fi + if [ -n "$SECURITY_REASON" ] && [ -z "$RELEASE_REF" ]; then + echo "::error::a security cut needs ref: the full SHA of the fix commit on main" exit 1 fi args=(--repo "$GITHUB_REPOSITORY" --summary "$GITHUB_STEP_SUMMARY") [ "$DRY_RUN" = true ] && args+=(--dry-run) [ -n "$RELEASE_REF" ] && args+=(--ref "$RELEASE_REF") - echo "## Weekly release ($([ "$DRY_RUN" = true ] && echo 'dry run' || echo 'cut'))" >> "$GITHUB_STEP_SUMMARY" + [ -n "$SECURITY_REASON" ] && args+=(--security "$SECURITY_REASON") + echo "## Stable release ($([ "$DRY_RUN" = true ] && echo 'dry run' || echo 'cut'), $GITHUB_EVENT_NAME)" >> "$GITHUB_STEP_SUMMARY" bun --no-env-file --config=/dev/null --no-install run scripts/release-cut.ts "${args[@]}" dry-run: @@ -110,5 +130,5 @@ jobs: set -euo pipefail git fetch --quiet origin '+refs/heads/stable:refs/remotes/origin/stable' 2>/dev/null || echo "origin has no stable yet" git fetch --quiet --tags origin - echo "## Weekly release (dry run from PR #${{ github.event.pull_request.number }})" >> "$GITHUB_STEP_SUMMARY" + echo "## Stable release (dry run from PR #${{ github.event.pull_request.number }})" >> "$GITHUB_STEP_SUMMARY" bun --no-env-file --config=/dev/null --no-install run scripts/release-cut.ts --repo "$GITHUB_REPOSITORY" --summary "$GITHUB_STEP_SUMMARY" --dry-run diff --git a/AGENTS.md b/AGENTS.md index 03f4695b7..4232c097b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,6 +29,7 @@ Invoke them by name (e.g., `/office-hours`). |-------|-------------| | `/review` | Pre-landing PR review. Finds bugs that pass CI but break in prod; lists every gate edit for gate integrity. | | `/deslop-shared-libs` | Find worthwhile shared-code extractions in recent work. Recommendations only. | +| `/write-skill` | Write or improve an agent skill without bloat: edit ladder, size per level (`gstack-skill-bill`), skill-to-bin drift check, full diets with a before/after test. | | `/test-audit` | Sweep existing tests for low-value, implementation-coupled or duplicate tests. Report-only unless you approve a batch. | | `/codex` | Second opinion via OpenAI Codex. Review, challenge, or consult modes. Available outside the Codex harness. | | `/claude-code` | Second opinion via Claude Code. Review, challenge, or consult modes. Available outside the Claude Code harness. | diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 674472421..c91ac7c04 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -359,7 +359,7 @@ Templates contain the workflows, tips, and examples that require human judgment. | `{{DESIGN_CRAFT_RULE:}}` | `resolvers/design-craft.ts` | One paragraph of `design-html/sections/craft.md.tmpl`, found by its bold lead, with its bullets, for /design-review's craft audit; a missing or ambiguous lead fails generation | | `{{DESIGN_PERSUADE_CHECKS}}` | `resolvers/design-craft.ts` | The five Persuade conversion checks (one primary action, named objections, proof beside each claim, a real risk reversal, an index/noindex decision) for /design-review's craft audit and /plan-design-review Pass 1 | | `{{DESIGN_SHOTGUN_LOOP}}` | `resolvers/design.ts` | Shared comparison board feedback loop for /design-shotgun, /plan-design-review, /design-consultation | -| `{{UX_PRINCIPLES}}` | `resolvers/design.ts` | User behavioral foundations (scanning, satisficing, goodwill reservoir, trunk test) for /design-html, /design-shotgun, /design-review, /plan-design-review | +| `{{UX_PRINCIPLES}}` | `resolvers/design.ts` | User behavioral foundations (scanning, satisficing, goodwill reservoir, trunk test) for /design-html and /design-shotgun | | `{{GBRAIN_CONTEXT_LOAD}}` | `resolvers/gbrain.ts` | Brain-first context search with keyword extraction, health awareness, and data-research routing. Injected into 10 brain-aware skills. Suppressed on non-brain hosts. | | `{{GBRAIN_SAVE_RESULTS}}` | `resolvers/gbrain.ts` | Post-skill brain persistence with entity enrichment, throttle handling, and per-skill save instructions. 8 skill-specific save formats. | | `{{FOREGROUND_DISPATCH_NOTE}}` | `resolvers/constants.ts` | Canonical `run_in_background: false` guidance for every synchronous Agent-tool subagent dispatch (subagents run in the background by default since Claude Code v2.1.198). Single source of truth; carriers are pinned per file by `test/run-in-background-guidance.test.ts`. | diff --git a/CHANGELOG.md b/CHANGELOG.md index 06a61f393..b0f5f92ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [Unreleased] +## [1.91.290.0] - 2026-10-28 **Installing gstack inside an agent harness now gives that harness gstack's skills, the harness's own way.** Until now OpenClaw and Hermes were instruction-only: `./setup --host hermes` printed a digest path and changed nothing, so an agent working inside OpenClaw or Hermes could not find a single gstack skill until a person wired one in by hand, and QM, dots, Muse Code and Grok were unknown to gstack (Garry, 2026-10-27: "when gstack is installed inside OpenClaw, Hermes, qm, muse, grok bot, dots or any harness it auto resolves and figures out how to use the skills"). `./setup` run inside OpenClaw, Hermes or a Dot now registers gstack there in the same run; `gstack-uninstall` takes it back out. @@ -25,6 +25,60 @@ ### Docs - docs/hosts.md § Agent harnesses (install, verify and remove for each harness), docs/install.md, docs/uninstall.md, docs/OPENCLAW.md (native skills or ACP), docs/ADDING_A_HOST.md (harness hosts and how to add a target), docs/capy-install.md, README host matrix. +## [1.91.289.0] - 2026-10-28 + +**/plan-design-review, /office-hours and /design-review load 15 to 40 percent fewer bytes per run, with the same or better task results.** Garry picked the skill-diet pilot on 2026-10-27 (A1): "pilot now with the upstream skill-diet on two or three large skills nobody is editing this week (design-review, office-hours, plan-design-review). Each slimmed skill ships only if it passes skill-diet's before/after test and gstack's own paid tests for that skill." All three passed both. Each SKILL.md now holds what every run needs; blocks only some runs use moved into on-demand sections behind a one-line "read when" pointer, and prose the model already knows was cut. Descriptions and triggers are unchanged. + +### Changed +- **/plan-design-review**: Claude-host SKILL.md 66,063 → 56,209 bytes (−14.9%). skill-diet panel (Opus 5.5, 3 runs per arm): success 25/33 before and after (no skill: 11/33); lower median tokens per successful task 210,252 → 197,581 (−6.0%); the blind judge preferred the dieted skill in 8 of 12 pairs. The designer intro keeps its rule (generate mockups when `DESIGN_READY`) and drops the command list. +- **/office-hours**: 73,248 → 43,885 bytes (−40.1%); Codex host 81,152 → 48,074. The Phase 3.5 second opinion and the Phase 4.25 visual design (sketch and mockup) moved to `sections/second-opinion.md` and `sections/visual-design.md`; the Phase 3.5 question stays in the main file. Panel: 26/27 → 27/27 (no skill: 14/27); lower median tokens 170,197 → 124,941 (−26.6%); judge: after 10, before 5 of 15. +- **/design-review**: 115,947 → 84,868 bytes (−26.8%); Codex host 124,144 → 90,977. The outside voices moved to `sections/outside-voices.md` (after Phase 6) and the test bootstrap to `sections/test-bootstrap.md`, read at Phase 8e.5, its only consumer. The design methodology prose in `scripts/resolvers/design.ts` (rendered only by these skills) was tightened; reports go to `$REPORT_DIR` under the state root, as Phase 10 already said, and the checklist headers count the items they now list. Panel: 32/36 → 34/36 (no skill: 14/33); lower median tokens 326,662 → 257,819 (−21.1%); judge: after 9, before 5, 1 tie. +- Every other skill's regenerated output is byte-identical to main on every host (`gen:skill-docs --host all`, `diff -rq` against a main worktree): 85 generated files differ, all belonging to the three skills. + +### Tests +- `test/design-printed-paths.test.ts`, `test/design-setup-probe.test.ts`, `test/gen-skill-docs.test.ts` and `test/run-in-background-guidance.test.ts` read the moved blocks from their new sections; `test/skill-validation.test.ts` and `test/touchfiles.test.ts` follow the new section files. +- Paid cases ran at each skill's product head. Every plan-design-review case is green; `carve-section-loading-plan-design-review` failed once and passed its one rerun (flaky). Office-hours is green except `office-hours-brain-writeback`, flaky at 1/3 and then 2/3 on its rerun; the failing trials piped the page on stdin as the unchanged gbrain host template documents, while the test's fake gbrain reads only `--content`, a fixture defect for the flaky ledger. design-review's four rule cases are green and both judges ('design-review/SKILL.md fix loop', 'plan-design-review/SKILL.md passes') PASS. `design-review-fix` self-skips without Aside and did not run. + +### Gate integrity +- `test/fixtures/skill-md-size-allowlist.json` (exact characters): 12 design-review pins deleted (now under the limit), 3 plan-design-review pins deleted and 8 lowered. Nothing raised. +- `test/fixtures/context-budget.json`: only the three skills' keys move, recaptured with `bun test/helpers/capture-context-budget.ts` (× 1.1): design-review 30295 → 22175, office-hours 19139 → 11467, plan-design-review 17262 → 14687. +- `test/helpers/carve-guards.ts` (exact bytes; ratios rounded up at 4 decimals): `maxSkeletonBytes` lowered to the measured skeletons (56,209, 43,885, 84,869; design-review measures 84,868 after the checklist count fix, one byte under its pin); design-review `minUnionBytes` 143,300 → 136,100 (union 137,602) and `maxSizeRatio` 1.3739 → 1.3001 (137,602 / 105,842 = 1.30007). +- The one raise: office-hours `maxSizeRatio` 1.1796 → 1.1921, granted by GStack Minder as merge-queue owner. The union grew 1,662 bytes because the Phase 3.5 question now sits in the main file while `CODEX_SECOND_OPINION` keeps its own copy; the measured 156,286 / 131,110 = 1.19202 needs 1.1921 after rounding up (the grant text said 1.1920). Per-run load fell 73,248 → 43,885 bytes. Follow-up: let `CODEX_SECOND_OPINION` take a flag that omits its question when the skeleton asks it, then lower the cap. +- `test/helpers/placeholder-allowlist.ts`: the `` entry is deleted. The design-review diet removed the only Phase 8d fence that used it, and the allowlist's own staleness check (`test/env-host-fence-conformance.test.ts`, CEO-12) failed. No timeout, trial count or case kind changed. + +## [1.91.288.0] - 2026-10-28 + +**Stable moves twice a week, and never twice in 72 hours unless a security fix needs it.** Garry asked, 2026-10-27: "Do we have rules around stable where we update stable except for security fixes at most once every 72 hours?" There were none: the Monday cut and any hand-run cut moved team installs whenever someone ran it. Now the release cut runs Monday and Thursday at 14:00 UTC ("Ok twice a week"), refuses any move inside 72 hours of the last one, and lets a security fix through only when it can tie the fix to the commits being released. + +### Added +- **The 72-hour floor in `scripts/release-cut.ts`.** Every cut, scheduled or by hand, measures stable's last move from the annotated `v` tag on stable (or its GitHub Release's publish time when later; any other tag on that commit is ignored) and refuses inside 72 hours. The refusal's first two lines are `REFUSED_FLOOR: stable moved to v at