* 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:
5
.changeset/clever-eagles-jump.md
Normal file
5
.changeset/clever-eagles-jump.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Changed
|
||||
pr: 3789
|
||||
---
|
||||
**Section separators now render responsively instead of wrapping** — stage banners, checkpoints, completion and error panels used fixed-width runs of box-drawing characters (a 53-column `━` rule, a 62-column `╔═╗` box). In a narrower pane those runs wrap and the border comes apart from the heading it framed. GSD now emits Markdown headings and `---` thematic breaks, which adapt to the available width in every runtime. (#3028)
|
||||
@@ -134,9 +134,7 @@ FRAMEWORK_RECOMMENDATION:
|
||||
Display to user:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
FRAMEWORK RECOMMENDATION
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### FRAMEWORK RECOMMENDATION
|
||||
|
||||
◆ Primary Pick: {framework}
|
||||
{rationale}
|
||||
|
||||
@@ -86,12 +86,14 @@ For each directory found:
|
||||
Display format:
|
||||
```
|
||||
Quick Tasks
|
||||
────────────────────────────────────────────────────────────
|
||||
|
||||
---
|
||||
slug date status
|
||||
backup-s3-policy 2026-04-10 in-progress
|
||||
auth-token-refresh-fix 2026-04-09 complete ✓
|
||||
update-node-deps 2026-04-08 abandoned? (>7 days, no summary)
|
||||
────────────────────────────────────────────────────────────
|
||||
|
||||
---
|
||||
3 tasks (1 complete, 2 incomplete/in-progress)
|
||||
```
|
||||
|
||||
@@ -113,12 +115,14 @@ If no directory found, print `No quick task found with slug: {SLUG}` and stop.
|
||||
Read PLAN.md and SUMMARY.md (if exists) for the given slug. Display:
|
||||
```
|
||||
Quick Task: {slug}
|
||||
─────────────────────────────────────
|
||||
|
||||
---
|
||||
Plan file: .planning/quick/{dir}/PLAN.md
|
||||
Status: {status from SUMMARY.md frontmatter, or "no summary yet"}
|
||||
Description: {first non-empty line from PLAN.md after frontmatter}
|
||||
Last action: {last meaningful line of SUMMARY.md, or "none"}
|
||||
─────────────────────────────────────
|
||||
|
||||
---
|
||||
Resume with: /gsd:quick resume {slug}
|
||||
```
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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**
|
||||
```
|
||||
|
||||
## 认证门控
|
||||
|
||||
@@ -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` 强制执行。
|
||||
@@ -323,9 +323,7 @@ When Claude encounters `type="checkpoint:*"`:
|
||||
|
||||
**For checkpoint:human-verify:**
|
||||
```
|
||||
╔═══════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Verification Required ║
|
||||
╚═══════════════════════════════════════════════════════╝
|
||||
### CHECKPOINT: Verification Required
|
||||
|
||||
Progress: 5/8 tasks complete
|
||||
Task: Responsive dashboard layout
|
||||
@@ -338,16 +336,14 @@ How to verify:
|
||||
3. Tablet (768px): Sidebar collapses to icons
|
||||
4. Mobile (375px): Sidebar hidden, hamburger menu appears
|
||||
|
||||
────────────────────────────────────────────────────────
|
||||
→ YOUR ACTION: Type "approved" or describe issues
|
||||
────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**→ YOUR ACTION: Type "approved" or describe issues**
|
||||
```
|
||||
|
||||
**For checkpoint:decision:**
|
||||
```
|
||||
╔═══════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Decision Required ║
|
||||
╚═══════════════════════════════════════════════════════╝
|
||||
### CHECKPOINT: Decision Required
|
||||
|
||||
Progress: 2/6 tasks complete
|
||||
Task: Select authentication provider
|
||||
@@ -369,16 +365,14 @@ Options:
|
||||
Pros: Free, no vendor lock-in, widely adopted
|
||||
Cons: More setup work, DIY security updates
|
||||
|
||||
────────────────────────────────────────────────────────
|
||||
→ YOUR ACTION: Select supabase, clerk, or nextauth
|
||||
────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**→ YOUR ACTION: Select supabase, clerk, or nextauth**
|
||||
```
|
||||
|
||||
**For checkpoint:human-action:**
|
||||
```
|
||||
╔═══════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Action Required ║
|
||||
╚═══════════════════════════════════════════════════════╝
|
||||
### CHECKPOINT: Action Required
|
||||
|
||||
Progress: 3/8 tasks complete
|
||||
Task: Deploy to Vercel
|
||||
@@ -393,9 +387,9 @@ What you need to do:
|
||||
|
||||
I'll verify: vercel whoami returns your account
|
||||
|
||||
────────────────────────────────────────────────────────
|
||||
→ YOUR ACTION: Type "done" when authenticated
|
||||
────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**→ YOUR ACTION: Type "done" when authenticated**
|
||||
```
|
||||
</execution_protocol>
|
||||
|
||||
|
||||
@@ -40,9 +40,7 @@ The executor MUST:
|
||||
2. Emit a structured halt report:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
MVP+TDD GATE TRIPPED — Plan {plan_id}, Task {task_id}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### MVP+TDD GATE TRIPPED — Plan {plan_id}, Task {task_id}
|
||||
|
||||
Reason: {missing_red_commit | red_commit_not_failing | feat_before_test}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Reusable prompt patterns for structured gate checks in workflows and agents.
|
||||
|
||||
**For checkpoint box format details, see `gsd-core/references/ui-brand.md`** -- checkpoint boxes use double-line border drawing with 62-character inner width.
|
||||
**For checkpoint format details, see `gsd-core/references/ui-brand.md`** -- a checkpoint is a `### CHECKPOINT: {Type}` heading followed by its content, then a `---` break and the bolded action prompt. Never a fixed-width drawn border: it wraps in a narrow pane.
|
||||
|
||||
## Rules
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
extraction restores byte-budget headroom the frozen ceiling exists to provide.
|
||||
-->
|
||||
|
||||
|
||||
**Exception:** If `gaps_found`, the `verify_phase_goal` step already presents the gap-closure path (`/gsd:plan-phase {X} --gaps`). No additional routing needed — skip auto-advance.
|
||||
|
||||
**No-transition check (spawned by auto-advance chain):**
|
||||
@@ -41,10 +40,9 @@ STOP. Do not proceed to auto-advance or transition.
|
||||
**If `--auto` flag present OR `AUTO_MODE` is true (AND verification passed with no gaps):**
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════╗
|
||||
║ AUTO-ADVANCING → TRANSITION ║
|
||||
║ Phase {X} verified, continuing chain ║
|
||||
╚══════════════════════════════════════════╝
|
||||
### AUTO-ADVANCING → TRANSITION
|
||||
|
||||
Phase {X} verified, continuing chain
|
||||
```
|
||||
|
||||
Execute the transition workflow inline (do NOT use Agent — orchestrator context is ~10-15%, transition needs phase completion data already in context):
|
||||
|
||||
@@ -289,9 +289,7 @@ When `workflow.tdd_mode` is enabled, the execute-phase orchestrator inserts a co
|
||||
### Review Checkpoint Format
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
TDD REVIEW — Phase {X}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### TDD REVIEW — Phase {X}
|
||||
|
||||
TDD Plans: {count} | Gate violations: {count}
|
||||
|
||||
|
||||
@@ -2,14 +2,51 @@
|
||||
|
||||
Visual patterns for user-facing GSD output. Orchestrators @-reference this file.
|
||||
|
||||
## Separators and Banners
|
||||
|
||||
**Never emit a fixed-width run of box-drawing characters.** A run of `━`, `─` or
|
||||
`═` is ordinary text to the host that renders your output. In a narrower pane it
|
||||
wraps, leaving orphan glyphs on a second line and coming apart from the heading it
|
||||
was meant to frame. Markdown adapts to the available width; a 53-character rule
|
||||
does not.
|
||||
|
||||
Three forms, and nothing else:
|
||||
|
||||
| Need | Emit |
|
||||
|---|---|
|
||||
| A titled section — stage, phase, checkpoint, completion, error | `### {TITLE}` (ATX heading) |
|
||||
| A break between two sections | `---` on its own line, **with a blank line above it** |
|
||||
| A framed panel of rows | `### {TITLE}` followed by the rows as plain lines |
|
||||
|
||||
**The blank line above `---` is load-bearing, not cosmetic.** A `---` placed
|
||||
directly under a line of text is parsed as a setext heading underline for that
|
||||
line, not as a thematic break — the rule silently swallows the line above it. A
|
||||
blank line is what makes it a thematic break. (A blank line *after* `---` is
|
||||
optional: a thematic break is a leaf block, so whatever follows starts a new
|
||||
block either way. Add one where it reads better.)
|
||||
|
||||
**A stage banner is a heading alone — do not put a `---` above it.** An ATX
|
||||
heading already separates, and it cannot be misparsed the way a bare `---` can.
|
||||
|
||||
### Why this is unconditional, not per-runtime
|
||||
|
||||
The alternative considered was a `rendersMarkdown` capability key, keeping
|
||||
line-art for terminal-oriented runtimes and Markdown for Markdown hosts. It was
|
||||
rejected: it needs a new descriptor key across every runtime plus the resolver,
|
||||
and it leaves two output conventions to keep in sync forever — the divergence
|
||||
class this repo already has a defect entry for. A heading and a thematic break
|
||||
carry the same structure in a plain terminal that a rule pair did, without
|
||||
committing to a width, so the second convention buys nothing. If a runtime ever
|
||||
turns up that genuinely needs line-art, add the key then, against that evidence.
|
||||
|
||||
---
|
||||
|
||||
## Stage Banners
|
||||
|
||||
Use for major workflow transitions.
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► {STAGE NAME}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► {STAGE NAME}
|
||||
```
|
||||
|
||||
**Stage names (uppercase):**
|
||||
@@ -25,20 +62,18 @@ Use for major workflow transitions.
|
||||
|
||||
---
|
||||
|
||||
## Checkpoint Boxes
|
||||
## Checkpoint Panels
|
||||
|
||||
User action required. 62-character width.
|
||||
User action required.
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: {Type} ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### CHECKPOINT: {Type}
|
||||
|
||||
{Content}
|
||||
|
||||
──────────────────────────────────────────────────────────────
|
||||
→ {ACTION PROMPT}
|
||||
──────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**→ {ACTION PROMPT}**
|
||||
```
|
||||
|
||||
**Types:**
|
||||
@@ -60,6 +95,9 @@ User action required. 62-character width.
|
||||
🎉 Milestone complete (only in banner)
|
||||
```
|
||||
|
||||
Status symbols are single characters, not runs — they do not wrap and are
|
||||
unaffected by the separator rule above.
|
||||
|
||||
---
|
||||
|
||||
## Progress Display
|
||||
@@ -79,6 +117,9 @@ Tasks: 2/4 complete
|
||||
Plans: 3/5 complete
|
||||
```
|
||||
|
||||
The bar itself is a fixed 10-cell gauge, not a separator; it is intentionally
|
||||
fixed-width and stays as it is.
|
||||
|
||||
---
|
||||
|
||||
## Spawning Indicators
|
||||
@@ -104,7 +145,7 @@ Plans: 3/5 complete
|
||||
Always at end of major completions.
|
||||
|
||||
```
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up
|
||||
|
||||
@@ -114,23 +155,19 @@ Always at end of major completions.
|
||||
|
||||
`{copy-paste command}`
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**Also available:**
|
||||
- `/gsd-alternative-1` — description
|
||||
- `/gsd-alternative-2` — description
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Box
|
||||
## Error Panel
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ ERROR ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### ERROR
|
||||
|
||||
{Error description}
|
||||
|
||||
@@ -149,14 +186,21 @@ Always at end of major completions.
|
||||
| 3 | ○ | 0/2 | 0% |
|
||||
```
|
||||
|
||||
Table rules use ASCII `-`, never box-drawing characters.
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- Varying box/banner widths
|
||||
- Mixing banner styles (`===`, `---`, `***`)
|
||||
- Fixed-width runs of `━`, `─` or `═` as separators — they wrap in a narrow pane
|
||||
- Box panels drawn with double-line box characters (U+2554, U+2557, U+255A, U+255D, U+2551, U+2560, U+2563) — the borders wrap independently of their contents. They are named here by code point rather than shown, because the guard below rejects the characters themselves anywhere in shipped content.
|
||||
- A `---` directly under a line of text with no blank line between — that is a setext heading underline, not a break, and it swallows the line above
|
||||
- Boxing a heading between two rules — the heading is the separator
|
||||
- Mixing banner styles (`===`, `***`)
|
||||
- Skipping `GSD ►` prefix in banners
|
||||
- Random emoji (`🚀`, `✨`, `💫`)
|
||||
- Missing Next Up block after completions
|
||||
|
||||
Enforced by `tests/responsive-separators.test.cjs`.
|
||||
|
||||
</ui_patterns>
|
||||
|
||||
@@ -63,9 +63,7 @@ Exit.
|
||||
|
||||
Present banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► ADD TESTS — Phase ${phase_number}: ${phase_name}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► ADD TESTS — Phase ${phase_number}: ${phase_name}
|
||||
```
|
||||
</step>
|
||||
|
||||
@@ -111,7 +109,6 @@ Read each file to verify classification. Don't classify based on filename alone.
|
||||
<step name="present_classification">
|
||||
Present the classification to the user for confirmation before proceeding:
|
||||
|
||||
|
||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
||||
|
||||
```
|
||||
@@ -280,9 +277,7 @@ For each approved E2E test:
|
||||
Create a test coverage report and present to user:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► TEST GENERATION COMPLETE
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► TEST GENERATION COMPLETE
|
||||
|
||||
## Results
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ Continue (non-blocking).
|
||||
AI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-AI-SPEC.md 2>/dev/null | head -1)
|
||||
```
|
||||
|
||||
|
||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
||||
**If exists:** Use AskUserQuestion:
|
||||
- header: "Existing AI-SPEC"
|
||||
@@ -96,9 +95,7 @@ If "Update": continue to step 5.
|
||||
|
||||
Display:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AI DESIGN CONTRACT — PHASE {N}: {name}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AI DESIGN CONTRACT — PHASE {N}: {name}
|
||||
|
||||
◆ Step 1/4 — Framework Selection...
|
||||
```
|
||||
@@ -265,9 +262,7 @@ gsd_run query commit "docs({phase_slug}): generate AI-SPEC.md — {primary_frame
|
||||
## 12. Display Completion
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AI-SPEC COMPLETE — PHASE {N}: {name}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AI-SPEC COMPLETE — PHASE {N}: {name}
|
||||
|
||||
◆ Framework: {primary_framework}
|
||||
◆ System Type: {system_type}
|
||||
|
||||
@@ -244,7 +244,7 @@ Output this markdown directly (not as a code block). Route based on status:
|
||||
|
||||
All requirements covered. Cross-phase integration verified. E2E flows complete.
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
|
||||
|
||||
@@ -254,7 +254,7 @@ All requirements covered. Cross-phase integration verified. E2E flows complete.
|
||||
|
||||
/gsd:complete-milestone {version}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
@@ -289,7 +289,7 @@ All requirements covered. Cross-phase integration verified. E2E flows complete.
|
||||
|
||||
Phases needing validation: run `/gsd:validate-phase {N}` for each flagged phase.
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
|
||||
|
||||
@@ -310,13 +310,13 @@ For Nyquist-coverage gaps flagged in the table above, prefer running
|
||||
<N>` if SECURITY.md was flagged) before inserting a new closure phase —
|
||||
they may close the gap retroactively without a new phase.
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**Also available:**
|
||||
- cat .planning/v{version}-MILESTONE-AUDIT.md — see full report
|
||||
- /gsd:complete-milestone {version} — proceed anyway (accept tech debt)
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
@@ -338,7 +338,7 @@ All requirements met. No critical blockers. Accumulated tech debt needs review.
|
||||
|
||||
### Total: {N} items across {M} phases
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Options
|
||||
|
||||
@@ -356,7 +356,7 @@ Insert a closure phase using the standard chain:
|
||||
/gsd:plan-phase <N>
|
||||
/gsd:execute-phase <N>
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
</offer_next>
|
||||
|
||||
<success_criteria>
|
||||
|
||||
@@ -106,9 +106,7 @@ Parse JSON for: `milestone_version`, `milestone_name`, `phase_count`, `completed
|
||||
Display startup banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTONOMOUS
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AUTONOMOUS
|
||||
|
||||
Milestone: {milestone_version} — {milestone_name}
|
||||
Phases: {phase_count} total, {completed_phases} complete
|
||||
@@ -175,9 +173,7 @@ Exit cleanly.
|
||||
**If no incomplete phases remain:**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTONOMOUS ▸ COMPLETE 🎉
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AUTONOMOUS ▸ COMPLETE 🎉
|
||||
|
||||
All phases complete! Nothing left to do.
|
||||
```
|
||||
@@ -216,9 +212,7 @@ Extract `phase_name`, `goal`, `success_criteria` from each. Store for use in exe
|
||||
For the current phase, display the progress banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTONOMOUS ▸ Phase {N}/{T}: {Name} [████░░░░] {P}%
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AUTONOMOUS ▸ Phase {N}/{T}: {Name} [████░░░░] {P}%
|
||||
```
|
||||
|
||||
Where N is the ROADMAP phase number, T is the milestone `phase_count`, and P = completed milestone phases / T × 100. Use `phase_count`, not remaining phases: phase 63 in a 7-phase milestone is `Phase 63/7`, not `Phase 63/3`. If N > T, render `Phase {N} ({position}/{T})`. Use an 8-character bar with █ and ░.
|
||||
@@ -594,9 +588,7 @@ Read and execute: `$HOME/.claude/gsd-core/references/autonomous-smart-discuss.md
|
||||
**If `TO_PHASE` is set and current phase number >= `TO_PHASE`:** The target phase has been reached. Do not iterate further. Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTONOMOUS ▸ --to ${TO_PHASE} REACHED
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AUTONOMOUS ▸ --to ${TO_PHASE} REACHED
|
||||
|
||||
Completed through phase ${TO_PHASE} as requested.
|
||||
Remaining phases were not executed.
|
||||
@@ -646,9 +638,7 @@ If all phases complete, proceed to lifecycle step.
|
||||
**If `ONLY_PHASE` is set:** Skip lifecycle. A single phase does not trigger audit/complete/cleanup. Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTONOMOUS ▸ PHASE ${ONLY_PHASE} COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AUTONOMOUS ▸ PHASE ${ONLY_PHASE} COMPLETE ✓
|
||||
|
||||
Phase ${ONLY_PHASE}: ${PHASE_NAME} — Done
|
||||
Mode: Single phase (--only)
|
||||
@@ -664,9 +654,7 @@ Exit cleanly.
|
||||
Display lifecycle transition banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTONOMOUS ▸ LIFECYCLE
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AUTONOMOUS ▸ LIFECYCLE
|
||||
|
||||
All phases complete → Starting lifecycle: audit → complete → cleanup
|
||||
Milestone: {milestone_version} — {milestone_name}
|
||||
@@ -755,9 +743,7 @@ Cleanup shows its own dry-run and asks user for approval internally — this is
|
||||
Display final completion banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTONOMOUS ▸ COMPLETE 🎉
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AUTONOMOUS ▸ COMPLETE 🎉
|
||||
|
||||
Milestone: {milestone_version} — {milestone_name}
|
||||
Status: Complete ✅
|
||||
@@ -788,9 +774,7 @@ When any phase operation fails or a blocker is detected, present 3 options via A
|
||||
**On "Stop autonomous mode":** Display progress summary:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTONOMOUS ▸ STOPPED
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AUTONOMOUS ▸ STOPPED
|
||||
|
||||
Completed: {list of completed phases}
|
||||
Skipped: {list of skipped phases}
|
||||
|
||||
@@ -690,11 +690,11 @@ TOTAL=$(echo "$FRONTMATTER" | grep "total:" | head -1 | cut -d: -f2 | xargs)
|
||||
Display inline summary to user:
|
||||
|
||||
```
|
||||
═══════════════════════════════════════════════════════════════
|
||||
---
|
||||
|
||||
Code Review Complete: Phase ${PHASE_NUMBER} (${PHASE_NAME})
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
Depth: ${REVIEW_DEPTH} (${DEPTH_PROVENANCE})
|
||||
Files Reviewed: ${FILES_REVIEWED}
|
||||
@@ -703,10 +703,11 @@ Display inline summary to user:
|
||||
Critical: ${CRITICAL}
|
||||
Warning: ${WARNING}
|
||||
Info: ${INFO}
|
||||
──────────
|
||||
|
||||
---
|
||||
Total: ${TOTAL}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
```
|
||||
|
||||
If status is "clean":
|
||||
@@ -735,7 +736,7 @@ grep -A 3 "^### CR-\|^### BL-\|^### WR-" "${REVIEW_PATH}" | head -n 12
|
||||
|
||||
**Note on tests:** Automated tests for this command and workflow are planned for Phase 4 (Pipeline Integration & Testing, requirement INFR-03). Phase 2 focuses on correct implementation; Phase 4 adds regression coverage across platforms.
|
||||
|
||||
═══════════════════════════════════════════════════════════════
|
||||
---
|
||||
</step>
|
||||
|
||||
</process>
|
||||
|
||||
@@ -44,7 +44,8 @@ For each file found, parse frontmatter fields (`status`, `trigger`, `updated`) a
|
||||
|
||||
```
|
||||
Active Debug Sessions
|
||||
─────────────────────────────────────────────
|
||||
|
||||
---
|
||||
# Slug Status Updated
|
||||
1 auth-token-null investigating 2026-04-12
|
||||
hypothesis: JWT decode fails when token contains nested claims
|
||||
@@ -53,7 +54,8 @@ Active Debug Sessions
|
||||
2 form-submit-500 fixing 2026-04-11
|
||||
hypothesis: Missing null check on req.body.user
|
||||
next: Verify fix passes regression test
|
||||
─────────────────────────────────────────────
|
||||
|
||||
---
|
||||
Run `/gsd:debug continue <slug>` to resume a session.
|
||||
No sessions? `/gsd:debug <description>` to start.
|
||||
```
|
||||
|
||||
@@ -258,9 +258,7 @@ gsd_run query commit "docs({phase_num}): add root causes from diagnosis" --files
|
||||
|
||||
Display:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► DIAGNOSIS COMPLETE
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► DIAGNOSIS COMPLETE
|
||||
|
||||
| Gap (Truth) | Root Cause | Files |
|
||||
|-------------|------------|-------|
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
Display banner:
|
||||
```text
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTO-ADVANCING TO PLAN
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AUTO-ADVANCING TO PLAN
|
||||
|
||||
Context captured (assumptions mode). Launching plan-phase...
|
||||
```
|
||||
|
||||
@@ -48,9 +48,7 @@ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-pars
|
||||
|
||||
Banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTO-ADVANCING TO PLAN
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AUTO-ADVANCING TO PLAN
|
||||
|
||||
Context captured. Launching plan-phase...
|
||||
```
|
||||
@@ -69,9 +67,7 @@ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-pars
|
||||
|
||||
- **PHASE COMPLETE** → Full chain succeeded. Display:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► PHASE ${PHASE} COMPLETE
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► PHASE ${PHASE} COMPLETE
|
||||
|
||||
Auto-advance pipeline finished: discuss → plan → execute
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/d
|
||||
<step name="validate">
|
||||
**Check for input.**
|
||||
|
||||
|
||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
||||
If `$ARGUMENTS` is empty, ask via AskUserQuestion:
|
||||
|
||||
@@ -85,9 +84,7 @@ Which approach fits better?
|
||||
**Show the routing decision.**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► ROUTING
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► ROUTING
|
||||
|
||||
**Input:** {first 80 chars of $ARGUMENTS}
|
||||
**Routing to:** {chosen command}
|
||||
|
||||
@@ -30,9 +30,7 @@ AGENT_SKILLS_AUDITOR=$(gsd_run query agent-skills gsd-eval-auditor)
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► EVAL AUDIT — PHASE {N}: {name}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► EVAL AUDIT — PHASE {N}: {name}
|
||||
```
|
||||
|
||||
## 1. Detect Input State
|
||||
@@ -47,7 +45,6 @@ EVAL_REVIEW_FILE=$(ls "${PHASE_DIR}"/*-EVAL-REVIEW.md 2>/dev/null | head -1)
|
||||
**State B** — SUMMARY.md exists, no AI-SPEC.md: Audit against general best practices
|
||||
**State C** — No SUMMARY.md: Exit — "Phase {N} not executed. Run /gsd:execute-phase {N} first."
|
||||
|
||||
|
||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
||||
**If `EVAL_REVIEW_FILE` non-empty:** Use AskUserQuestion:
|
||||
- header: "Existing Eval Review"
|
||||
@@ -121,9 +118,7 @@ Read the written EVAL-REVIEW.md. Extract:
|
||||
## 5. Display Summary
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► EVAL AUDIT COMPLETE — PHASE {N}: {name}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► EVAL AUDIT COMPLETE — PHASE {N}: {name}
|
||||
|
||||
◆ Score: {overall_score}/100
|
||||
◆ Verdict: {verdict}
|
||||
|
||||
@@ -333,7 +333,7 @@ Canonical per-task commit rules live in **`agents/gsd-executor.md`** (`<task_com
|
||||
<step name="checkpoint_protocol">
|
||||
On `type="checkpoint:*"`: automate everything possible first. Checkpoints are for verification/decisions only.
|
||||
|
||||
Display: `CHECKPOINT: [Type]` box → Progress {X}/{Y} → Task name → type-specific content → `YOUR ACTION: [signal]`
|
||||
Display: `### CHECKPOINT: [Type]` heading → Progress {X}/{Y} → Task name → type-specific content → `---` → `**YOUR ACTION: [signal]**`
|
||||
|
||||
| Type | Content | Resume signal |
|
||||
|------|---------|---------------|
|
||||
|
||||
@@ -14,7 +14,6 @@ RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/d
|
||||
|
||||
**If `response_language` is set:** All user-facing questions, prompts, and explanations in this workflow MUST be presented in `{response_language}`. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated.
|
||||
|
||||
|
||||
<step name="parse_args">
|
||||
**Parse arguments:**
|
||||
|
||||
@@ -136,9 +135,7 @@ If the check prints, append it to the Warnings section of the report as `[W025]`
|
||||
**Format and display results:**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD Health Check
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD Health Check
|
||||
|
||||
Status: HEALTHY | DEGRADED | BROKEN
|
||||
Errors: N | Warnings: N | Info: N
|
||||
|
||||
@@ -19,9 +19,7 @@ RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/d
|
||||
**If `response_language` is set:** All user-facing questions, prompts, and explanations in this workflow MUST be presented in `{response_language}`. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated.
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► IMPORT
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► IMPORT
|
||||
```
|
||||
|
||||
</step>
|
||||
@@ -57,9 +55,7 @@ test -f "{FILEPATH}" || echo "FILE_NOT_FOUND"
|
||||
If FILE_NOT_FOUND: display error and exit:
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ ERROR ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### ERROR
|
||||
|
||||
File not found: {FILEPATH}
|
||||
|
||||
@@ -251,9 +247,7 @@ gsd_run query commit "docs({phase}): import plan from {basename FILEPATH}" --fil
|
||||
|
||||
Display completion:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► IMPORT COMPLETE
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► IMPORT COMPLETE
|
||||
```
|
||||
|
||||
Show: plan filename written, phase directory, validation result, next steps.
|
||||
|
||||
@@ -24,7 +24,6 @@ RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/d
|
||||
|
||||
**If `response_language` is set:** All user-facing questions, prompts, and explanations in this workflow MUST be presented in `{response_language}`. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated.
|
||||
|
||||
|
||||
<step name="preflight">
|
||||
Verify prerequisites:
|
||||
|
||||
@@ -351,7 +350,7 @@ AskUserQuestion:
|
||||
|
||||
<step name="report">
|
||||
```
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## Inbox Triage Complete
|
||||
|
||||
@@ -368,7 +367,7 @@ Next steps:
|
||||
- Merge ready PRs
|
||||
- Triage unclassified issues
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
```
|
||||
</step>
|
||||
|
||||
|
||||
@@ -14,9 +14,7 @@ Scan a repo for mixed planning documents (ADR, PRD, SPEC, DOC), synthesize them
|
||||
Display the stage banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► INGEST DOCS
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► INGEST DOCS
|
||||
```
|
||||
|
||||
</step>
|
||||
@@ -332,9 +330,7 @@ gsd_run commit \
|
||||
Display completion:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► INGEST DOCS COMPLETE
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► INGEST DOCS COMPLETE
|
||||
```
|
||||
|
||||
Show:
|
||||
|
||||
@@ -37,11 +37,13 @@ Render the seeds as a table, sorted by `seed_id` (already sorted by the tool). T
|
||||
|
||||
```
|
||||
Seeds
|
||||
─────────────────────────────────────────────────────────────────────
|
||||
|
||||
---
|
||||
ID Status Scope Trigger Title
|
||||
SEED-001 dormant large when websockets land Real-time collaboration
|
||||
SEED-006 triggered medium MILE-04 planning Remove legacy auth crates
|
||||
─────────────────────────────────────────────────────────────────────
|
||||
|
||||
---
|
||||
<count> seeds (<summary rendered as "N status" pairs, e.g. "1 dormant, 1 triggered">)
|
||||
```
|
||||
|
||||
|
||||
@@ -40,16 +40,15 @@ These are empty strings by default. Set via: `gsd-tools.cjs query config-set man
|
||||
Display startup banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► MANAGER
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► MANAGER
|
||||
|
||||
{milestone_version} — {milestone_name}
|
||||
{phase_count} phases · {completed_count} complete
|
||||
|
||||
✓ Discuss → inline ◆ Plan/Execute → inline (background when FLATTEN=false)
|
||||
Dashboard auto-refreshes when background work is active.
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
Proceed to dashboard step.
|
||||
@@ -92,9 +91,7 @@ Use `deps_display` from init JSON for the Deps column — shows which phases thi
|
||||
Example output:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► DASHBOARD
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► DASHBOARD
|
||||
████████████░░░░░░░░ 60% (3/5 phases)
|
||||
◆ Background: Planning Phase 4
|
||||
| # | Phase | Deps | D | P | E | Status |
|
||||
@@ -116,9 +113,7 @@ Use `queued_milestone_version` and `queued_milestone_name` for the header. Phase
|
||||
Example:
|
||||
|
||||
```
|
||||
───────────────────────────────────────────────────────────────
|
||||
◆ Queued — {queued_milestone_version} {queued_milestone_name} ({queued_phases.length} phases)
|
||||
───────────────────────────────────────────────────────────────
|
||||
### ◆ Queued — {queued_milestone_version} {queued_milestone_name} ({queued_phases.length} phases)
|
||||
| # | Phase | Deps | Status |
|
||||
|---|----------------------|------|--------------|
|
||||
| 31| Email Logs | — | · Queued |
|
||||
@@ -134,16 +129,13 @@ Queued phases are NOT eligible for the Continue action menu — they live in a f
|
||||
If `all_complete` is true:
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ MILESTONE COMPLETE ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### MILESTONE COMPLETE
|
||||
|
||||
All {phase_count} phases verified complete. Ready for final steps:
|
||||
→ /gsd:verify-work — run acceptance testing
|
||||
→ /gsd:complete-milestone — archive and wrap up
|
||||
```
|
||||
|
||||
|
||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
||||
Ask user via AskUserQuestion:
|
||||
- **question:** "All phases complete. What next?"
|
||||
@@ -188,9 +180,7 @@ Handle responses:
|
||||
Display recommendations compactly:
|
||||
|
||||
```
|
||||
───────────────────────────────────────────────────────────────
|
||||
▶ Next Steps
|
||||
───────────────────────────────────────────────────────────────
|
||||
### ▶ Next Steps
|
||||
|
||||
Continue:
|
||||
→ Execute Phase 32 (background)
|
||||
@@ -411,15 +401,14 @@ Classify the error:
|
||||
Display final status with progress bar:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SESSION END
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SESSION END
|
||||
|
||||
{milestone_version} — {milestone_name}
|
||||
{PROGRESS_BAR} {progress_pct}% ({completed_count}/{phase_count} phases)
|
||||
|
||||
Resume anytime: /gsd:manager
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
**Note:** Any background agents still running will continue to completion. Their results will be visible on next `/gsd:manager` or `/gsd:progress` invocation.
|
||||
|
||||
@@ -131,9 +131,7 @@ AskUserQuestion(
|
||||
Before writing any files, present a summary of what was gathered and ask for confirmation.
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► MILESTONE SUMMARY
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► MILESTONE SUMMARY
|
||||
|
||||
**Milestone v[X.Y]: [Name]**
|
||||
|
||||
@@ -325,9 +323,7 @@ AskUserQuestion: "Research the domain ecosystem for new features before defining
|
||||
**If user chose "Research first":**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► RESEARCHING
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► RESEARCHING
|
||||
|
||||
◆ Spawning 4 researchers in parallel... (each runs in a subagent — no output until they return, ~1–5 min; expected, not a freeze)
|
||||
→ Stack, Features, Architecture, Pitfalls
|
||||
@@ -422,9 +418,7 @@ This guarantees `gsd-roadmapper` (which lists SUMMARY.md as required reading) ne
|
||||
|
||||
Display key findings from SUMMARY.md:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► RESEARCH COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► RESEARCH COMPLETE ✓
|
||||
|
||||
**Stack additions:** [from SUMMARY.md]
|
||||
**Feature table stakes:** [from SUMMARY.md]
|
||||
@@ -436,9 +430,7 @@ Display key findings from SUMMARY.md:
|
||||
## 9. Define Requirements
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► DEFINING REQUIREMENTS
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► DEFINING REQUIREMENTS
|
||||
```
|
||||
|
||||
Read PROJECT.md: core value, current milestone goals, validated requirements (what exists).
|
||||
@@ -509,9 +501,7 @@ gsd_run query commit "docs: define milestone v[X.Y] requirements" --files .plann
|
||||
## 10. Create Roadmap
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► CREATING ROADMAP
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► CREATING ROADMAP
|
||||
|
||||
◆ Spawning roadmapper... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
@@ -645,9 +635,7 @@ Print a summary:
|
||||
## 11. Done
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► MILESTONE INITIALIZED ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► MILESTONE INITIALIZED ✓
|
||||
|
||||
**Milestone v[X.Y]: [Name]**
|
||||
|
||||
|
||||
@@ -151,9 +151,7 @@ If spike/sketch findings skills exist, read their SKILL.md files to inform the q
|
||||
**Display stage banner:**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► QUESTIONING
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► QUESTIONING
|
||||
```
|
||||
|
||||
**Open the conversation:**
|
||||
@@ -700,9 +698,7 @@ Use AskUserQuestion:
|
||||
Display stage banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► RESEARCHING
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► RESEARCHING
|
||||
|
||||
Researching [domain] ecosystem...
|
||||
```
|
||||
@@ -942,9 +938,7 @@ This guarantees `gsd-roadmapper` (which lists SUMMARY.md as required reading) ne
|
||||
Display research complete banner and key findings:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► RESEARCH COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► RESEARCH COMPLETE ✓
|
||||
|
||||
## Key Findings
|
||||
|
||||
@@ -962,9 +956,7 @@ Files: `.planning/research/`
|
||||
Display stage banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► DEFINING REQUIREMENTS
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► DEFINING REQUIREMENTS
|
||||
```
|
||||
|
||||
**Load context:**
|
||||
@@ -1131,9 +1123,7 @@ When `TEXT_MODE=true` (per the workflow's existing TEXT_MODE handling for non-Cl
|
||||
Display stage banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► CREATING ROADMAP
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► CREATING ROADMAP
|
||||
|
||||
◆ Spawning roadmapper... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
@@ -1300,9 +1290,7 @@ gsd_run query commit "docs: create roadmap ([N] phases)" --files .planning/ROADM
|
||||
Present completion summary:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► PROJECT INITIALIZED ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► PROJECT INITIALIZED ✓
|
||||
|
||||
**[Project Name]**
|
||||
|
||||
@@ -1321,9 +1309,7 @@ Present completion summary:
|
||||
**If auto mode:**
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════╗
|
||||
║ AUTO-ADVANCING → DISCUSS PHASE 1 ║
|
||||
╚══════════════════════════════════════════╝
|
||||
### AUTO-ADVANCING → DISCUSS PHASE 1
|
||||
```
|
||||
|
||||
Exit skill and invoke SlashCommand("/gsd:discuss-phase 1 --auto")
|
||||
@@ -1340,7 +1326,7 @@ PHASE1_HAS_UI=$(echo "$PHASE1_SECTION" | grep -qi "UI hint.*yes" && echo "true"
|
||||
**If Phase 1 has UI (`PHASE1_HAS_UI` is `true`):**
|
||||
|
||||
```
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
|
||||
|
||||
@@ -1356,13 +1342,13 @@ PHASE1_HAS_UI=$(echo "$PHASE1_SECTION" | grep -qi "UI hint.*yes" && echo "true"
|
||||
- /gsd:ui-phase 1 — generate UI design contract (recommended for frontend phases)
|
||||
- /gsd:plan-phase 1 — skip discussion, plan directly
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
```
|
||||
|
||||
**If Phase 1 has no UI:**
|
||||
|
||||
```
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
|
||||
|
||||
@@ -1377,7 +1363,7 @@ PHASE1_HAS_UI=$(echo "$PHASE1_SECTION" | grep -qi "UI hint.*yes" && echo "true"
|
||||
**Also available:**
|
||||
- /gsd:plan-phase 1 — skip discussion, plan directly
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
```
|
||||
|
||||
</process>
|
||||
|
||||
@@ -337,9 +337,7 @@ If user selects "Skip research": skip to step 6.
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► RESEARCHING PHASE {X}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► RESEARCHING PHASE {X}
|
||||
|
||||
◆ Spawning researcher... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
@@ -434,9 +432,7 @@ Resolve active contribution hooks from `PLAN_PRE_HOOKS_JSON` where `kind == "con
|
||||
Display banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SECURITY THREAT MODEL REQUIRED (ASVS L{SECURITY_ASVS})
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SECURITY THREAT MODEL REQUIRED (ASVS L{SECURITY_ASVS})
|
||||
|
||||
Each PLAN.md must include a <threat_model> block.
|
||||
Block on: {SECURITY_BLOCK} severity threats.
|
||||
@@ -522,7 +518,8 @@ Output this markdown directly (not as a code block):
|
||||
## ⚠ UI-SPEC.md missing for Phase {N}
|
||||
▶ Recommended next step:
|
||||
`/gsd:ui-phase {N} ${GSD_WS}` — generate UI design contract before planning
|
||||
───────────────────────────────────────────────
|
||||
|
||||
---
|
||||
Also available:
|
||||
- `/gsd:plan-phase {N} --skip-ui ${GSD_WS}` — plan without UI-SPEC (not recommended for frontend phases)
|
||||
```
|
||||
@@ -618,9 +615,7 @@ Pattern mapper activation is owned by the `pattern-mapper` capability's `plan:pr
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► PATTERN MAPPING PHASE {X}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► PATTERN MAPPING PHASE {X}
|
||||
|
||||
◆ Spawning pattern mapper... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
@@ -690,9 +685,7 @@ independent of the teams-status guard above, AC2).
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► PLANNING PHASE {X}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► PLANNING PHASE {X}
|
||||
|
||||
◆ Spawning planner... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
@@ -993,9 +986,7 @@ Use AskUserQuestion for each gap (or batch if multiple gaps).
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► VERIFYING PLANS
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► VERIFYING PLANS
|
||||
|
||||
◆ Spawning plan checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
@@ -1222,9 +1213,7 @@ BOUNCE_SCRIPT=$(gsd_run query config-get workflow.plan_bounce_script --raw 2>/de
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► BOUNCING PLANS (External Refinement)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► BOUNCING PLANS (External Refinement)
|
||||
|
||||
Script: ${BOUNCE_SCRIPT}
|
||||
Max passes: ${BOUNCE_PASSES}
|
||||
@@ -1498,9 +1487,7 @@ fi
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► AUTO-ADVANCING TO EXECUTE
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► AUTO-ADVANCING TO EXECUTE
|
||||
|
||||
Plans ready. Launching execute-phase...
|
||||
```
|
||||
@@ -1515,9 +1502,7 @@ The `--no-transition` flag tells execute-phase to return status after verificati
|
||||
**Handle execute-phase return:**
|
||||
- **PHASE COMPLETE** → Display final summary:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► PHASE ${PHASE} COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► PHASE ${PHASE} COMPLETE ✓
|
||||
|
||||
Auto-advance pipeline finished.
|
||||
|
||||
@@ -1541,9 +1526,7 @@ Output this markdown directly (not as a code block):
|
||||
|
||||
`${GAPS_EXEC_FLAG}` projects the just-completed planning mode onto the follow-up execute command (#3297): it expands to `--gaps-only` for a `--gaps` planning run (so the handoff points at execute-phase's gap-closure scope — only the newly created `gap_closure: true` plans — not the whole phase) and to empty for a standard or `--reviews` run (whole-phase scope, unchanged). Substitute it verbatim; when empty, collapse the extra space.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► PHASE {X} PLANNED ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► PHASE {X} PLANNED ✓
|
||||
|
||||
**Phase {X}: {Name}** — {N} plan(s) in {M} wave(s)
|
||||
|
||||
@@ -1555,7 +1538,7 @@ Output this markdown directly (not as a code block):
|
||||
Research: {Completed | Used existing | Skipped}
|
||||
Verification: {Passed | Passed with override | Skipped}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
|
||||
|
||||
@@ -1565,7 +1548,7 @@ Verification: {Passed | Passed with override | Skipped}
|
||||
|
||||
/gsd:execute-phase {X} ${GAPS_EXEC_FLAG} ${GSD_WS}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**Also available:**
|
||||
- cat .planning/phases/{phase-dir}/*-PLAN.md — review plans
|
||||
@@ -1573,7 +1556,7 @@ Verification: {Passed | Passed with override | Skipped}
|
||||
- /gsd:review --phase {X} --all — peer review plans with external AIs
|
||||
- /gsd:plan-phase {X} --reviews — replan incorporating review feedback
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
</offer_next>
|
||||
|
||||
<windows_troubleshooting>
|
||||
|
||||
@@ -13,9 +13,7 @@ fi
|
||||
|
||||
2. Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► PRD EXPRESS PATH
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► PRD EXPRESS PATH
|
||||
|
||||
Using PRD: {PRD_FILE}
|
||||
Generating CONTEXT.md from requirements...
|
||||
|
||||
@@ -134,9 +134,7 @@ PHASE_INFO=$(gsd_run roadmap get-phase "${PHASE}")
|
||||
Display startup banner:
|
||||
|
||||
```text
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► PLAN CONVERGENCE — Phase {phase_number}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► PLAN CONVERGENCE — Phase {phase_number}
|
||||
|
||||
Reviewers: {REVIEWER_DISPLAY}
|
||||
Max cycles: {MAX_CYCLES}
|
||||
@@ -400,9 +398,7 @@ gsd_run state planned-phase --phase "${PHASE}" --name "${phase_name}" --plans "$
|
||||
|
||||
Display:
|
||||
```text
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► CONVERGENCE COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► CONVERGENCE COMPLETE ✓
|
||||
|
||||
Phase {phase_number} converged in {cycle} cycle(s).
|
||||
No HIGH concerns remaining.
|
||||
|
||||
@@ -63,9 +63,7 @@ if [ "$PR_STRICT" = "true" ]; then PR_MODE="strict"; else PR_MODE="default"; PR_
|
||||
|
||||
Display:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► PR BRANCH
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► PR BRANCH
|
||||
|
||||
Branch: {CURRENT_BRANCH}
|
||||
Target: {TARGET}
|
||||
|
||||
@@ -21,7 +21,6 @@ RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/d
|
||||
|
||||
**If `response_language` is set:** All user-facing questions, prompts, and explanations in this workflow MUST be presented in `{response_language}`. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated.
|
||||
|
||||
|
||||
## 1. Initialize
|
||||
|
||||
Parse flags from $ARGUMENTS:
|
||||
@@ -37,7 +36,6 @@ PROFILE_PATH="$HOME/.claude/gsd-core/USER-PROFILE.md"
|
||||
|
||||
**If profile exists AND --refresh NOT set AND --questionnaire NOT set:**
|
||||
|
||||
|
||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
||||
Use AskUserQuestion:
|
||||
- header: "Existing Profile"
|
||||
@@ -72,9 +70,7 @@ Continue to step 2.
|
||||
Display consent screen:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD > PROFILE YOUR CODING STYLE
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD > PROFILE YOUR CODING STYLE
|
||||
|
||||
Claude starts every conversation generic. A profile teaches Claude
|
||||
how YOU actually work -- not how you think you work.
|
||||
@@ -421,9 +417,7 @@ If nothing changed: Display "No changes detected -- your profile is already up t
|
||||
**Display final summary:**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD > PROFILE COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD > PROFILE COMPLETE ✓
|
||||
|
||||
Your profile: $HOME/.claude/gsd-core/USER-PROFILE.md
|
||||
```
|
||||
|
||||
@@ -65,63 +65,49 @@ Display banner based on active flags:
|
||||
|
||||
If `$FULL_MODE` (all phases enabled — `--full` or all granular flags):
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► QUICK TASK (FULL)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► QUICK TASK (FULL)
|
||||
|
||||
◆ Discussion + research + plan checking + verification enabled
|
||||
```
|
||||
|
||||
If `$DISCUSS_MODE` and `$VALIDATE_MODE` (no research):
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► QUICK TASK (DISCUSS + VALIDATE)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► QUICK TASK (DISCUSS + VALIDATE)
|
||||
|
||||
◆ Discussion + plan checking + verification enabled
|
||||
```
|
||||
|
||||
If `$DISCUSS_MODE` and `$RESEARCH_MODE` (no validate):
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► QUICK TASK (DISCUSS + RESEARCH)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► QUICK TASK (DISCUSS + RESEARCH)
|
||||
|
||||
◆ Discussion + research enabled
|
||||
```
|
||||
|
||||
If `$RESEARCH_MODE` and `$VALIDATE_MODE` (no discuss):
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► QUICK TASK (RESEARCH + VALIDATE)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► QUICK TASK (RESEARCH + VALIDATE)
|
||||
|
||||
◆ Research + plan checking + verification enabled
|
||||
```
|
||||
|
||||
If `$DISCUSS_MODE` only:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► QUICK TASK (DISCUSS)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► QUICK TASK (DISCUSS)
|
||||
|
||||
◆ Discussion phase enabled — surfacing gray areas before planning
|
||||
```
|
||||
|
||||
If `$RESEARCH_MODE` only:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► QUICK TASK (RESEARCH)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► QUICK TASK (RESEARCH)
|
||||
|
||||
◆ Research phase enabled — investigating approaches before planning
|
||||
```
|
||||
|
||||
If `$VALIDATE_MODE` only:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► QUICK TASK (VALIDATE)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► QUICK TASK (VALIDATE)
|
||||
|
||||
◆ Plan checking + verification enabled
|
||||
```
|
||||
|
||||
@@ -4,9 +4,7 @@ Skip this step entirely if NOT `$DISCUSS_MODE`.
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► DISCUSSING QUICK TASK
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► DISCUSSING QUICK TASK
|
||||
|
||||
◆ Surfacing gray areas for: ${DESCRIPTION}
|
||||
```
|
||||
|
||||
@@ -4,9 +4,7 @@ Skip this step entirely if NOT `$VALIDATE_MODE`.
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► CHECKING PLAN
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► CHECKING PLAN
|
||||
|
||||
◆ Spawning plan checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
|
||||
@@ -4,9 +4,7 @@ Skip this step entirely if NOT `$VALIDATE_MODE`.
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► VERIFYING RESULTS
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► VERIFYING RESULTS
|
||||
|
||||
◆ Spawning verifier... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
|
||||
@@ -4,9 +4,7 @@ Skip this step entirely if NOT `$RESEARCH_MODE`.
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► RESEARCHING QUICK TASK
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► RESEARCHING QUICK TASK
|
||||
|
||||
◆ Investigating approaches for: ${DESCRIPTION} (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
|
||||
@@ -133,17 +133,13 @@ fi
|
||||
Present complete project status to user:
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ PROJECT STATUS ║
|
||||
╠══════════════════════════════════════════════════════════════╣
|
||||
║ Building: [one-liner from PROJECT.md "What This Is"] ║
|
||||
║ ║
|
||||
║ Phase: [X] of [Y] - [Phase name] ║
|
||||
║ Plan: [A] of [B] - [Status] ║
|
||||
║ Progress: [██████░░░░] XX% ║
|
||||
║ ║
|
||||
║ Last activity: [date] - [what happened] ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### PROJECT STATUS
|
||||
|
||||
Building: [one-liner from PROJECT.md "What This Is"]
|
||||
Phase: [X] of [Y] - [Phase name]
|
||||
Plan: [A] of [B] - [Status]
|
||||
Progress: [██████░░░░] XX%
|
||||
Last activity: [date] - [what happened]
|
||||
|
||||
[If incomplete work found:]
|
||||
⚠️ Incomplete work detected:
|
||||
|
||||
@@ -404,9 +404,7 @@ blocked, not silently redirected** (ADR-2782 D5).
|
||||
|
||||
Display progress:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► CROSS-AI REVIEW — Phase {N}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► CROSS-AI REVIEW — Phase {N}
|
||||
|
||||
◆ Reviewing with {CLI}... done ✓
|
||||
◆ Reviewing with {CLI}... done ✓
|
||||
@@ -507,9 +505,7 @@ gsd_run query commit "docs: cross-AI review for phase {N}" --files {phase_dir}/{
|
||||
Display summary:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► REVIEW COMPLETE
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► REVIEW COMPLETE
|
||||
|
||||
Phase {N} reviewed by {count} AI systems.
|
||||
|
||||
|
||||
@@ -747,9 +747,7 @@ Never write a tier the user explicitly chose to keep; the existing value must su
|
||||
Display:
|
||||
|
||||
```text
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► ADVANCED SETTINGS UPDATED
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► ADVANCED SETTINGS UPDATED
|
||||
|
||||
| Setting | Value |
|
||||
|--------------------------------------------|-------|
|
||||
|
||||
@@ -302,9 +302,7 @@ Display the masked confirmation table. **No plaintext API keys appear in this
|
||||
output under any circumstance.**
|
||||
|
||||
```text
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► INTEGRATIONS UPDATED
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► INTEGRATIONS UPDATED
|
||||
|
||||
Search Integrations
|
||||
| Field | Value |
|
||||
|
||||
@@ -606,9 +606,7 @@ Write `~/.gsd/defaults.json` with:
|
||||
Display:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SETTINGS UPDATED
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SETTINGS UPDATED
|
||||
|
||||
| Setting | Value |
|
||||
|----------------------|-------|
|
||||
|
||||
@@ -571,7 +571,7 @@ Each dispatch is best-effort: if it errors, record a warning and continue — ne
|
||||
|
||||
<step name="report">
|
||||
```
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ✓ Phase {X}: {Name} — Shipped
|
||||
|
||||
@@ -587,7 +587,7 @@ Next steps:
|
||||
- /gsd:complete-milestone (if last phase in milestone)
|
||||
- /gsd:progress (to see what's next)
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
```
|
||||
</step>
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@ Read all files referenced by the invoking prompt's execution_context before star
|
||||
|
||||
<step name="banner">
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SKETCH WRAP-UP
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SKETCH WRAP-UP
|
||||
```
|
||||
</step>
|
||||
|
||||
@@ -55,17 +53,15 @@ Present each unprocessed sketch in ascending order. For each sketch, show:
|
||||
|
||||
Then ask the user:
|
||||
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Decision Required ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### CHECKPOINT: Decision Required
|
||||
|
||||
Sketch {NNN}: {name} — Winner: Variant {X}
|
||||
|
||||
{key design decisions summary}
|
||||
|
||||
──────────────────────────────────────────────────────────────
|
||||
→ Include / Exclude / Partial / Let me look at it
|
||||
──────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**→ Include / Exclude / Partial / Let me look at it**
|
||||
|
||||
**If "Let me look at it":**
|
||||
1. Provide: `open .planning/sketches/NNN-name/index.html`
|
||||
@@ -239,9 +235,7 @@ gsd_run query commit "docs(sketch-wrap-up): package [N] sketch findings into pro
|
||||
|
||||
<step name="report">
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SKETCH WRAP-UP COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SKETCH WRAP-UP COMPLETE ✓
|
||||
|
||||
**Curated:** {N} sketches ({included} included, {excluded} excluded)
|
||||
**Design areas:** {list}
|
||||
@@ -252,7 +246,7 @@ gsd_run query commit "docs(sketch-wrap-up): package [N] sketch findings into pro
|
||||
The sketch-findings skill will auto-load when building the UI.
|
||||
```
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up
|
||||
|
||||
@@ -260,7 +254,7 @@ The sketch-findings skill will auto-load when building the UI.
|
||||
|
||||
`/gsd:sketch` (run with no argument — its frontier mode analyzes the sketch landscape and proposes consistency and frontier sketches)
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**Also available:**
|
||||
- `/gsd:plan-phase` — start building the real UI
|
||||
@@ -268,7 +262,7 @@ The sketch-findings skill will auto-load when building the UI.
|
||||
- `/gsd:sketch [idea]` — sketch a specific new design area
|
||||
- `/gsd:explore` — continue exploring
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
</step>
|
||||
|
||||
</process>
|
||||
|
||||
@@ -21,9 +21,7 @@ Read all files referenced by the invoking prompt's execution_context before star
|
||||
|
||||
<step name="banner">
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SKETCHING
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SKETCHING
|
||||
```
|
||||
|
||||
Parse `$ARGUMENTS` for:
|
||||
@@ -272,9 +270,7 @@ open .planning/sketches/NNN-descriptive-name/index.html
|
||||
|
||||
**e.** Present to the user with a checkpoint:
|
||||
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Verification Required ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### CHECKPOINT: Verification Required
|
||||
|
||||
**Sketch {NNN}: {name}**
|
||||
|
||||
@@ -282,9 +278,9 @@ Open: `open .planning/sketches/NNN-name/index.html`
|
||||
|
||||
Compare: {what to look for between variants}
|
||||
|
||||
──────────────────────────────────────────────────────────────
|
||||
→ Which variant feels right? Or cherry-pick elements across variants.
|
||||
──────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**→ Which variant feels right? Or cherry-pick elements across variants.**
|
||||
|
||||
**f.** Handle feedback:
|
||||
- **Pick a direction:** mark winner, move to next sketch
|
||||
@@ -315,9 +311,7 @@ gsd_run query commit "docs(sketch-NNN): [winning direction] — [key visual insi
|
||||
After all sketches complete:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SKETCH COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SKETCH COMPLETE ✓
|
||||
|
||||
## Design Direction
|
||||
{what we landed on overall}
|
||||
@@ -329,7 +323,7 @@ After all sketches complete:
|
||||
{anything unresolved or worth revisiting}
|
||||
```
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up
|
||||
|
||||
@@ -337,14 +331,14 @@ After all sketches complete:
|
||||
|
||||
`/gsd:sketch --wrap-up`
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**Also available:**
|
||||
- `/gsd:sketch` — sketch more (or run with no argument for frontier mode)
|
||||
- `/gsd:plan-phase` — start building the real UI
|
||||
- `/gsd:spike` — spike technical feasibility of a design pattern
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
</step>
|
||||
|
||||
</process>
|
||||
|
||||
@@ -93,9 +93,7 @@ Wait for the user's response before continuing. Map the chosen number to the cor
|
||||
**Show the routing decision.**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SMART ENTRY
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SMART ENTRY
|
||||
|
||||
**Situation:** {situation}
|
||||
**Routing to:** {chosen command}
|
||||
|
||||
@@ -13,9 +13,7 @@ Read all files referenced by the invoking prompt's execution_context before star
|
||||
|
||||
<step name="banner">
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SPIKE WRAP-UP
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SPIKE WRAP-UP
|
||||
```
|
||||
</step>
|
||||
|
||||
@@ -267,9 +265,7 @@ gsd_run query commit "docs(spike-wrap-up): package [N] spike findings into proje
|
||||
|
||||
<step name="report">
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SPIKE WRAP-UP COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SPIKE WRAP-UP COMPLETE ✓
|
||||
|
||||
**Processed:** {N} spikes
|
||||
**Feature areas:** {list}
|
||||
@@ -287,7 +283,7 @@ The spike-findings skill will auto-load in future build conversations.
|
||||
|
||||
After the summary, present next-step options:
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up
|
||||
|
||||
@@ -295,7 +291,7 @@ After the summary, present next-step options:
|
||||
|
||||
`/gsd:spike` (run with no argument — its frontier mode analyzes the spike landscape and proposes integration and frontier spikes)
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**Also available:**
|
||||
- `/gsd:plan-phase` — start planning the real implementation
|
||||
@@ -303,7 +299,7 @@ After the summary, present next-step options:
|
||||
- `/gsd:explore` — continue exploring
|
||||
- Other
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
</step>
|
||||
|
||||
</process>
|
||||
|
||||
@@ -20,12 +20,9 @@ RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/d
|
||||
|
||||
**If `response_language` is set:** All user-facing questions, prompts, and explanations in this workflow MUST be presented in `{response_language}`. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated.
|
||||
|
||||
|
||||
<step name="banner">
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SPIKING
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SPIKING
|
||||
```
|
||||
|
||||
Parse `$ARGUMENTS` for:
|
||||
@@ -175,15 +172,13 @@ Order by risk — most likely to kill the idea runs first.
|
||||
<step name="align">
|
||||
**If `QUICK_MODE` is true:** Skip.
|
||||
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Decision Required ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### CHECKPOINT: Decision Required
|
||||
|
||||
{spike table from decompose step}
|
||||
|
||||
──────────────────────────────────────────────────────────────
|
||||
→ Build all in this order, or adjust the list?
|
||||
──────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**→ Build all in this order, or adjust the list?**
|
||||
</step>
|
||||
|
||||
<step name="research">
|
||||
@@ -358,17 +353,15 @@ tags: [tag1, tag2]
|
||||
- Self-verifiable: run, iterate if findings warrant deeper investigation, update verdict
|
||||
- Needs human judgment: present checkpoint box:
|
||||
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Verification Required ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### CHECKPOINT: Verification Required
|
||||
|
||||
**Spike {NNN}: {name}**
|
||||
**How to run:** {command}
|
||||
**What to expect:** {concrete outcomes}
|
||||
|
||||
──────────────────────────────────────────────────────────────
|
||||
→ Does this match what you expected? Describe what you see.
|
||||
──────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**→ Does this match what you expected? Describe what you see.**
|
||||
|
||||
**h.** Update `.planning/spikes/MANIFEST.md` with the spike's row, setting the Idea column to
|
||||
this spike's idea key.
|
||||
@@ -390,16 +383,14 @@ Do not rush to a verdict. A spike that says "VALIDATED — it works" with no nua
|
||||
|
||||
**k.** If core assumption invalidated:
|
||||
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ CHECKPOINT: Decision Required ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### CHECKPOINT: Decision Required
|
||||
|
||||
Core assumption invalidated by Spike {NNN}.
|
||||
{what was invalidated and why}
|
||||
|
||||
──────────────────────────────────────────────────────────────
|
||||
→ Continue with remaining spikes / Pivot approach / Abandon
|
||||
──────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**→ Continue with remaining spikes / Pivot approach / Abandon**
|
||||
</step>
|
||||
|
||||
<step name="update_conventions">
|
||||
@@ -435,9 +426,7 @@ gsd_run query commit "docs(spikes): update conventions" --files .planning/spikes
|
||||
|
||||
<step name="report">
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► SPIKE COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► SPIKE COMPLETE ✓
|
||||
|
||||
## Verdicts
|
||||
|
||||
@@ -456,7 +445,7 @@ gsd_run query commit "docs(spikes): update conventions" --files .planning/spikes
|
||||
{what to use, avoid, watch out for}
|
||||
```
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up
|
||||
|
||||
@@ -464,14 +453,14 @@ gsd_run query commit "docs(spikes): update conventions" --files .planning/spikes
|
||||
|
||||
`/gsd:spike --wrap-up`
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**Also available:**
|
||||
- `/gsd:spike` — spike more ideas (or run with no argument for frontier mode)
|
||||
- `/gsd:plan-phase` — start planning the real implementation
|
||||
- `/gsd:explore` — continue exploring the idea
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
</step>
|
||||
|
||||
</process>
|
||||
|
||||
@@ -42,12 +42,14 @@ Apply filter for LIST-OPEN (show only status=open or status=in_progress) or LIST
|
||||
Display:
|
||||
```
|
||||
Context Threads
|
||||
─────────────────────────────────────────────────────────
|
||||
|
||||
---
|
||||
slug status updated title
|
||||
auth-decision open 2026-04-09 OAuth vs Session tokens
|
||||
db-schema-v2 in_progress 2026-04-07 Connection pool sizing
|
||||
frontend-build-tools resolved 2026-04-01 Vite vs webpack
|
||||
─────────────────────────────────────────────────────────
|
||||
|
||||
---
|
||||
3 threads (2 open/in_progress, 1 resolved)
|
||||
```
|
||||
|
||||
@@ -96,7 +98,8 @@ When SUBCMD=status and SLUG is set (already sanitized):
|
||||
2. Read the file and display a summary:
|
||||
```
|
||||
Thread: {SLUG}
|
||||
─────────────────────────────────────
|
||||
|
||||
---
|
||||
Title: {title from frontmatter or # heading}
|
||||
Status: {status from frontmatter or ## Status heading}
|
||||
Updated: {updated from frontmatter}
|
||||
@@ -107,7 +110,8 @@ When SUBCMD=status and SLUG is set (already sanitized):
|
||||
|
||||
Next Steps:
|
||||
{content of ## Next Steps section}
|
||||
─────────────────────────────────────
|
||||
|
||||
---
|
||||
Resume with: /gsd:thread {SLUG}
|
||||
Close with: /gsd:thread close {SLUG}
|
||||
```
|
||||
|
||||
@@ -115,9 +115,7 @@ If "Update": continue to step 5.
|
||||
|
||||
Display:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► UI DESIGN CONTRACT — PHASE {N}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► UI DESIGN CONTRACT — PHASE {N}
|
||||
|
||||
◆ Spawning UI researcher... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
@@ -188,9 +186,7 @@ Display blocker details and options. Exit workflow.
|
||||
|
||||
Display:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► VERIFYING UI-SPEC
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► VERIFYING UI-SPEC
|
||||
|
||||
◆ Spawning UI checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
@@ -423,16 +419,14 @@ STATE coverage and REFERENCES those rows rather than restating the copy (de-dup)
|
||||
|
||||
Display:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► UI-SPEC READY ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► UI-SPEC READY ✓
|
||||
|
||||
**Phase {N}: {Name}** — UI design contract approved
|
||||
|
||||
Dimensions: 7/7 passed
|
||||
{If any FLAGs: "Recommendations: {N} (non-blocking)"}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
|
||||
|
||||
@@ -448,7 +442,7 @@ Dimensions: 7/7 passed
|
||||
|
||||
(or `/gsd:plan-phase {N}` to skip discussion)
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
```
|
||||
|
||||
## 11. Commit (if configured)
|
||||
|
||||
@@ -33,9 +33,7 @@ UI_AUDITOR_MODEL=$(gsd_run query resolve-model gsd-ui-auditor --raw)
|
||||
|
||||
Display banner:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► UI AUDIT — PHASE {N}: {name}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► UI AUDIT — PHASE {N}: {name}
|
||||
```
|
||||
|
||||
## 1. Detect Input State
|
||||
@@ -127,9 +125,7 @@ Agent(
|
||||
Display score summary:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► UI AUDIT COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► UI AUDIT COMPLETE ✓
|
||||
|
||||
**Phase {N}: {Name}** — Overall: {score}/24
|
||||
|
||||
@@ -149,7 +145,7 @@ Top fixes:
|
||||
|
||||
Full review: {path to UI-REVIEW.md}
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next
|
||||
|
||||
@@ -157,7 +153,7 @@ Full review: {path to UI-REVIEW.md}
|
||||
|
||||
- `/gsd:verify-work {N}` — UAT testing before phase completion
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
```
|
||||
|
||||
## Automated UI Verification (when Playwright-MCP is available)
|
||||
|
||||
@@ -13,9 +13,7 @@ affect the core planning pipeline.
|
||||
Display the stage banner:
|
||||
|
||||
```text
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► ULTRAPLAN PHASE ⚠ BETA
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► ULTRAPLAN PHASE ⚠ BETA
|
||||
Ultraplan is in research preview (Claude Code v2.1.91+).
|
||||
Use /gsd:plan-phase for stable local planning.
|
||||
```
|
||||
@@ -44,9 +42,7 @@ fi
|
||||
If the output is empty or unset, display the following error and exit:
|
||||
|
||||
```text
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ RUNTIME ERROR ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### RUNTIME ERROR
|
||||
|
||||
/gsd:ultraplan-phase requires Claude Code.
|
||||
ultraplan is not available in this runtime.
|
||||
@@ -159,9 +155,7 @@ Display the return-path instructions **before** triggering ultraplan so they are
|
||||
in the terminal scroll-back after ultraplan launches:
|
||||
|
||||
```text
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
WHEN THE PLAN IS READY — WHAT TO DO
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### WHEN THE PLAN IS READY — WHAT TO DO
|
||||
|
||||
When ◆ ultraplan ready appears in your terminal:
|
||||
|
||||
@@ -176,9 +170,7 @@ When ◆ ultraplan ready appears in your terminal:
|
||||
/gsd:import will run conflict detection, convert to GSD format,
|
||||
validate via plan-checker, update ROADMAP.md, and commit.
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Launching ultraplan for Phase {N}: {phase_name}...
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### Launching ultraplan for Phase {N}: {phase_name}...
|
||||
```
|
||||
|
||||
</step>
|
||||
|
||||
@@ -15,14 +15,11 @@ RESPONSE_LANGUAGE=$(gsd_run query config-get response_language --default "" 2>/d
|
||||
|
||||
**If `response_language` is set:** All user-facing questions, prompts, and explanations in this workflow MUST be presented in `{response_language}`. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated.
|
||||
|
||||
|
||||
<step name="banner" priority="first">
|
||||
Display the stage banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► UNDO
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► UNDO
|
||||
```
|
||||
</step>
|
||||
|
||||
@@ -70,7 +67,6 @@ Recent GSD commits:
|
||||
3. ghi9012 fix(02-03): correct validation logic
|
||||
```
|
||||
|
||||
|
||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
||||
Use AskUserQuestion to ask:
|
||||
- question: "Which commits to revert? Enter numbers (e.g., 1,3) or 'all'"
|
||||
@@ -237,9 +233,7 @@ If any revert fails (merge conflict or error):
|
||||
```
|
||||
3. Display:
|
||||
```
|
||||
╔══════════════════════════════════════════════════════════════╗
|
||||
║ ERROR ║
|
||||
╚══════════════════════════════════════════════════════════════╝
|
||||
### ERROR
|
||||
|
||||
Revert failed on commit ${HASH}.
|
||||
Likely cause: merge conflict with subsequent changes.
|
||||
@@ -271,9 +265,7 @@ git commit -m "revert: undo ${N} selected commits — ${REVERT_REASON}"
|
||||
Display the completion banner:
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► UNDO COMPLETE ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► UNDO COMPLETE ✓
|
||||
```
|
||||
|
||||
Show summary:
|
||||
@@ -284,7 +276,7 @@ Show summary:
|
||||
|
||||
Show next steps:
|
||||
```
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
|
||||
|
||||
@@ -294,13 +286,13 @@ Show next steps:
|
||||
|
||||
/gsd:progress
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
**Also available:**
|
||||
- `/gsd:execute-phase ${PHASE}` — re-execute if needed
|
||||
- `/gsd:undo --last 1` — undo the revert itself if something went wrong
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
```
|
||||
</step>
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ Read all files referenced by the invoking prompt's execution_context before star
|
||||
<process>
|
||||
**If `response_language` is configured:** All user-facing questions, prompts, and explanations in this workflow MUST be presented in that language. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated.
|
||||
|
||||
|
||||
<step name="get_installed_version">
|
||||
Detect the installed GSD version, scope, runtime, and config dir.
|
||||
|
||||
@@ -253,11 +252,12 @@ rm -f "$CHANGELOG_TMP"
|
||||
**Latest:** {LATEST_VERSION}
|
||||
|
||||
### What's New
|
||||
────────────────────────────────────────────────────────────
|
||||
|
||||
---
|
||||
|
||||
{CHANGELOG_PREVIEW}
|
||||
|
||||
────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
⚠️ **Note:** The installer performs a clean install of GSD folders:
|
||||
- `commands/gsd/` will be wiped and replaced
|
||||
@@ -277,7 +277,6 @@ Your custom files in other locations are preserved:
|
||||
If you've modified any GSD files directly, they'll be automatically backed up to `gsd-local-patches/` and can be reapplied with `/gsd:update --reapply` after the update.
|
||||
```
|
||||
|
||||
|
||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
|
||||
Use AskUserQuestion:
|
||||
- Question: "Proceed with update?"
|
||||
@@ -480,9 +479,7 @@ The SessionStart hook (`gsd-check-update.js`) writes to the detected runtime's c
|
||||
Format completion message (changelog was already shown in confirmation step):
|
||||
|
||||
```
|
||||
╔═══════════════════════════════════════════════════════════╗
|
||||
║ GSD Updated: v1.5.10 → v1.5.15 ║
|
||||
╚═══════════════════════════════════════════════════════════╝
|
||||
### GSD Updated: v1.5.10 → v1.5.15
|
||||
|
||||
⚠️ Restart your runtime to pick up the new commands.
|
||||
|
||||
@@ -490,7 +487,6 @@ Format completion message (changelog was already shown in confirmation step):
|
||||
```
|
||||
</step>
|
||||
|
||||
|
||||
<step name="restore_custom_files">
|
||||
`backup_custom_files` copied user-added files into `gsd-user-files-backup/`
|
||||
before the wipe. Offer to put them back — now, against the release that was
|
||||
|
||||
@@ -649,9 +649,12 @@ Parse the JSON output. For the CURRENT PHASE ONLY, surface:
|
||||
If any are found, display:
|
||||
```
|
||||
Phase {N} Artifact Check
|
||||
─────────────────────────────────────────────────
|
||||
|
||||
---
|
||||
|
||||
{list each item with status and file path}
|
||||
─────────────────────────────────────────────────
|
||||
|
||||
---
|
||||
These items are open. Proceed anyway? [Y/n]
|
||||
```
|
||||
|
||||
@@ -687,9 +690,7 @@ Diagnosis runs automatically - no user prompt. Parallel agents investigate simul
|
||||
|
||||
Display:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► PLANNING FIXES
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► PLANNING FIXES
|
||||
|
||||
◆ Spawning planner for gap closure... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
@@ -754,9 +755,7 @@ On return:
|
||||
|
||||
Display:
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► VERIFYING FIX PLANS
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► VERIFYING FIX PLANS
|
||||
|
||||
◆ Spawning plan checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
@@ -860,9 +859,7 @@ Wait for user response.
|
||||
**Present completion and next steps:**
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GSD ► FIXES READY ✓
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
### GSD ► FIXES READY ✓
|
||||
|
||||
**Phase {X}: {Name}** — {N} gap(s) diagnosed, {M} fix plan(s) created
|
||||
|
||||
@@ -873,7 +870,7 @@ Wait for user response.
|
||||
|
||||
Plans verified and ready for execution.
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
|
||||
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
|
||||
|
||||
@@ -881,7 +878,7 @@ Plans verified and ready for execution.
|
||||
|
||||
`/clear` then `/gsd:execute-phase {phase} --gaps-only`
|
||||
|
||||
───────────────────────────────────────────────────────────────
|
||||
---
|
||||
```
|
||||
</step>
|
||||
|
||||
|
||||
@@ -86,12 +86,14 @@ For each directory found:
|
||||
Display format:
|
||||
```
|
||||
Quick Tasks
|
||||
────────────────────────────────────────────────────────────
|
||||
|
||||
---
|
||||
slug date status
|
||||
backup-s3-policy 2026-04-10 in-progress
|
||||
auth-token-refresh-fix 2026-04-09 complete ✓
|
||||
update-node-deps 2026-04-08 abandoned? (>7 days, no summary)
|
||||
────────────────────────────────────────────────────────────
|
||||
|
||||
---
|
||||
3 tasks (1 complete, 2 incomplete/in-progress)
|
||||
```
|
||||
|
||||
@@ -113,12 +115,14 @@ If no directory found, print `No quick task found with slug: {SLUG}` and stop.
|
||||
Read PLAN.md and SUMMARY.md (if exists) for the given slug. Display:
|
||||
```
|
||||
Quick Task: {slug}
|
||||
─────────────────────────────────────
|
||||
|
||||
---
|
||||
Plan file: .planning/quick/{dir}/PLAN.md
|
||||
Status: {status from SUMMARY.md frontmatter, or "no summary yet"}
|
||||
Description: {first non-empty line from PLAN.md after frontmatter}
|
||||
Last action: {last meaningful line of SUMMARY.md, or "none"}
|
||||
─────────────────────────────────────
|
||||
|
||||
---
|
||||
Resume with: /gsd-quick resume {slug}
|
||||
```
|
||||
|
||||
|
||||
@@ -1330,11 +1330,8 @@ function auditOpenArtifacts(cwd: string): AuditResult {
|
||||
function formatAuditReport(auditResult: AuditResult): string {
|
||||
const { counts, items, has_open_items, acknowledged } = auditResult;
|
||||
const lines: string[] = [];
|
||||
const hr = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
|
||||
|
||||
lines.push(hr);
|
||||
lines.push(' Milestone Close: Open Artifact Audit');
|
||||
lines.push(hr);
|
||||
lines.push('### Milestone Close: Open Artifact Audit');
|
||||
|
||||
// WARNING 3 (#3458 follow-up review): the acknowledged tally previously
|
||||
// existed only in `--json` output — the human report could not tell
|
||||
@@ -1343,12 +1340,12 @@ function formatAuditReport(auditResult: AuditResult): string {
|
||||
if (!has_open_items) {
|
||||
lines.push('');
|
||||
if (acknowledged.total > 0) {
|
||||
lines.push(` All artifact types clear (${acknowledged.total} previously acknowledged item${acknowledged.total !== 1 ? 's' : ''} still suppressed).`);
|
||||
lines.push(`All artifact types clear (${acknowledged.total} previously acknowledged item${acknowledged.total !== 1 ? 's' : ''} still suppressed).`);
|
||||
} else {
|
||||
lines.push(' All artifact types clear. Safe to proceed.');
|
||||
lines.push('All artifact types clear. Safe to proceed.');
|
||||
}
|
||||
lines.push('');
|
||||
lines.push(hr);
|
||||
lines.push('---');
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
@@ -1459,12 +1456,12 @@ function formatAuditReport(auditResult: AuditResult): string {
|
||||
}
|
||||
|
||||
lines.push('');
|
||||
lines.push(hr);
|
||||
lines.push(` ${counts.total} item${counts.total !== 1 ? 's' : ''} require decisions before close.`);
|
||||
lines.push('---');
|
||||
lines.push('');
|
||||
lines.push(`**${counts.total} item${counts.total !== 1 ? 's' : ''} require decisions before close.**`);
|
||||
if (acknowledged.total > 0) {
|
||||
lines.push(` ${acknowledged.total} previously acknowledged item${acknowledged.total !== 1 ? 's' : ''} also suppressed above the ${counts.total} open item${counts.total !== 1 ? 's' : ''}.`);
|
||||
lines.push(`${acknowledged.total} previously acknowledged item${acknowledged.total !== 1 ? 's' : ''} also suppressed above the ${counts.total} open item${counts.total !== 1 ? 's' : ''}.`);
|
||||
}
|
||||
lines.push(hr);
|
||||
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
@@ -890,7 +890,6 @@ function cmdTddReviewCheckpoint(projectDir: string, args: string[], raw: boolean
|
||||
const violations = rows.filter(r => r.status === 'FAIL').length;
|
||||
|
||||
// Build review table
|
||||
const sep = '━'.repeat(53);
|
||||
const tableHeader = '| Plan | RED | GREEN | REFACTOR | Status |';
|
||||
const tableDivider = '|------|-----|-------|----------|--------|';
|
||||
const tableRows = rows.map(r =>
|
||||
@@ -898,9 +897,7 @@ function cmdTddReviewCheckpoint(projectDir: string, args: string[], raw: boolean
|
||||
);
|
||||
|
||||
let table = [
|
||||
sep,
|
||||
` TDD REVIEW — Phase ${phase}`,
|
||||
sep,
|
||||
`### TDD REVIEW — Phase ${phase}`,
|
||||
'',
|
||||
`TDD Plans: ${tddPlanFiles.length} | Gate violations: ${violations}`,
|
||||
'',
|
||||
|
||||
63
src/uat.cts
63
src/uat.cts
@@ -421,8 +421,6 @@ interface CheckpointFrame {
|
||||
direction?: 'rtl';
|
||||
}
|
||||
|
||||
const CHECKPOINT_BOX_WIDTH = 64; // total column width of the ╔══...╗ border, borders stay byte-identical
|
||||
|
||||
const CHECKPOINT_FRAMES: Record<string, CheckpointFrame> = {
|
||||
english: {
|
||||
banner: 'CHECKPOINT: Verification Required',
|
||||
@@ -529,56 +527,6 @@ function resolveCheckpointFrame(responseLanguage: string | undefined): Checkpoin
|
||||
return (key && CHECKPOINT_FRAMES[key]) || CHECKPOINT_FRAMES.english;
|
||||
}
|
||||
|
||||
// Approximate terminal-cell width. East Asian Width W/F code points occupy two
|
||||
// cells, while Unicode combining marks occupy no additional cell beyond their
|
||||
// base character. Counting only W/F ranges is insufficient for scripts such as
|
||||
// Devanagari: Hindi vowel signs and viramas are combining marks, and treating
|
||||
// each as a full cell visibly shifts the checkpoint box's right border.
|
||||
function isWideCodePoint(codePoint: number): boolean {
|
||||
return (
|
||||
(codePoint >= 0x1100 && codePoint <= 0x115f) || // Hangul Jamo
|
||||
codePoint === 0x2329 || codePoint === 0x232a ||
|
||||
(codePoint >= 0x2e80 && codePoint <= 0x303e) || // CJK Radicals .. CJK Symbols and Punctuation
|
||||
(codePoint >= 0x3041 && codePoint <= 0x33ff) || // Hiragana .. CJK Compatibility
|
||||
(codePoint >= 0x3400 && codePoint <= 0x4dbf) || // CJK Unified Ideographs Extension A
|
||||
(codePoint >= 0x4e00 && codePoint <= 0x9fff) || // CJK Unified Ideographs
|
||||
(codePoint >= 0xa000 && codePoint <= 0xa4cf) || // Yi Syllables
|
||||
(codePoint >= 0xac00 && codePoint <= 0xd7a3) || // Hangul Syllables
|
||||
(codePoint >= 0xf900 && codePoint <= 0xfaff) || // CJK Compatibility Ideographs
|
||||
(codePoint >= 0xfe30 && codePoint <= 0xfe4f) || // CJK Compatibility Forms
|
||||
(codePoint >= 0xff00 && codePoint <= 0xff60) || // Fullwidth Forms
|
||||
(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
|
||||
(codePoint >= 0x20000 && codePoint <= 0x3fffd) // CJK Unified Ideographs Extension B+ / supplementary
|
||||
);
|
||||
}
|
||||
|
||||
// Non-spacing/enclosing marks and format controls occupy zero terminal cells.
|
||||
// Spacing combining marks (General_Category=Mc), such as Devanagari vowel
|
||||
// signs, still advance the cursor and must contribute one column.
|
||||
const ZERO_WIDTH_MARK_RE = /\p{gc=Mn}|\p{gc=Me}|\p{gc=Cf}/u;
|
||||
|
||||
// Iterates by Unicode code point (not UTF-16 code unit) so astral characters
|
||||
// are measured once, not as two surrogate units.
|
||||
function displayWidth(text: string): number {
|
||||
let width = 0;
|
||||
for (const ch of text) {
|
||||
if (ZERO_WIDTH_MARK_RE.test(ch)) continue;
|
||||
width += isWideCodePoint(ch.codePointAt(0) as number) ? 2 : 1;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
// Pads `text` into a `║ text… ║` line matching CHECKPOINT_BOX_WIDTH. Content
|
||||
// that overflows the box (a longer translated string) is left unpadded rather
|
||||
// than truncated — a slightly ragged border beats losing text.
|
||||
function checkpointBoxLine(text: string): string {
|
||||
const innerWidth = CHECKPOINT_BOX_WIDTH - 2;
|
||||
const content = ` ${text}`;
|
||||
const padLength = innerWidth - displayWidth(content);
|
||||
const padded = padLength > 0 ? content + ' '.repeat(padLength) : content;
|
||||
return `║${padded}║`;
|
||||
}
|
||||
|
||||
const RTL_ISOLATE = '\u2067';
|
||||
const POP_DIRECTIONAL_ISOLATE = '\u2069';
|
||||
|
||||
@@ -593,17 +541,15 @@ function buildCheckpoint(currentTest: { number: number; name: string; expected:
|
||||
const banner = isolateCheckpointFrameText(frame.banner, frame);
|
||||
const instruction = isolateCheckpointFrameText(frame.instruction, frame);
|
||||
return [
|
||||
'╔══════════════════════════════════════════════════════════════╗',
|
||||
checkpointBoxLine(banner),
|
||||
'╚══════════════════════════════════════════════════════════════╝',
|
||||
`### ${banner}`,
|
||||
'',
|
||||
`**Test ${currentTest.number}: ${currentTest.name}**`,
|
||||
'',
|
||||
currentTest.expected,
|
||||
'',
|
||||
'──────────────────────────────────────────────────────────────',
|
||||
instruction,
|
||||
'──────────────────────────────────────────────────────────────',
|
||||
'---',
|
||||
'',
|
||||
`**${instruction}**`,
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
@@ -1676,7 +1622,6 @@ export = {
|
||||
CHECKPOINT_FRAMES,
|
||||
CHECKPOINT_LANGUAGE_ALIASES,
|
||||
resolveCheckpointFrame,
|
||||
checkpointBoxLine,
|
||||
parseDeferredItems,
|
||||
parseDeferredItemsWithStatus,
|
||||
acknowledgeDeferredItem,
|
||||
|
||||
@@ -238,8 +238,8 @@ describe('audit cutover: output shape equivalence', () => {
|
||||
`audit-open must succeed. stderr: ${result.error}`);
|
||||
const lines = result.output.split('\n').map(l => l.trim()).filter(Boolean);
|
||||
assert.ok(
|
||||
lines.includes('Milestone Close: Open Artifact Audit'),
|
||||
`report title must appear as a standalone line; got: ${JSON.stringify(lines.slice(0, 5))}`,
|
||||
lines.includes('### Milestone Close: Open Artifact Audit'),
|
||||
`report title must appear as a standalone Markdown heading line; got: ${JSON.stringify(lines.slice(0, 5))}`,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -481,7 +481,7 @@ describe('audit-open — does not crash with ReferenceError (#2659)', () => {
|
||||
* 2. Even after switching to `core.output(formatted, raw)`, the human-readable
|
||||
* branch JSON-stringifies the formatted string because `core.output` only
|
||||
* bypasses JSON encoding when called as `core.output(null, true, rawValue)`.
|
||||
* Result: stdout contains `"━━━…\n Milestone Close: …\n…"` (a JSON string
|
||||
* Result: stdout contains `"### Milestone Close: …\n…"` (a JSON string
|
||||
* literal) instead of the rendered report.
|
||||
*
|
||||
* The shape assertions below catch both regressions structurally — never via
|
||||
@@ -640,10 +640,10 @@ describe('audit-open — output shape (#2911)', () => {
|
||||
|
||||
const lines = result.output.split('\n').map(l => l.trim()).filter(Boolean);
|
||||
|
||||
// The first non-empty line must be the divider character row, *not* a
|
||||
// JSON-encoded string starting with a quote. If core.output JSON-stringified
|
||||
// The first non-empty line must be the rendered Markdown heading row, *not*
|
||||
// a JSON-encoded string starting with a quote. If core.output JSON-stringified
|
||||
// the formatted report, the entire payload sits on one line wrapped in
|
||||
// double quotes ("━━━…\n…").
|
||||
// double quotes ("### Milestone Close: …\n…").
|
||||
assert.ok(
|
||||
!result.output.startsWith('"'),
|
||||
'text-mode stdout must not begin with a JSON quote (would mean the report was JSON.stringified)'
|
||||
@@ -655,8 +655,8 @@ describe('audit-open — output shape (#2911)', () => {
|
||||
|
||||
// Section headers from formatAuditReport that must appear as standalone lines.
|
||||
assert.ok(
|
||||
lines.includes('Milestone Close: Open Artifact Audit'),
|
||||
`expected report title as a standalone line; got lines: ${JSON.stringify(lines.slice(0, 5))}`
|
||||
lines.includes('### Milestone Close: Open Artifact Audit'),
|
||||
`expected report title as a standalone Markdown heading line; got lines: ${JSON.stringify(lines.slice(0, 5))}`
|
||||
);
|
||||
assert.ok(
|
||||
lines.includes('All artifact types clear. Safe to proceed.'),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": 1,
|
||||
"paths": {
|
||||
"execute-plan.md": "#3370: the Pattern A dispatch prompt spec gained the gate-semantics clause (gate=\"blocking\" (the default) is auto-approvable in auto-mode per the executor's own checkpoint protocol, gate=\"blocking-human\" always surfaces to a human; add no instruction overriding that protocol), closing the identically-shaped dispatch-time gap on the single-plan path named in the issue. Growth ~248 bytes (38913 -> 39161, still under the DEFAULT 40 KiB ceiling). Supersedes the spent #2652 fragment (merged into next), which also named execute-plan.md and would otherwise double-ack the same path. #3659 appends --mode \"$ISOLATION\" to the #2649 pre-dispatch base-check and corrects the baseRef restore-advice to the orchestrator/harness split (+154B; the harness does not read baseRef, #48). Deliberate growth. — #3299 append: the tracer feedback gate's interactive branch, which read `Interactive: STOP -> return a checkpoint:human-verify` and keyed on auto-mode alone, now branches on HUMAN_VERIFY_MODE — under the documented `end-of-phase` default an automated-only tracer `<verify>` is re-run and continues to expansion with no checkpoint. Growth is 796 bytes (39315 -> 40111, still under the DEFAULT_CAP of 40960 LF bytes, 849 bytes of headroom; the earlier 39161 -> 39957 / 1003 figures were measured before the 2026-08-22 merge of next and are superseded): that branch plus a `HUMAN_VERIFY_MODE=` read alongside the existing RUNTIME/USE_WORKTREES reads in parse_segments. The read carries an explicit `--default end-of-phase` because `workflow.human_verify_mode` is absent from SCHEMA_DEFAULTS (src/config.cts): a bare config-get exits non-zero with `Key not found` on any project whose config.json predates #3309, which is every pre-existing project and the reporter's exact config. The three carve-outs (`<human-check>`, `gate=\"blocking-human\"`, `mid-flight`) are stated inline here rather than delegated to gsd-core/references/dispatch-isolation-gate.md, because this file is the inline dispatch path used for step-by-step / non-Claude-Code execution, where agents/gsd-executor.md is never loaded. This entry carries #3370's own reason forward verbatim above rather than replacing it — that growth is in the base and still needs its account. The blocking-human STOP is evaluated BEFORE the auto-mode branch here, matching golden rule 6 in checkpoints.md — the earlier ordering let an autonomous run continue past a blocking-human tracer."
|
||||
"execute-plan.md": "#3370: the Pattern A dispatch prompt spec gained the gate-semantics clause (gate=\"blocking\" (the default) is auto-approvable in auto-mode per the executor's own checkpoint protocol, gate=\"blocking-human\" always surfaces to a human; add no instruction overriding that protocol), closing the identically-shaped dispatch-time gap on the single-plan path named in the issue. Growth ~248 bytes (38913 -> 39161, still under the DEFAULT 40 KiB ceiling). Supersedes the spent #2652 fragment (merged into next), which also named execute-plan.md and would otherwise double-ack the same path. #3659 appends --mode \"$ISOLATION\" to the #2649 pre-dispatch base-check and corrects the baseRef restore-advice to the orchestrator/harness split (+154B; the harness does not read baseRef, #48). Deliberate growth. — #3299 append: the tracer feedback gate's interactive branch, which read `Interactive: STOP -> return a checkpoint:human-verify` and keyed on auto-mode alone, now branches on HUMAN_VERIFY_MODE — under the documented `end-of-phase` default an automated-only tracer `<verify>` is re-run and continues to expansion with no checkpoint. Growth is 796 bytes (39315 -> 40111, still under the DEFAULT_CAP of 40960 LF bytes, 849 bytes of headroom; the earlier 39161 -> 39957 / 1003 figures were measured before the 2026-08-22 merge of next and are superseded): that branch plus a `HUMAN_VERIFY_MODE=` read alongside the existing RUNTIME/USE_WORKTREES reads in parse_segments. The read carries an explicit `--default end-of-phase` because `workflow.human_verify_mode` is absent from SCHEMA_DEFAULTS (src/config.cts): a bare config-get exits non-zero with `Key not found` on any project whose config.json predates #3309, which is every pre-existing project and the reporter's exact config. The three carve-outs (`<human-check>`, `gate=\"blocking-human\"`, `mid-flight`) are stated inline here rather than delegated to gsd-core/references/dispatch-isolation-gate.md, because this file is the inline dispatch path used for step-by-step / non-Claude-Code execution, where agents/gsd-executor.md is never loaded. This entry carries #3370's own reason forward verbatim above rather than replacing it — that growth is in the base and still needs its account. The blocking-human STOP is evaluated BEFORE the auto-mode branch here, matching golden rule 6 in checkpoints.md — the earlier ordering let an autonomous run continue past a blocking-human tracer. — #3028 append: the checkpoint_protocol step's one-line display spec described the old drawn box (`CHECKPOINT: [Type]` box … `YOUR ACTION: [signal]`). Under #3028 a checkpoint is a `### CHECKPOINT: [Type]` heading, a `---` break and a bolded action prompt, so the spec now reads `### CHECKPOINT: [Type]` heading → Progress {X}/{Y} → Task name → type-specific content → `---` → `**YOUR ACTION: [signal]**`. Growth is 22 bytes (40111 -> 40133, still under the DEFAULT_CAP of 40960 LF bytes, 827 bytes of headroom) — the naming of the two new Markdown tokens and the heading marker. Appended here rather than filed as a new fragment because a growth ack keys on the BARE FILENAME and #3370 already declares `execute-plan.md`; two sources naming one path is a hard duplicate-key error, so this follows the same supersede-by-append route #3370 itself took for the spent #2652 fragment. Every other file this change touches SHRANK, which needs no acknowledgment."
|
||||
}
|
||||
}
|
||||
|
||||
297
tests/responsive-separators.test.cjs
Normal file
297
tests/responsive-separators.test.cjs
Normal file
@@ -0,0 +1,297 @@
|
||||
// Guards the responsive-separator convention defined in
|
||||
// gsd-core/references/ui-brand.md § "Separators and Banners".
|
||||
//
|
||||
// Shipped content (gsd-core/, agents/, commands/) is runtime-loaded text: an
|
||||
// agent reads it and emits the banners it describes. A fixed-width run of
|
||||
// box-drawing characters is ordinary text to a Markdown-rendering host, so in a
|
||||
// narrower pane it wraps and leaves orphan glyphs on a second line, coming apart
|
||||
// from the heading it was meant to frame. Markdown headings and thematic breaks
|
||||
// adapt to the available width instead.
|
||||
//
|
||||
// Enhancement for https://github.com/open-gsd/gsd-core/issues/3028.
|
||||
//
|
||||
// SCOPE - deliberately narrow, see the negative-space cases below:
|
||||
// * A "rule line" is a line whose trimmed content is composed ENTIRELY of
|
||||
// U+2500, U+2501 or U+2550, three or more of them. Tree glyphs, boxed-table
|
||||
// rows, progress bars and inline art all carry other characters and are
|
||||
// never flagged.
|
||||
// * A "panel character" is one of U+2554 U+2557 U+255A U+255D U+2551 anywhere
|
||||
// on a line.
|
||||
// * docs/ is NOT scanned: a documentation page may legitimately quote the old
|
||||
// form while explaining the change. src/ is not scanned either - its
|
||||
// section-divider comments are read in an editor, never emitted.
|
||||
|
||||
const { describe, test, afterEach } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fc = require('fast-check');
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const REPO_ROOT = path.join(__dirname, '..');
|
||||
|
||||
// Runtime-loaded text: an agent reads these and emits what they describe.
|
||||
const SHIPPED_ROOTS = ['gsd-core', 'agents', 'commands'];
|
||||
|
||||
// A shorter run than MIN_RULE_RUN is a glyph fragment, not a rule.
|
||||
const MIN_RULE_RUN = 3;
|
||||
const RULE_LINE_RE = /^[─━═]{3,}$/;
|
||||
|
||||
// Double-line box vocabulary only. Single-line characters (└ ├ │ ...) are
|
||||
// deliberately absent: they are legitimate tree glyphs, not panel borders.
|
||||
const PANEL_CHAR_RE = /[╔╗╚╝║╠╣╦╩╬]/;
|
||||
|
||||
const HEAVY = '━';
|
||||
const LIGHT = '─';
|
||||
const DOUBLE = '═';
|
||||
|
||||
// `trim()` also strips a trailing \r, so CRLF files are handled without a
|
||||
// separate newline split dialect.
|
||||
function isFixedWidthRuleLine(line) {
|
||||
return RULE_LINE_RE.test(line.trim());
|
||||
}
|
||||
|
||||
function hasPanelCharacter(line) {
|
||||
return PANEL_CHAR_RE.test(line);
|
||||
}
|
||||
|
||||
function findMarkdownFiles(dir) {
|
||||
const out = [];
|
||||
let entries;
|
||||
try {
|
||||
entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') return out;
|
||||
throw err;
|
||||
}
|
||||
for (const entry of entries) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
out.push(...findMarkdownFiles(full));
|
||||
} else if (entry.isFile() && entry.name.endsWith('.md')) {
|
||||
out.push(full);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function shippedMarkdownFiles() {
|
||||
const files = [];
|
||||
for (const root of SHIPPED_ROOTS) {
|
||||
files.push(...findMarkdownFiles(path.join(REPO_ROOT, root)));
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
function scan(files, predicate) {
|
||||
const violations = [];
|
||||
for (const filePath of files) {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
const lines = content.split(/\r?\n/);
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if (predicate(lines[i])) {
|
||||
violations.push(
|
||||
`${path.relative(REPO_ROOT, filePath)}:${i + 1}: ${lines[i].trim().slice(0, 40)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return violations;
|
||||
}
|
||||
|
||||
const REMEDY =
|
||||
'\n\nPer gsd-core/references/ui-brand.md "Separators and Banners":\n' +
|
||||
' - a stage banner is an ATX heading with no rule lines;\n' +
|
||||
' - a panel becomes a heading followed by its rows as plain lines;\n' +
|
||||
' - a divider between sections is --- with a blank line above and below.\n' +
|
||||
'A fixed-width run wraps in a narrow pane and comes apart from its heading.\n' +
|
||||
'See https://github.com/open-gsd/gsd-core/issues/3028';
|
||||
|
||||
describe('responsive-separators', () => {
|
||||
const tmpDirs = [];
|
||||
afterEach(() => {
|
||||
while (tmpDirs.length) cleanup(tmpDirs.pop());
|
||||
});
|
||||
|
||||
function tempFile(name, content) {
|
||||
const dir = createTempDir('responsive-separators');
|
||||
tmpDirs.push(dir);
|
||||
const filePath = path.join(dir, name);
|
||||
fs.writeFileSync(filePath, content, 'utf-8');
|
||||
return filePath;
|
||||
}
|
||||
|
||||
test('the shipped-content walk actually finds files', () => {
|
||||
const files = shippedMarkdownFiles();
|
||||
// Without this the two repo-wide scans below would pass vacuously if the
|
||||
// roots were ever renamed out from under them.
|
||||
assert.ok(
|
||||
files.length > 100,
|
||||
`expected the shipped roots (${SHIPPED_ROOTS.join(', ')}) to hold markdown, found ${files.length}`
|
||||
);
|
||||
for (const root of SHIPPED_ROOTS) {
|
||||
assert.ok(
|
||||
files.some((f) => f.startsWith(path.join(REPO_ROOT, root) + path.sep)),
|
||||
`no markdown found under shipped root "${root}"`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('no shipped content emits a fixed-width rule line', () => {
|
||||
const violations = scan(shippedMarkdownFiles(), isFixedWidthRuleLine);
|
||||
assert.deepStrictEqual(
|
||||
violations,
|
||||
[],
|
||||
'Fixed-width box-drawing rule lines found in shipped content:\n' +
|
||||
violations.map((v) => ` - ${v}`).join('\n') +
|
||||
REMEDY
|
||||
);
|
||||
});
|
||||
|
||||
test('no shipped content emits box-panel characters', () => {
|
||||
const violations = scan(shippedMarkdownFiles(), hasPanelCharacter);
|
||||
assert.deepStrictEqual(
|
||||
violations,
|
||||
[],
|
||||
'Box-panel characters found in shipped content:\n' +
|
||||
violations.map((v) => ` - ${v}`).join('\n') +
|
||||
REMEDY
|
||||
);
|
||||
});
|
||||
|
||||
test(`run of ${MIN_RULE_RUN - 1} is not a rule (limit-1)`, () => {
|
||||
assert.equal(isFixedWidthRuleLine(HEAVY.repeat(MIN_RULE_RUN - 1)), false);
|
||||
assert.equal(isFixedWidthRuleLine(LIGHT.repeat(MIN_RULE_RUN - 1)), false);
|
||||
assert.equal(isFixedWidthRuleLine(DOUBLE.repeat(MIN_RULE_RUN - 1)), false);
|
||||
});
|
||||
|
||||
test(`run of ${MIN_RULE_RUN} is a rule (limit)`, () => {
|
||||
assert.equal(isFixedWidthRuleLine(HEAVY.repeat(MIN_RULE_RUN)), true);
|
||||
assert.equal(isFixedWidthRuleLine(LIGHT.repeat(MIN_RULE_RUN)), true);
|
||||
assert.equal(isFixedWidthRuleLine(DOUBLE.repeat(MIN_RULE_RUN)), true);
|
||||
});
|
||||
|
||||
test(`run of ${MIN_RULE_RUN + 1} is a rule (limit+1)`, () => {
|
||||
assert.equal(isFixedWidthRuleLine(HEAVY.repeat(MIN_RULE_RUN + 1)), true);
|
||||
});
|
||||
|
||||
test('run of 1 is not a rule', () => {
|
||||
assert.equal(isFixedWidthRuleLine(HEAVY), false);
|
||||
});
|
||||
|
||||
test('lines that merely contain box-drawing glyphs are not rules', () => {
|
||||
const notRules = [
|
||||
'└── file.md',
|
||||
'├── dir/',
|
||||
'├────┼────┤',
|
||||
'Progress: ████░░ 80%',
|
||||
'| --- | --- |',
|
||||
'---',
|
||||
'### GSD ► EXECUTING WAVE 8',
|
||||
'',
|
||||
' \t ',
|
||||
`a${HEAVY.repeat(3)}`,
|
||||
`${HEAVY.repeat(3)}a`,
|
||||
];
|
||||
for (const line of notRules) {
|
||||
assert.equal(
|
||||
isFixedWidthRuleLine(line),
|
||||
false,
|
||||
`should not be a rule: ${JSON.stringify(line)}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('an indented rule line is still a rule', () => {
|
||||
assert.equal(isFixedWidthRuleLine(` ${HEAVY.repeat(4)} `), true);
|
||||
assert.equal(isFixedWidthRuleLine(`\t${LIGHT.repeat(3)}\t`), true);
|
||||
});
|
||||
|
||||
test('mixed rule characters still count as a rule', () => {
|
||||
assert.equal(isFixedWidthRuleLine(`${LIGHT}${LIGHT}${LIGHT}${HEAVY}${HEAVY}${DOUBLE}`), true);
|
||||
});
|
||||
|
||||
test('panel characters are detected anywhere on the line', () => {
|
||||
assert.equal(hasPanelCharacter('╔══╗'), true);
|
||||
assert.equal(hasPanelCharacter('║ CHECKPOINT: Verification Required ║'), true);
|
||||
assert.equal(hasPanelCharacter('╚══╝'), true);
|
||||
assert.equal(hasPanelCharacter('╠══╣'), true);
|
||||
assert.equal(hasPanelCharacter('├──┤'), false);
|
||||
assert.equal(hasPanelCharacter('│ tree'), false);
|
||||
assert.equal(hasPanelCharacter('### CHECKPOINT: Verification Required'), false);
|
||||
assert.equal(hasPanelCharacter('└── file.md'), false);
|
||||
});
|
||||
|
||||
test('the scan flags a heavy fixed-width rule line, with path and line number', () => {
|
||||
const filePath = tempFile(
|
||||
'banner.md',
|
||||
['Display banner:', '', HEAVY.repeat(53), ' GSD ► PLANNING', ''].join('\n')
|
||||
);
|
||||
const violations = scan([filePath], isFixedWidthRuleLine);
|
||||
assert.equal(violations.length, 1);
|
||||
assert.match(violations[0], /banner\.md:3: /);
|
||||
});
|
||||
|
||||
test('the scan flags box-panel characters', () => {
|
||||
const filePath = tempFile(
|
||||
'panel.md',
|
||||
['╔══╗', '║ ERROR ║', '╚══╝'].join('\n')
|
||||
);
|
||||
const violations = scan([filePath], hasPanelCharacter);
|
||||
assert.equal(violations.length, 3);
|
||||
});
|
||||
|
||||
test('CRLF files are scanned identically to LF files', () => {
|
||||
const crlf = tempFile(
|
||||
'crlf.md',
|
||||
['Display:', HEAVY.repeat(53), '└── file.md', ''].join('\r\n')
|
||||
);
|
||||
const violations = scan([crlf], isFixedWidthRuleLine);
|
||||
assert.equal(violations.length, 1, 'the \\r must not defeat the rule-line trim');
|
||||
assert.match(violations[0], /crlf\.md:2: /);
|
||||
});
|
||||
|
||||
test('a file with no separators produces no violations', () => {
|
||||
const filePath = tempFile(
|
||||
'clean.md',
|
||||
['# Title', '', '### GSD ► PLANNING', '', '---', '', 'Body.'].join('\n')
|
||||
);
|
||||
assert.deepStrictEqual(scan([filePath], isFixedWidthRuleLine), []);
|
||||
assert.deepStrictEqual(scan([filePath], hasPanelCharacter), []);
|
||||
});
|
||||
|
||||
test('an empty shipped file produces no violations', () => {
|
||||
const filePath = tempFile('empty.md', '');
|
||||
assert.deepStrictEqual(scan([filePath], isFixedWidthRuleLine), []);
|
||||
assert.deepStrictEqual(scan([filePath], hasPanelCharacter), []);
|
||||
});
|
||||
|
||||
const RULE_CHAR = fc.constantFrom(LIGHT, HEAVY, DOUBLE);
|
||||
|
||||
test(`property: any pure run of ${MIN_RULE_RUN}+ rule characters is flagged`, () => {
|
||||
fc.assert(
|
||||
fc.property(
|
||||
fc.array(RULE_CHAR, { minLength: MIN_RULE_RUN, maxLength: 120 }),
|
||||
fc.constantFrom('', ' ', ' ', '\t', ' \t '),
|
||||
fc.constantFrom('', ' ', ' ', '\t', '\r'),
|
||||
(chars, lead, trail) => isFixedWidthRuleLine(`${lead}${chars.join('')}${trail}`)
|
||||
),
|
||||
{ seed: 3028, numRuns: 300 }
|
||||
);
|
||||
});
|
||||
|
||||
test('property: a line carrying any non-rule, non-whitespace character is never flagged', () => {
|
||||
fc.assert(
|
||||
fc.property(
|
||||
fc.array(RULE_CHAR, { minLength: 0, maxLength: 60 }),
|
||||
fc
|
||||
.string({ minLength: 1, maxLength: 12 })
|
||||
.filter((s) => /\S/.test(s) && !/^[─━═\s]*$/.test(s)),
|
||||
fc.array(RULE_CHAR, { minLength: 0, maxLength: 60 }),
|
||||
(before, intruder, after) =>
|
||||
isFixedWidthRuleLine(`${before.join('')}${intruder}${after.join('')}`) === false
|
||||
),
|
||||
{ seed: 3028, numRuns: 300 }
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -15,7 +15,6 @@ const {
|
||||
CHECKPOINT_FRAMES,
|
||||
CHECKPOINT_LANGUAGE_ALIASES,
|
||||
resolveCheckpointFrame,
|
||||
checkpointBoxLine,
|
||||
parseDeferredItems,
|
||||
} = require('../gsd-core/bin/lib/uat.cjs');
|
||||
|
||||
@@ -932,12 +931,12 @@ describe('uat render-checkpoint', () => {
|
||||
|
||||
assert.ok(japanese.includes('チェックポイント'));
|
||||
assert.ok(japanese.includes('`pass`'));
|
||||
// Structural lines (borders, separators, Test N heading, expected content) are untouched.
|
||||
assert.ok(japanese.includes('╔══════════════════════════════════════════════════════════════╗'));
|
||||
assert.ok(japanese.includes('╚══════════════════════════════════════════════════════════════╝'));
|
||||
assert.ok(japanese.includes('──────────────────────────────────────────────────────────────'));
|
||||
// Structural lines (heading marker, separator, Test N heading, expected content) are untouched.
|
||||
assert.ok(japanese.includes('### チェックポイント: 検証が必要です'));
|
||||
assert.ok(japanese.includes('---'));
|
||||
assert.ok(japanese.includes('**Test 1: Sample**'));
|
||||
assert.ok(japanese.includes('Something happens.'));
|
||||
assert.ok(!/[╔╗╚╝║]/.test(japanese), 'the box border must be gone (#3028)');
|
||||
assert.notStrictEqual(japanese, english);
|
||||
});
|
||||
|
||||
@@ -1116,91 +1115,73 @@ describe('uat render-checkpoint', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// Regression: #2402 review medium finding — checkpointBoxLine() padded using
|
||||
// JS string `.length` (UTF-16 code units), not display width. The property
|
||||
// below supplies an independent, category-labelled cell-width oracle rather
|
||||
// than copying the implementation's Unicode range logic.
|
||||
describe('checkpoint banner padding uses terminal display width (#2402, #2530)', () => {
|
||||
test('property: category-labelled strings are padded to a 62-cell interior', () => {
|
||||
const oneCell = fc.constantFrom(
|
||||
{ text: 'a', width: 1 },
|
||||
{ text: '7', width: 1 },
|
||||
{ text: ' ', width: 1 },
|
||||
{ text: '\u093e', width: 1 }, // Mc: DEVANAGARI VOWEL SIGN AA
|
||||
{ text: '\u093f', width: 1 }, // Mc: DEVANAGARI VOWEL SIGN I
|
||||
{ text: '\u0949', width: 1 }, // Mc: DEVANAGARI VOWEL SIGN CANDRA O
|
||||
);
|
||||
const zeroCell = fc.constantFrom(
|
||||
{ text: '\u0301', width: 0 }, // Mn: COMBINING ACUTE ACCENT
|
||||
{ text: '\u093c', width: 0 }, // Mn: DEVANAGARI SIGN NUKTA
|
||||
{ text: '\u20dd', width: 0 }, // Me: COMBINING ENCLOSING CIRCLE
|
||||
{ text: '\u200d', width: 0 }, // Cf: ZERO WIDTH JOINER
|
||||
{ text: '\u2066', width: 0 }, // Cf: LEFT-TO-RIGHT ISOLATE
|
||||
{ text: '\u2069', width: 0 }, // Cf: POP DIRECTIONAL ISOLATE
|
||||
);
|
||||
const twoCell = fc.constantFrom(
|
||||
{ text: '界', width: 2 },
|
||||
{ text: '語', width: 2 },
|
||||
{ text: '한', width: 2 },
|
||||
);
|
||||
// Regression: #3028 — the checkpoint renderer no longer draws a 64-column
|
||||
// double-line box (checkpointBoxLine/displayWidth/isWideCodePoint/
|
||||
// ZERO_WIDTH_MARK_RE/CHECKPOINT_BOX_WIDTH were removed from src/uat.cts).
|
||||
// These cases now pin the heading form (`### {banner}`) directly instead of
|
||||
// a padded box interior; the localized-language coverage that used to prove
|
||||
// display-width-correct padding now proves the banner text is emitted
|
||||
// intact, unpadded, and box-free.
|
||||
describe('checkpoint banner renders as a heading, not a box (#2402, #2530, #3028)', () => {
|
||||
test('exact rendered banner heading for Japanese/Chinese/Korean (regression pin)', () => {
|
||||
const currentTest = { number: 1, name: 'Sample', expected: 'Something happens.' };
|
||||
const japanese = buildCheckpoint(currentTest, 'Japanese');
|
||||
const chinese = buildCheckpoint(currentTest, 'Chinese');
|
||||
const korean = buildCheckpoint(currentTest, 'Korean');
|
||||
|
||||
fc.assert(fc.property(
|
||||
fc.array(fc.oneof(oneCell, zeroCell, twoCell), { maxLength: 35 }),
|
||||
(cells) => {
|
||||
const text = cells.map((cell) => cell.text).join('');
|
||||
const textWidth = cells.reduce((sum, cell) => sum + cell.width, 0);
|
||||
const padding = ' '.repeat(Math.max(0, 60 - textWidth));
|
||||
assert.strictEqual(
|
||||
checkpointBoxLine(text),
|
||||
`║ ${text}${padding}║`,
|
||||
);
|
||||
},
|
||||
));
|
||||
});
|
||||
assert.strictEqual(japanese.split('\n')[0], '### チェックポイント: 検証が必要です');
|
||||
assert.strictEqual(chinese.split('\n')[0], '### 检查点:需要验证');
|
||||
assert.strictEqual(korean.split('\n')[0], '### 체크포인트: 검증 필요');
|
||||
|
||||
test('padding boundary: width limit-1, limit, and limit+1', () => {
|
||||
for (const width of [59, 60, 61]) {
|
||||
const text = 'a'.repeat(width);
|
||||
assert.strictEqual(
|
||||
checkpointBoxLine(text),
|
||||
`║ ${text}${' '.repeat(Math.max(0, 60 - width))}║`,
|
||||
`unexpected rendering at text width ${width}`,
|
||||
);
|
||||
for (const output of [japanese, chinese, korean]) {
|
||||
assert.ok(!/[╔╗╚╝║]/.test(output), 'the box border must be gone (#3028)');
|
||||
}
|
||||
});
|
||||
|
||||
test('exact rendered banner lines for Japanese/Chinese/Korean (regression pin)', () => {
|
||||
test('exact rendered Hindi banner heading ignores combining-mark cell width (regression pin)', () => {
|
||||
const currentTest = { number: 1, name: 'Sample', expected: 'Something happens.' };
|
||||
assert.strictEqual(
|
||||
buildCheckpoint(currentTest, 'Japanese').split('\n')[1],
|
||||
'║ チェックポイント: 検証が必要です ║',
|
||||
);
|
||||
assert.strictEqual(
|
||||
buildCheckpoint(currentTest, 'Chinese').split('\n')[1],
|
||||
'║ 检查点:需要验证 ║',
|
||||
);
|
||||
assert.strictEqual(
|
||||
buildCheckpoint(currentTest, 'Korean').split('\n')[1],
|
||||
'║ 체크포인트: 검증 필요 ║',
|
||||
);
|
||||
});
|
||||
|
||||
test('exact rendered Hindi banner line ignores combining-mark cell width (regression pin)', () => {
|
||||
const currentTest = { number: 1, name: 'Sample', expected: 'Something happens.' };
|
||||
assert.strictEqual(
|
||||
buildCheckpoint(currentTest, 'Hindi').split('\n')[1],
|
||||
`║ चेकपॉइंट: सत्यापन आवश्यक${' '.repeat(40)}║`,
|
||||
);
|
||||
const hindi = buildCheckpoint(currentTest, 'Hindi');
|
||||
assert.strictEqual(hindi.split('\n')[0], '### चेकपॉइंट: सत्यापन आवश्यक');
|
||||
assert.ok(!/[╔╗╚╝║]/.test(hindi), 'the box border must be gone (#3028)');
|
||||
});
|
||||
|
||||
test('exact rendered Arabic frame is isolated inside the LTR checkpoint layout', () => {
|
||||
const currentTest = { number: 1, name: 'Sample', expected: 'Something happens.' };
|
||||
const arabic = buildCheckpoint(currentTest, 'Arabic');
|
||||
// The one behavior the box removal must not disturb: the RTL banner and
|
||||
// instruction text stay wrapped in directional isolates.
|
||||
assert.strictEqual(
|
||||
arabic.split('\n')[1],
|
||||
`║ \u2067نقطة تحقق: المراجعة مطلوبة\u2069${' '.repeat(34)}║`,
|
||||
arabic.split('\n')[0],
|
||||
`### نقطة تحقق: المراجعة مطلوبة`,
|
||||
);
|
||||
assert.ok(arabic.includes('\u2067اكتب `pass` أو صف المشكلة.\u2069'));
|
||||
assert.ok(arabic.includes('اكتب `pass` أو صف المشكلة.'));
|
||||
assert.ok(!/[╔╗╚╝║]/.test(arabic), 'the box border must be gone (#3028)');
|
||||
});
|
||||
|
||||
test('emits an over-long banner intact (no box to overflow)', (t) => {
|
||||
// Previously a banner exceeding the 64-column inner width produced a
|
||||
// ragged, unpadded border. Now there is no border to overflow — the
|
||||
// full heading text is emitted intact regardless of length. None of the
|
||||
// shipped frames are long enough to exercise this, so a synthetic frame
|
||||
// is registered on the exported (mutable) lookup tables for the
|
||||
// duration of the test.
|
||||
const longBanner = `${'X'.repeat(80)}: Verification required well beyond the old 64-column box width`;
|
||||
const frameKey = '__test_overlong_frame__3028__';
|
||||
const aliasKey = '__test_overlong_alias__3028__';
|
||||
CHECKPOINT_FRAMES[frameKey] = {
|
||||
banner: longBanner,
|
||||
instruction: 'Type `pass` or describe what\'s wrong.',
|
||||
};
|
||||
CHECKPOINT_LANGUAGE_ALIASES[aliasKey] = frameKey;
|
||||
t.after(() => {
|
||||
delete CHECKPOINT_FRAMES[frameKey];
|
||||
delete CHECKPOINT_LANGUAGE_ALIASES[aliasKey];
|
||||
});
|
||||
const currentTest = { number: 1, name: 'Sample', expected: 'Something happens.' };
|
||||
const output = buildCheckpoint(currentTest, aliasKey);
|
||||
assert.strictEqual(output.split('\n')[0], `### ${longBanner}`,
|
||||
'an over-long banner must be emitted in full, not truncated or wrapped');
|
||||
assert.ok(!/[╔╗╚╝║]/.test(output), 'no box characters should appear regardless of banner length');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1224,7 +1205,9 @@ awaiting: user response
|
||||
assert.strictEqual(result.success, true, `render-checkpoint failed: ${result.error}`);
|
||||
assert.ok(result.output.includes('**Test 2: Submit form validation**'));
|
||||
assert.ok(result.output.includes('Empty submit keeps controls visible.'));
|
||||
assert.ok(result.output.includes("Type `pass` or describe what's wrong."));
|
||||
// The instruction line renders as a bold line preceded by a `---` thematic
|
||||
// break, not inside a box border (#3028).
|
||||
assert.ok(result.output.includes("---\n\n**Type `pass` or describe what's wrong.**"));
|
||||
});
|
||||
|
||||
test('strips protocol leak lines from current test copy', () => {
|
||||
@@ -1440,14 +1423,14 @@ awaiting: user response
|
||||
assert.ok(result.output.includes('Verificación requerida'), 'banner should be in Spanish');
|
||||
assert.ok(result.output.includes('Escribe `pass`'), 'instruction line should be in Spanish');
|
||||
|
||||
// Structure/IDs stay untranslated: box borders, the Test N: name line, and the
|
||||
// expected content are preserved verbatim.
|
||||
assert.ok(result.output.includes('╔══════════════════════════════════════════════════════════════╗'));
|
||||
assert.ok(result.output.includes('╚══════════════════════════════════════════════════════════════╝'));
|
||||
assert.ok(result.output.includes('──────────────────────────────────────────────────────────────'));
|
||||
// Structure/IDs stay untranslated: the heading marker, the `---` separator,
|
||||
// the Test N: name line, and the expected content are preserved verbatim.
|
||||
assert.ok(result.output.includes('### PUNTO DE CONTROL: Verificación requerida'));
|
||||
assert.ok(result.output.includes('---'));
|
||||
assert.ok(result.output.includes('**Test 2: Submit form validation**'));
|
||||
assert.ok(result.output.includes('Empty submit keeps controls visible.'));
|
||||
assert.ok(result.output.includes('Validation error copy is shown.'));
|
||||
assert.ok(!/[╔╗╚╝║]/.test(result.output), 'the box border must be gone (#3028)');
|
||||
});
|
||||
|
||||
// Regression guard for the "unset ⇒ byte-identical English" acceptance criterion.
|
||||
@@ -1471,17 +1454,15 @@ awaiting: user response
|
||||
assert.strictEqual(result.success, true, `render-checkpoint failed: ${result.error}`);
|
||||
|
||||
const expected = [
|
||||
'╔══════════════════════════════════════════════════════════════╗',
|
||||
'║ CHECKPOINT: Verification Required ║',
|
||||
'╚══════════════════════════════════════════════════════════════╝',
|
||||
'### CHECKPOINT: Verification Required',
|
||||
'',
|
||||
'**Test 2: Submit form validation**',
|
||||
'',
|
||||
'Empty submit keeps controls visible.\nValidation error copy is shown.',
|
||||
'',
|
||||
'──────────────────────────────────────────────────────────────',
|
||||
'Type `pass` or describe what\'s wrong.',
|
||||
'──────────────────────────────────────────────────────────────',
|
||||
'---',
|
||||
'',
|
||||
'**Type `pass` or describe what\'s wrong.**',
|
||||
].join('\n');
|
||||
|
||||
assert.strictEqual(result.output, expected);
|
||||
|
||||
Reference in New Issue
Block a user