enhance(#3028): responsive Markdown separators instead of fixed-width rules (#3789)

* feat(#3028): responsive Markdown separators instead of fixed-width rules

Stage banners, checkpoints, completion and error panels used fixed-width
runs of box-drawing characters -- a 53-column heavy rule and a 62-column
double-line box. Those runs are ordinary text to a Markdown-rendering
host, so in a narrower pane they wrap and the border comes apart from
the heading it framed.

Shipped content now emits an ATX heading for a titled section and a
blank-line-delimited --- for a break between sections, both of which
adapt to the available width. The same convention is applied to the
three code sites that built these strings at runtime: the UAT
checkpoint renderer, the milestone-close audit report, and the TDD
review checkpoint table.

Removing the box also removes its only reason to exist -- the
east-asian-width padding helpers that kept its right border aligned
(checkpointBoxLine, displayWidth, isWideCodePoint, ZERO_WIDTH_MARK_RE,
CHECKPOINT_BOX_WIDTH). RTL directional isolation is unchanged.

The convention is specified in gsd-core/references/ui-brand.md and
enforced across all shipped content by tests/responsive-separators.test.cjs.

Refs #3028

* test(#3028): pin the heading form in checkpoint and audit-report assertions

These suites asserted the exact box borders and the 62-column padded
banner interior. With the box gone they assert the ### heading form,
the --- break and the bolded instruction line, and each now carries a
positive assertion that no box character remains -- which is what pins
the fix rather than merely tolerating it.

Language coverage is converted, not dropped: Japanese, Chinese, Korean,
Hindi and Arabic all still assert their rendered banner, and the Arabic
case still asserts the RTL directional isolates the box removal must
not disturb. Adds a case for a banner longer than the old inner width,
which previously produced a ragged border and now has none.

Refs #3028

* chore(#3028): acknowledge execute-plan.md growth from the checkpoint display spec

The checkpoint_protocol display spec described the drawn box; it now
describes the heading, the --- break and the bolded action prompt,
which costs 22 bytes (40111 -> 40133, 827 under the cap).

Appended to the existing #3370 fragment rather than filed as a new one:
a growth ack keys on the bare filename and #3370 already declares
execute-plan.md, so a second source naming it would be a hard
duplicate-key error. Same supersede-by-append route #3370 took for the
spent #2652 fragment.

Refs #3028

* docs(#3028): state the load-bearing half of the separator rule, and amend the zh-CN reference

Review found three things.

The rule as first written demanded a blank line above AND below every
---. Only the one above is load-bearing: it is what stops CommonMark
reading the rule as a setext underline for the line above. The one below
is cosmetic, because a thematic break is a leaf block. The rule now says
that, with the reason, instead of asserting a stricter form the content
does not keep.

The zh-CN reference had received the mechanical box-to-heading swap but
none of the prose behind it: it still claimed a 62-character checkpoint
width and still listed --- among forbidden mixed banner styles, so it
contradicted the convention it was translating. It now carries the
separator section, the setext reasoning, the unconditional-vs-per-runtime
rationale and a corrected anti-pattern list, in Chinese.

The user guide asserted that a heading is not a degradation anywhere.
That is an assertion, not a demonstration. It now says what was actually
traded away in a plain terminal, points at the recorded rationale, and
invites the report that would justify the capability flag instead.

Refs #3028

* chore(#3028): backfill changeset PR number

Refs #3028

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-08-23 22:38:12 -04:00
committed by GitHub
parent 107eb8c1d9
commit cf15682d1c
74 changed files with 849 additions and 699 deletions

View File

@@ -11,6 +11,7 @@ A narrative companion guide to GSD Core — orient yourself here, then follow th
- [Slash-command forms](#slash-command-forms-hyphen-vs-colon)
- [Namespace routing primer](#namespace-routing-primer-gsd-ns--v140)
- [Reading GSD's output](#reading-gsds-output)
- [Project lifecycle overview](#project-lifecycle-overview)
- [Workflow Diagrams](#workflow-diagrams)
- [UI Design Contract](#ui-design-contract)
@@ -73,6 +74,66 @@ On the seven nesting runtimes listed above, upgrading to v1.40 changes skill inv
---
## Reading GSD's output
GSD marks its sections with Markdown, not with drawn borders. There are exactly
three forms, and every workflow, checkpoint and report uses them:
| You see | It means |
|---|---|
| `### GSD ► {STAGE NAME}` | A major workflow transition — planning, executing a wave, verifying, completing |
| `### CHECKPOINT: {Type}` | GSD is waiting on you. The bolded `**→ …**` line at the bottom tells you what to type |
| `---` | A break between two sections — most often before the **▶ Next Up** block at the end of a completion |
Panels that used to be drawn with box characters are now a heading followed by
their rows as ordinary lines. A checkpoint reads:
```
### CHECKPOINT: Verification Required
Progress: 5/8 tasks complete
Task: Responsive dashboard layout
How to verify:
1. Visit: http://localhost:3000/dashboard
---
**→ YOUR ACTION: Type "approved" or describe issues**
```
### Why there are no drawn borders
Earlier releases framed stage banners between two 53-character runs
of `━`, and checkpoints were a 62-column box drawn with `╔`, `║` and `╚`. Those
runs are ordinary text to whatever renders GSD's output. In a pane narrower than
the run, the rule wraps and the leftover glyphs land on a second line — so the
border comes apart from the heading it was framing, and the output looks broken
rather than merely narrow. Reported against the Codex desktop interface in
[#3028](https://github.com/open-gsd/gsd-core/issues/3028).
A Markdown heading and a thematic break carry the same structure without
committing to a width, so they read correctly in a narrow pane and a wide one.
This is unconditional: every runtime gets the same output. The alternative — a
capability flag that kept line-art for terminal-oriented runtimes — was weighed
and rejected, because it leaves two output conventions to keep in sync forever
for a gain that is aesthetic rather than structural. What a terminal loses is the
drawn frame; what it keeps is the title, the hierarchy and the break, none of
which depended on the frame. The reasoning is recorded in
`gsd-core/references/ui-brand.md § Why this is unconditional, not per-runtime`.
If you run GSD in a host where the heading form reads worse than the old boxes
did, that is worth reporting — it is the evidence that would justify the flag.
Single-cell tokens are unaffected and unchanged: the status symbols
(`✓ ✗ ◆ ○ ⚠`), the `GSD ►` prefix, and the ten-cell progress gauge
(`Progress: ████████░░ 80%`) are not runs and do not wrap.
The convention is specified in `gsd-core/references/ui-brand.md` and enforced
against all shipped content by `tests/responsive-separators.test.cjs`.
---
## Project lifecycle overview
The core GSD loop is: **discuss → plan → execute → verify → ship**, repeated per phase. The full step-by-step walkthrough — including example outputs, what files get created, and all the flags in play — is in the dedicated tutorial.

View File

@@ -75,9 +75,12 @@ Installed: 1.39.0
Latest: 1.41.0
### What's New
────────────────────────────────────────────────────────────
---
[changelog entries for 1.40.0 and 1.41.0]
────────────────────────────────────────────────────────────
---
Proceed with update? [Yes, update now / No, cancel]
```

View File

@@ -54,9 +54,12 @@ Installed: 1.39.0
Latest: 1.41.0
### What's New
────────────────────────────────────────────────────────────
---
[changelog entries for 1.40.0 and 1.41.0]
────────────────────────────────────────────────────────────
---
Proceed with update? [Yes, update now / No, cancel]
```

View File

@@ -54,9 +54,12 @@ Installed: 1.39.0
Latest: 1.41.0
### What's New
────────────────────────────────────────────────────────────
---
[changelog entries for 1.40.0 and 1.41.0]
────────────────────────────────────────────────────────────
---
Proceed with update? [Yes, update now / No, cancel]
```

View File

@@ -54,9 +54,12 @@ Installed: 1.39.0
Latest: 1.41.0
### What's New
────────────────────────────────────────────────────────────
---
[changelog entries for 1.40.0 and 1.41.0]
────────────────────────────────────────────────────────────
---
Proceed with update? [Yes, update now / No, cancel]
```

View File

@@ -441,17 +441,15 @@ deliverables into checkpoints. It presents one checkpoint at a time; the first
one looks like this (the test wording depends on what was built):
```text
╔══════════════════════════════════════════════════════════════╗
║ CHECKPOINT: Verification Required ║
╚══════════════════════════════════════════════════════════════╝
### CHECKPOINT: Verification Required
**Test 1: Add a to-do**
Running `node todo.js add "buy milk"` creates a pending item without errors.
──────────────────────────────────────────────────────────────
Type `pass` or describe what's wrong.
──────────────────────────────────────────────────────────────
---
**Type `pass` or describe what's wrong.**
```
Type `pass` when reality matches, or describe what differs. GSD records the

View File

@@ -54,9 +54,12 @@ Installed: 1.39.0
Latest: 1.41.0
### What's New
────────────────────────────────────────────────────────────
---
[changelog entries for 1.40.0 and 1.41.0]
────────────────────────────────────────────────────────────
---
Proceed with update? [Yes, update now / No, cancel]
```

View File

@@ -202,9 +202,7 @@
**对于 checkpoint:human-verify:**
```
╔═══════════════════════════════════════════════════════╗
║ CHECKPOINT: 需要验证 ║
╚═══════════════════════════════════════════════════════╝
### CHECKPOINT: 需要验证
进度: 5/8 任务完成
任务: 响应式仪表板布局
@@ -217,16 +215,14 @@
3. 平板端 (768px): 侧边栏折叠为图标
4. 移动端 (375px): 侧边栏隐藏,出现汉堡菜单
────────────────────────────────────────────────────────
→ 你的操作: 输入 "approved" 或描述问题
────────────────────────────────────────────────────────
---
**→ 你的操作: 输入 "approved" 或描述问题**
```
**对于 checkpoint:decision:**
```
╔═══════════════════════════════════════════════════════╗
║ CHECKPOINT: 需要决策 ║
╚═══════════════════════════════════════════════════════╝
### CHECKPOINT: 需要决策
进度: 2/6 任务完成
任务: 选择认证提供商
@@ -248,9 +244,9 @@
优点: 免费,无供应商锁定,广泛采用
缺点: 更多设置工作,自行 DIY 安全更新
────────────────────────────────────────────────────────
→ 你的操作: 选择 supabase、clerk 或 nextauth
────────────────────────────────────────────────────────
---
**→ 你的操作: 选择 supabase、clerk 或 nextauth**
```
## 认证门控

View File

@@ -2,14 +2,43 @@
面向用户的 GSD 输出的视觉模式。编排器通过 @ 引用此文件。
## 分隔符与横幅
**绝不要输出固定宽度的制表符(box-drawing)长串。** 由 `━`、`─`、`═` 组成的长串,对渲染
输出的宿主而言只是普通文本。当窗格比该长串更窄时,它会折行,多余的字符落到第二行,于是边框
与它本应框住的标题就散开了。Markdown 会自适应可用宽度,53 个字符的横线不会。
只有三种形式,没有其他:
| 需求 | 输出 |
|---|---|
| 带标题的区块 —— 阶段、检查点、完成、错误 | `### {标题}`(ATX 标题) |
| 两个区块之间的分隔 | 单独一行的 `---`,**其上方必须有一个空行** |
| 带边框的信息面板 | `### {标题}`,随后各行以普通文本列出 |
**`---` 上方的空行是关键,不是排版讲究。** 紧跟在一行文本下方的 `---` 会被解析为该行的
setext 标题下划线,而不是分隔线 —— 那条横线会把上面那行"吞掉"。空行才使它成为分隔线。
(`---` *下方*的空行是可选的:分隔线是叶子块,后面的内容无论如何都会另起一块。)
**阶段横幅只用标题,不要在其上方加 `---`。** ATX 标题本身就是分隔,而且不会像裸的 `---`
那样被误解析。
### 为什么是统一形式,而非按运行时区分
曾考虑过引入 `rendersMarkdown` 能力键:终端类运行时保留线条图形,Markdown 宿主使用
Markdown。该方案被否决 —— 它需要在每个运行时描述符及解析器中新增一个键,并且会留下两套必须
永远保持同步的输出约定,正是本仓库已记录的"分叉"缺陷类别。在纯终端里,标题与分隔线所承载的
结构与原来的横线对并无二致,却不必锁定宽度,因此第二套约定并无收益。若将来确实出现需要线条
图形的运行时,届时再依据该证据添加此键。
---
## 阶段横幅
用于主要工作流过渡。
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► {阶段名称}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
### GSD ► {阶段名称}
```
**阶段名称(大写):**
@@ -25,20 +54,18 @@
---
## 检查点框
## 检查点面板
需要用户操作。62 字符宽度。
需要用户操作。
```
╔══════════════════════════════════════════════════════════════╗
║ CHECKPOINT: {类型} ║
╚══════════════════════════════════════════════════════════════╝
### CHECKPOINT: {类型}
{内容}
──────────────────────────────────────────────────────────────
→ {操作提示}
──────────────────────────────────────────────────────────────
---
**→ {操作提示}**
```
**类型:**
@@ -102,7 +129,7 @@
始终在主要完成后。
```
───────────────────────────────────────────────────────────────
---
## ▶ 下一步
@@ -112,22 +139,20 @@
<sub>`/clear` 优先 → 全新上下文窗口</sub>
───────────────────────────────────────────────────────────────
---
**也可选:**
- (根据工作流选填可选命令,例如 `/gsd-progress --next`)
───────────────────────────────────────────────────────────────
---
```
---
## 错误框
## 错误面板
```
╔══════════════════════════════════════════════════════════════╗
║ ERROR ║
╚══════════════════════════════════════════════════════════════╝
### ERROR
{错误描述}
@@ -150,8 +175,16 @@
## 反模式
- 变化的框/横幅宽度
- 混合横幅样式(`===`、`---`、`***`)
- 用 `━`、`─`、`═` 的固定宽度长串作分隔符 —— 在窄窗格中会折行
- 用双线制表符(U+2554、U+2557、U+255A、U+255D、U+2551、U+2560、U+2563)画的边框面板 ——
边框会独立于内容折行。此处以码位命名而非直接展示,因为守卫检查会拒绝这些字符出现在任何
随包分发的内容中。
- 紧跟在一行文本下方、中间没有空行的 `---` —— 那是 setext 标题下划线而非分隔线,会把上面
那行吞掉
- 把标题夹在两条横线之间 —— 标题本身就是分隔
- 混合横幅样式(`===`、`***`)
- 横幅中缺少 `GSD ►` 前缀
- 随机 emoji(`🚀`、`✨`、`💫`)
- 完成后缺少下一步区块
- 完成后缺少下一步区块
由 `tests/responsive-separators.test.cjs` 强制执行。