You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 10b77de
Browse filesBrowse the repository at this point in the historyBrowse files
v1.91.284.0 feat(absorb-tstack): /modernize skill, per-skill Codex default prompts, gstack-post --attribute, the unattended grant and a CONTRIBUTING porting section (#3497)
* feat(absorb-tstack): /modernize skill, per-skill Codex default prompts, gstack-post --attribute, the unattended grant and a CONTRIBUTING porting section
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(modernize): read the PR text through gstack-issue-guard; re-measure the context ceiling
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: pre-landing review fixes
- gstack-post: with --attribute, report scan findings at the body file's own
line numbers and a finding inside the callout as PART: attribute (the
coordinates were shifted by the three callout lines)
- /modernize: route commit messages through gstack-issue-guard with the rest
of the PR text, read isCrossRepository and maintainerCanModify, fetch a fork
head from pull/N/head, refuse a PR whose head is the base branch, and list
moved files NUL-safely
- docs/unattended.md: say that /ship never rebases or force-pushes and an
unattended /modernize stops at its verdict
- CHANGELOG: seven skills carry a codex_default_prompt, not six
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: /modernize fetches a fork head with a forced refspec and stops when the PR moved
A rerun after the author force-pushed left refs/modernize/pr-N stale (the
non-forced refspec is rejected with exit 0 through the pipe), and the lease
pinned to the current remote head would then have let the rebased stale head
overwrite the author's newer commits. The fetch is forced, Steps 2 and 4 stop
when the head is not the one observed in Step 1, and the head-is-base guard
applies only to same-repository PRs (a fork's main into main is a valid head).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: /modernize stops on merge commits in the PR range and pushes a fork head to the fork
A plain git rebase drops merge commits and git range-diff compares only the
non-merge patches, so a change authored in a PR's merge commit disappeared
while the proof printed = for every patch and Step 5 force-pushed the loss.
Step 4 now counts merges in $MERGE_BASE..$OLD_HEAD and stops when there are
any, switches with -C so a rerun after a refused lease does not fail on the
existing branch, and asks for a cross-repository diff to be read as untrusted
input before the repository's test command runs it. Step 5 pushes a fork head
to the fork's URL with the same pinned lease instead of to origin, where the
lease could never match.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: cover the Codex default-prompt render, the openai.yaml ownership proof and the unattended grant
Adds the /ship coverage audit's tests: test/host-renders-openai-yaml-proof.test.ts
drives bin/gstack-host-renders.sh's weakened proof against a rendered yaml, a
hand-written one and a generic-prompt one; test/unattended-grant.test.ts pins the
docs/unattended.md grant section and the handoff pointer; test/gen-skill-docs.test.ts
adds modernize to the skills whose codex_default_prompt must reach openai.yaml.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs: name the static lists a new skill still needs and the Codex metadata file
From the ship documentation audit: CONTRIBUTING's new-skill checklist said no
static list needs a row, which this PR disproved (lib/doctor-components.ts
SKILL_REQUIREMENTS and the catalog rows); the host comparison table and
docs/ADDING_A_HOST.md gain the agents/openai.yaml default_prompt source; a blank
line before the new docs/unattended.md grant heading.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: pre-landing review fixes
CHANGELOG [Unreleased] gate-integrity record: the catalog and context-budget
figures re-measured on top of main (1,314 -> 1,359; modernize 9466;
alwaysOnTotal 7458 -> 7880), as the merge commit's ratchets state.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs: name the optional codex_default_prompt key in the new-skill checklist
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(parity): re-pin the ship union cap at 1.7405 after the main merge
GSTA-36 grant 2026-10-27. On the tree merged with f27a760 the ship union is 326,691 / 187,706 = 1.7404 against #3487's 1.7400; the only ship bytes this PR adds are the handoff section's one-line pointer. Cap 1.7400 -> 1.7405, measurement appended to the comment.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs(changelog): the [Unreleased] gate-integrity bullet names the ship cap re-pin
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: pre-landing review fixes
CHANGELOG [Unreleased]: end the Codex default-prompts sentence at
"generator input only." so bin/gstack-prose-lint --unreleased --gate
(main's new quality-gate step) passes, and restate the gate-integrity
bullet's figures for the tree merged with 1f246e3: ship cap
1.7524 -> 1.7528 (329,005 / 187,706), modernize 9466 -> 3772 after the
preamble hoist, alwaysOnTotal 7769 -> 7880, catalog 1,359 unchanged.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: pre-landing review fixes
CHANGELOG [Unreleased] gate-integrity bullet names the current ship cap pin
(1.7525 -> 1.7529, measured 329,024 / 187,706 on the 392f50d merge) and keeps
the 1.7528 pin in the earlier-merges clause.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
Copy file name to clipboardExpand all lines: AGENTS.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -49,6 +49,7 @@ Invoke them by name (e.g., `/office-hours`).
49
49
|-------|-------------|
50
50
|`/ship`| Run tests, review, gate integrity, push, open PR. Workspace-aware version queue. |
51
51
|`/land-and-deploy`| Merge the PR, wait for CI and deploy, verify production health. |
52
+
|`/modernize`| Bring an aging PR onto its current base and decide MODERNIZE, REWORK or CLOSE, with `git range-diff` proof before a pinned-lease push. Never closes a PR itself. |
52
53
|`/canary`| Post-deploy monitoring loop in your Aside browser (or gstack's own when Aside is absent). |
53
54
|`/landing-report`| Read-only dashboard for the workspace-aware ship queue. |
54
55
|`/document-release`| Audit relevant docs before final verification on every ship; also supports standalone documentation updates. |
Copy file name to clipboardExpand all lines: CHANGELOG.md
+19Lines changed: 19 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,24 @@
1
1
# Changelog
2
2
3
+
## [1.91.284.0] - 2026-10-27
4
+
5
+
**Five small takes from tstack, in gstack's words.** Theo Browne's tstack (MIT, 2026-10-24) is nine short PR-landing skills; its best ideas that gstack lacked were a verb for an aging pull request, a Codex picker that says what each skill is for, a written grant for unattended sessions, a repo-level rule for porting a contributor's PR, and a visible author line on agent-posted comments. Each lands here as gstack prose and gstack code; no sentence is copied (NOTICE.md). Approved by Garry, 2026-10-26: "Do it, send" (yes to the opt-in attribution callout, off by default).
6
+
7
+
### Added
8
+
- **`/modernize`**: bring an aging pull request onto its current base and decide whether it should still exist. The skill reads the PR as a claim (the problem, the change, what must be true of the codebase), reads its title, body and comments only through `gstack-issue-guard`, reads what moved under it for conflicts in meaning as well as in text, and writes one of three verdicts before anything is pushed: **MODERNIZE** (rebase with the author's commits kept as theirs, `git range-diff` with a one-line reason per changed hunk, the repo's own tests, then `git push --force-with-lease=<branch>:<observed head sha>`), **REWORK** (the problem stands, this diff does not; nothing pushed) or **CLOSE** (recommended with the superseding commit; the PR stays open until someone with the authority says so). The skill never closes a PR and never uses a bare `--force`; `/careful` already rates the lease push MEDIUM. Rows in README, AGENTS.md, `docs/skills.md` (with a deep dive), `docs/PROJECT_STRUCTURE.md`, the root router and `gstack/llms.txt`.
9
+
- **Per-skill Codex default prompts**: a template's `codex_default_prompt:` frontmatter key becomes that skill's `default_prompt` in `agents/openai.yaml`, the sentence Codex's picker inserts when a user chooses the skill; a template without the key keeps `Use <name> for this task.`. `/ship`, `/review`, `/qa`, `/autoplan`, `/office-hours`, `/land-and-deploy` and `/modernize` carry one. The key is generator input only. No host's SKILL.md render carries it (Claude strips it; the allowlist hosts never copy it), so every pinned render is byte-identical. `bin/gstack-host-renders.sh`'s proof that a yaml file is gstack's accepts any `default_prompt` line.
10
+
- **`gstack-post --attribute "<model> on behalf of <login>"`** on `pr-comment`, `issue-comment` and `reply`: opt-in and off by default, it opens the comment with a `> [!NOTE]` callout (`🤖 **<text>**`) naming who wrote it, then the body as written. The callout bytes are scanned and bound into the MEDIUM confirmation token with the body, so a changed attribution needs a new token (findings are reported at the body file's own line numbers, a finding in the callout as `PART: attribute`); the flag is refused (exit 64) on a PR body, a title or an issue body, and for empty, multi-line or over-200-character text. `review/greptile-triage.md` says when to add it to a reply.
11
+
- **`docs/unattended.md` "What an unattended session may do"**: merge never (nor auto-merge, a merge queue or `gh pr ready` on a PR the run did not open); force-push only the run's own branch, after a rebase the plan or a `/modernize` disposition named, with `git range-diff` in the handoff and a lease pinned to the observed sha; tests only by the methods the repository's instructions name, no ask-first; proof is what the run observed itself. Anything outside the list is a pending approval item. `/ship`'s handoff section points at it (one line in the lazy section; this PR adds no skeleton bytes).
12
+
- **CONTRIBUTING.md "Porting a contributor's PR"**: review and test the port head first (a fork's green checks prove nothing here), keep their commits as theirs with `Co-authored-by` on anything rewritten, your fixes on top as separate commits, the port names the original and its author, and the original closes with a specific thank-you linking the port; closing without landing is a maintainer's call. The repo-side home of the crew rule in `docs/captain.md`.
13
+
14
+
### Tests
15
+
- `test/modernize-skill.test.ts` (new): the verdict table and its order before any push, the never-close rule, the range-diff and pinned-lease lines, the `/careful` MEDIUM rating, and every catalog row a new skill owes.
16
+
- `test/gen-skill-docs.test.ts`: the seven prompts reach `openai.yaml`, a skill without the key keeps the fallback, `extractCodexDefaultPrompt` reads quoted and plain values inside the frontmatter only, and no host render carries the key.
17
+
- `test/gstack-post.test.ts`: no flag means the body is sent as written; the callout on all three comment operations and on GitLab; scan and token cover the callout; refusals on the other operations and bad text.
18
+
19
+
### Gate integrity
20
+
- The one threshold this PR moves is the ship union cap in `test/helpers/carve-guards.ts`, 1.7525 → 1.7529 under GStack Minder's grant. On the tree merged with 392f50d4 (v1.91.281.0) the union is 329,024 / 187,706 = 1.7529 against main's 1.7525 (pinned at a measured 1.7524 by #3516), the handoff pointer line being this PR's only ship bytes; the skeleton is main's 60,900 (0 skeleton bytes added). Earlier merges of this PR pinned 1.7405 on top of #3487's 1.7400 (326,691 bytes on the f27a7605 merge) and 1.7528 on top of main's 1.7524 (329,005 bytes on the 1f246e35 merge) the same way. No timeout, trial count or case kind changed. `test/catalog-budget.test.ts` moves 1,314 → 1,359 token-equivalents for the new skill, re-measured on top of main's `/ubicloud` and `/design-motion` rows (pre-addition 5,249 bytes on d9b0d600, modernize adds 178, measured 5,427 = 1,357 on the merged tree; 9 bytes left), and `test/fixtures/context-budget.json` is recaptured on the merged tree, where the hoist moved modernize's preamble into a lazy section (`modernize` 9466 → 3772) and `alwaysOnTotal` goes 7769 → 7880 (capture × 1.05). No pinned render in `test/fixtures/skill-md-size-allowlist.json` changed size.
21
+
3
22
## [1.91.283.0] - 2026-10-27
4
23
5
24
**/benchmark's collection, comparison, budget and trend phases now agree with its vet-the-number section.** The 2026-10-27 flaky pass (GSTA-128) read the `benchmark/SKILL.md perf collection` judge's six samples from run 40558235014: every sample named the same contradictions, so the prose was repaired and no threshold moved.
Copy file name to clipboardExpand all lines: CONTRIBUTING.md
+49-6Lines changed: 49 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -424,10 +424,10 @@ eval files, and misses the strict classifier. No API keys needed.
424
424
-**Aside driver contract** (`test/aside-driver.test.ts`) — Browser behaviour in skills is written against `scripts/resolvers/aside.ts` (`{{ASIDE_SETUP}}`) and verified live against the Aside CLI on a Mac. CI cannot run Aside, so the Aside E2E tests self-skip where `aside` is not installed; the static pins (detection, fallback hand-off, consent, credential, one-flow-per-script, sentinel) are what CI proves.
425
425
-**Aside render wrapper** (`test/aside-render.test.ts`) — Pins the option mapping and generated script of `lib/aside-render.ts` everywhere, and drives both engines hermetically with fake `aside` / `browse` executables (probe classification, the stdout contract, loopback-server policy, failure paths, the timeout kill, engine choice and the mid-run fallback); the live render (PDF + screenshot through a real Aside) runs only where Aside is open and self-skips elsewhere. make-pdf's render gates (`make-pdf/test/e2e/*-gate.test.ts`) and `test/skill-e2e-diagram.test.ts` are engine-agnostic: they run through whichever engine resolves (`browserAvailable()` — Aside, or the browse binary `bun run build:gates` compiles, which is what Linux CI does) and skip only when neither exists.
426
426
-**Render CLI** (`test/gstack-render-cli.test.ts`) — Pins `bin/gstack-render.ts` against a fake daemon (`GSTACK_SKIP_ASIDE=1` + `GSTACK_BROWSE_BIN`): argv guards exit 1 with the usage line, `--help` exits 0, `ENGINE=` first then `OK <path>` then fenced `EVAL` / `PAGE_ERRORS`, `--serve-root` containment, the no-browser first line, and prompt exit after a successful render. `make-pdf/test/cli-exit-codes.test.ts` and `make-pdf/test/setup-smoke.test.ts` pin the `pdf` binary's error-to-exit-code map and `$P setup`'s engine report.
427
-
-**Generator tests** (`test/gen-skill-docs.test.ts`) — Tests the template system: verifies placeholders resolve correctly, output includes value hints for flags (e.g. `-d <N>` not just `-d`), enriched descriptions for key commands (e.g. `is` lists valid states, `press` lists key examples).
427
+
-**Generator tests** (`test/gen-skill-docs.test.ts`) — Tests the template system: verifies placeholders resolve correctly, output includes value hints for flags (e.g. `-d <N>` not just `-d`), enriched descriptions for key commands (e.g. `is` lists valid states, `press` lists key examples), and that a template's `codex_default_prompt:` frontmatter key reaches the Codex `agents/openai.yaml``default_prompt` (a template without the key keeps `Use <name> for this task.`) while no host's SKILL.md render carries the key.
428
428
-**Design detector, catalog, and DESIGN.md** (`test/gstack-design-detect.test.ts`, `test/design-detect-contract.test.ts`, `test/design-catalog.test.ts`, `test/design-checklist-sync.test.ts`, `test/design-md.test.ts`, `test/frontend-scope.test.ts`, `test/impeccable-fixtures.test.ts`) — Drive `bin/gstack-design-detect.ts` through the fake engine in `test/fixtures/fake-impeccable.ts` (probe order, the never-execute-a-repository-file rule, the `--changed` target allow-list, `design_detector: off`, analytics lines, output sanitizing), pin the catalog invariants and the generated `review/design-checklist.md`, round-trip the open DESIGN.md reader/writer, and check the real engine captures (`test/fixtures/impeccable-*.json`, engine 0.1.3) against the contract. `test/dom-dump-hygiene.test.ts` runs `lib/dom-dump.js` in a real Chromium page through the built browse binary; it self-skips without the binary and is opt-in outside CI (`GSTACK_DOM_DUMP_HYGIENE=1`).
429
429
-**Tier-alignment invariant** (`test/e2e-tier-alignment.test.ts`) — For every self-gated `test/skill-e2e-*.test.ts` named in a touchfiles dep list, the file's `EVALS_TIER` self-gate must match its declared tier in `E2E_TIERS`. Kills the "inert demotion" class where a test is re-tiered in `touchfiles.ts` but the file still gates on the old tier and keeps running in the wrong lane. Unmapped or mixed-tier files are reported, never silently skipped.
430
-
-**Catalog budget** (`test/catalog-budget.test.ts`) — Caps the aggregate discovery surface: the sum of every skill's frontmatter `name` + `description` (what every host loads at discovery, every session) must stay under 1,251 token-equivalents, with a 260-byte per-skill cap. Counting goes through the shared census in `test/helpers/skill-census.ts` (physical files vs authored skills vs registry entries — three deliberately different counts). Adding a skill? The failure message carries the re-measure + ratchet protocol.
430
+
-**Catalog budget** (`test/catalog-budget.test.ts`) — Caps the aggregate discovery surface: the sum of every skill's frontmatter `name` + `description` (what every host loads at discovery, every session) must stay under 1,359 token-equivalents, with a 260-byte per-skill cap. Counting goes through the shared census in `test/helpers/skill-census.ts` (physical files vs authored skills vs registry entries — three deliberately different counts). Adding a skill? The failure message carries the re-measure + ratchet protocol.
431
431
-**Context-budget ratchet** (`test/context-budget-ratchet.test.ts`) — CI ceilings on the two token ledgers the catalog budget doesn't cover: the always-on full-frontmatter aggregate and each skill's per-invocation eager tokens (SKILL.md + forced-read references), graded against `test/fixtures/context-budget.json` via `lib/context-bill.ts`. New skills fail until they have a ceiling; ceilings for removed skills must be pruned. Legitimate growth or a landed reduction: re-run `bun test/helpers/capture-context-budget.ts` and commit the refreshed fixture in the same commit, so the change is a visible decision in the diff.
432
432
-**Dependency security regressions** (`test/dependency-security.test.ts`) — Run `bun test test/dependency-security.test.ts` to check the resolved `sharp` and `adm-zip` version floors, load Sharp, verify ordinary ZIP extraction, and reject extraction through destination-file and destination-directory symlinks. The symlink cases skip Windows. These checks complement the OSV scan; they do not change its existing exceptions.
433
433
@@ -766,6 +766,7 @@ Each host config (`hosts/*.ts`) controls:
| Suppressed sections | GBrain blocks vs GBrain blocks and Review Army; Codex retains outside-review sections routed to Claude Code |
768
768
| Model overlay |`claude` vs `gpt` (per-host `defaultModel`; `--model` or, at setup time, the Codex `config.toml` model overrides) |
769
+
| Metadata file | none vs `agents/openai.yaml` beside each skill (`generation.generateMetadata`); its `default_prompt` is the template's `codex_default_prompt:` frontmatter key, else `Use <name> for this task.` The key is generator input only and appears in no host's SKILL.md render |
769
770
770
771
See `scripts/host-config.ts` for the full `HostConfig` interface.
771
772
@@ -802,11 +803,11 @@ install ownership rules in the guide.
802
803
803
804
### Adding a new skill
804
805
805
-
When you add a new skill template, all hosts get it automatically:
806
-
1. Create `{skill}/SKILL.md.tmpl`
806
+
When you add a new skill template, every host renders it; a few static lists still need a row:
807
+
1. Create `{skill}/SKILL.md.tmpl` (optionally with a one-line `codex_default_prompt:` frontmatter key, the sentence Codex's picker inserts for the skill; without it the picker gets `Use <name> for this task.`)
807
808
2. Run `bun run gen:skill-docs --host all`
808
-
3. The dynamic template discovery picks it up, no static list to update
809
-
4. Budget it: run `bun test/helpers/capture-context-budget.ts` and commit the refreshed `test/fixtures/context-budget.json` — the context-budget ratchet fails any skill without a ceiling
809
+
3. The dynamic template discovery picks it up for every host render and for `gstack/llms.txt`. Two things it does not fill in: a row in `SKILL_REQUIREMENTS` in `lib/doctor-components.ts` (the doctor's `--for <skill>` table; `test/doctor-components.test.ts` fails on a skill directory without one, and the same gen run rerenders `bin/gstack-doctor-components.sh`), and the catalog rows readers find the skill through (README.md, AGENTS.md, `docs/skills.md`, `docs/PROJECT_STRUCTURE.md`, the root `SKILL.md.tmpl` router)
810
+
4. Budget it: run `bun test/helpers/capture-context-budget.ts` and commit the refreshed `test/fixtures/context-budget.json` — the context-budget ratchet fails any skill without a ceiling; the catalog budget (`test/catalog-budget.test.ts`) fails until its cap is ratcheted by the protocol in its failure message, and the number is quoted in CLAUDE.md, this file and `docs/CONTRIBUTOR_REFERENCE.md`
810
811
5. Commit `{skill}/SKILL.md` (and the generated `{skill}/sections/preamble.md` when the template's `preamble-tier` is 2 or higher), external host output is generated at setup time and gitignored
811
812
812
813
## Conductor workspaces
@@ -934,6 +935,48 @@ When community PRs accumulate, batch them into themed waves:
934
935
935
936
See [PR #205](../../pull/205) (v0.8.3) for the first wave as an example.
936
937
938
+
## Porting a contributor's PR
939
+
940
+
Sometimes the right way to land an outside PR is to port it: the diff is right
941
+
but it sits on an old base, its tests need our fixtures, or the repo's gates
942
+
(generated renders, size pins, the CHANGELOG entry) are things only a maintainer
943
+
can finish. The port keeps the contributor's credit and gives them a clear
944
+
answer; the steps below are the repo's version of the rule a maintainer crew
0 commit comments