@tekyzinc/gsd-t 4.7.11 → 4.8.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +1 -1
- package/bin/gsd-t-divergence-grammar.cjs +213 -0
- package/bin/gsd-t-guard-map-derive.cjs +210 -0
- package/bin/gsd-t-guard-map.cjs +279 -0
- package/bin/gsd-t-milestone-state.cjs +236 -0
- package/bin/gsd-t-rule-consume.cjs +141 -0
- package/bin/gsd-t-traceability-gate.cjs +321 -24
- package/bin/gsd-t.js +8 -0
- package/commands/gsd-t-doc-ripple.md +1 -1
- package/commands/gsd-t-milestone.md +33 -1
- package/package.json +1 -1
- package/templates/CLAUDE-global.md +2 -0
- package/templates/PseudoCode-spec.md +194 -0
- package/templates/prompts/keep-or-supersede-subagent.md +61 -0
- package/templates/prompts/qa-subagent.md +10 -0
- package/templates/prompts/red-team-subagent.md +12 -0
- package/templates/workflows/gsd-t-phase.workflow.js +22 -2
- package/templates/workflows/gsd-t-verify.workflow.js +101 -1
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# {Title} — {one-line subject of this behavior map}
|
|
2
|
+
|
|
3
|
+
**Pseudocode + intention for how {the subject} behaves, end to end.**
|
|
4
|
+
Covers WHERE the system makes each decision, WHEN it refuses to, and how the
|
|
5
|
+
result reaches every consumer. **{Scope line — what this is, and the one thing
|
|
6
|
+
it deliberately does NOT do.}**
|
|
7
|
+
|
|
8
|
+
> **⚠ {THE LOAD-BEARING DIRECTIVE} ({Author}, {date}).** {The single sentence a
|
|
9
|
+
> reader must not miss — the WHY behind the whole map, stated as the user's
|
|
10
|
+
> intention. Prose here is the USER's directive, never agent reasoning.}
|
|
11
|
+
|
|
12
|
+
_Forward-looking behavior map for **{milestone / scope id}**. Not yet built —
|
|
13
|
+
grounds itself in the EXISTING contracts it must respect:
|
|
14
|
+
`{existing-contract-a}.md`, `{existing-contract-b}.md`, and the frozen
|
|
15
|
+
`{Schema}` (`{field}` / `{field}` / `{field}`). Companion to
|
|
16
|
+
`PseudoCode-{Other}.md`. See `{roadmap ref}` for the milestone scope._
|
|
17
|
+
|
|
18
|
+
<!--
|
|
19
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
HOW TO USE THIS MOLD (delete this comment block in the real instance)
|
|
21
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
• Name the instance PseudoCode-[Title].md where [Title] is the SUBJECT
|
|
23
|
+
(PseudoCode-PayPal.md), never a milestone id. Only THIS blank mold keeps the
|
|
24
|
+
`-spec` suffix.
|
|
25
|
+
• Author the TWO ALTITUDES in order:
|
|
26
|
+
1. HIGH-LEVEL APPROACH — what / why / when, the actors, a one-breath
|
|
27
|
+
summary table. NO field-level detail. SIGN THIS OFF FIRST.
|
|
28
|
+
2. DETAILED — the full numbered `##` section set below, at
|
|
29
|
+
exemplar granularity (one section per decision boundary).
|
|
30
|
+
• Every section carries the FIVE SECTION ELEMENTS (see the contract §1):
|
|
31
|
+
Intention prose · Mechanism pseudocode · one-breath summary ·
|
|
32
|
+
[RULE] guard map · ⚠ Divergence flags · Appendix.
|
|
33
|
+
• Each `> **Intention.**` prose block sits ABOVE its fenced pseudocode and is
|
|
34
|
+
dated + attributed; the prose is the USER's WHY, never agent reasoning.
|
|
35
|
+
• The Mechanism block grounds in EXISTING contracts/schema and DEFERS concrete
|
|
36
|
+
identifiers to plan-time-against-the-real-schema.
|
|
37
|
+
• Guard map: render EVERY invariant as a one-line `[RULE] <invariant>` (or the
|
|
38
|
+
tagged `<invariant> [RULE — <tag>]` form). One marker = one rule.
|
|
39
|
+
• Divergence: wherever a NEW intention supersedes shipped code, write an
|
|
40
|
+
explicit `⚠ Divergence:` flag. Keep = no flag.
|
|
41
|
+
• Cite each implementing plan task back with:
|
|
42
|
+
**PseudoCode-Section**: {Title}#<github-slug-of-the-## heading>
|
|
43
|
+
Grammars are owned by the contract:
|
|
44
|
+
`.gsd-t/contracts/pseudocode-source-of-truth-contract.md`
|
|
45
|
+
(§2 guard-map, §3 section-citation, §4 divergence). Do NOT re-derive them here.
|
|
46
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
47
|
+
-->
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
<!-- ═══════════════ ALTITUDE 1 — HIGH-LEVEL APPROACH (sign off FIRST) ═══════════════ -->
|
|
52
|
+
|
|
53
|
+
## The one {call / decision}, in one breath
|
|
54
|
+
|
|
55
|
+
> **High-Level Approach ({Author}, {date}).** {What this does, why, and when —
|
|
56
|
+
> the actors and the single decision, in plain language. No field-level detail.
|
|
57
|
+
> This altitude is signed off BEFORE the Detailed sections below are written.}
|
|
58
|
+
|
|
59
|
+
| {Call / decision} | Lives in | Decides | Runs only when… |
|
|
60
|
+
|-------------------|----------|---------|-----------------|
|
|
61
|
+
| **{The call}** | **{realm — Server / Extension / …}** | {what it turns the input into} | {the precondition that triggers it} |
|
|
62
|
+
| **{The read / fallback}** | **{realm}** | {the question it answers} | {seller- / event-initiated trigger} |
|
|
63
|
+
|
|
64
|
+
> **SCOPE ({Author}, {date}).** {The deliberate OUT-of-scope boundary — what this
|
|
65
|
+
> map does NOT do. State the one status / call / surface it touches and the ones
|
|
66
|
+
> it refuses to touch. This keeps the Detailed sections honest.}
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
<!-- ═══════════════ ALTITUDE 2 — DETAILED (exemplar granularity) ═══════════════ -->
|
|
71
|
+
|
|
72
|
+
## 0. Where this picks up — {precondition / what exists before}
|
|
73
|
+
|
|
74
|
+
> **Intention ({Author}, {date}).** {What state the system is in when this map
|
|
75
|
+
> begins — the precondition. If this SUPERSEDES a shipped model, say so here and
|
|
76
|
+
> add the ⚠ Divergence flag below.}
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
PRECONDITION:
|
|
80
|
+
{The exact starting state — what is loaded / grouped / present, and what is
|
|
81
|
+
deliberately NOT persisted yet.}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
> ⚠ **Divergence from shipped {what} (plan-time reconcile):** {what the shipped
|
|
85
|
+
> code does today} vs. {what THIS intention does instead}. {What becomes dead
|
|
86
|
+
> code / what is reused.} Flag for the {milestone} plan.
|
|
87
|
+
> *(Keep = delete this flag. Supersede = keep it, per contract §4.)*
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 1. {First boundary} — {the trigger → the actor that owns it}
|
|
92
|
+
|
|
93
|
+
> **Intention.** {WHY this step exists and what the user wants it to do — the
|
|
94
|
+
> directive. One short paragraph, the user's voice.}
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
{ACTOR} on {trigger}:
|
|
98
|
+
{step}
|
|
99
|
+
{the call / the guard}
|
|
100
|
+
on {outcome}: {what happens}
|
|
101
|
+
on {failure}: {the safe fallback — never a crash}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 2. {Second boundary} — `{the operation}` (★ {why this one is load-bearing})
|
|
107
|
+
|
|
108
|
+
> **Intention.** {The WHY. Name the one invariant this section exists to protect
|
|
109
|
+
> — e.g. "every guard makes a double-click HARMLESS".}
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
{operationName}({inputs}):
|
|
113
|
+
|
|
114
|
+
# ── GATE 1 — {what it validates} (RULE) ────────────────────────────────
|
|
115
|
+
# Intention: {why this gate exists, in the user's voice}.
|
|
116
|
+
{load / lock}
|
|
117
|
+
if {precondition fails}: → {status / error} # {what it protects}
|
|
118
|
+
|
|
119
|
+
# ── GATE 2 — {backstop} (RULE, backstop) ───────────────────────────────
|
|
120
|
+
# Intention: {why — name it a backstop if upstream already blocks it}.
|
|
121
|
+
if {impossible condition}: → {status}
|
|
122
|
+
|
|
123
|
+
# ── STEP 3 — {the side-effecting call} ───────────────────── ★ {marker}
|
|
124
|
+
# Intention: {what crosses the boundary and what is deferred to real schema}.
|
|
125
|
+
{the call} # defer concrete ids to plan-time
|
|
126
|
+
|
|
127
|
+
# ── STEP 4 — PERSIST + {atomic effect} (RULE, one tx) ──────────────────
|
|
128
|
+
# Intention: {the record is born / the state flips — atomically}.
|
|
129
|
+
in ONE tx:
|
|
130
|
+
{write}
|
|
131
|
+
{flip state}
|
|
132
|
+
return {success shape}
|
|
133
|
+
|
|
134
|
+
# ── FAILURE — never half-{do the thing} (RULE) ─────────────────────────
|
|
135
|
+
# Intention: a failed {op} persists NOTHING — safe retry.
|
|
136
|
+
on {failure} (any point): nothing persisted ; → {status}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 3. {Further boundaries as needed} — {one `##` section per decision}
|
|
142
|
+
|
|
143
|
+
> **Intention.** {Add as many numbered `##` sections as the subject has distinct
|
|
144
|
+
> decision boundaries — match the exemplars' granularity, one section per real
|
|
145
|
+
> decision, not one giant section.}
|
|
146
|
+
|
|
147
|
+
```text
|
|
148
|
+
{the mechanism for this boundary}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## {N}. {Subject}-safety map — every guard, as a one-line [RULE]
|
|
154
|
+
|
|
155
|
+
```text
|
|
156
|
+
GATE: {condition} → {status} [RULE] {the invariant in one line}
|
|
157
|
+
GATE: {lock / serialize} [RULE] {what it prevents}
|
|
158
|
+
{deterministic guard} [RULE — {tag}] {invariant, tagged form}
|
|
159
|
+
{never-do-this} [RULE] {the prohibition}
|
|
160
|
+
{born / set at this exact point} [RULE] {the lifecycle invariant}
|
|
161
|
+
on {failure}: persist NOTHING [RULE] {the safe-failure invariant}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**{One paragraph restating the whole safety story in prose: the record is born
|
|
165
|
+
WHEN, the lock means WHAT, the one thing that can never happen, and why every
|
|
166
|
+
retry / double-action is harmless.}**
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## {Optional} — {ONE STORE / shared-state / known-gaps notes}
|
|
171
|
+
|
|
172
|
+
> {Optional sections the exemplars carry: a shared-store note, a "Known gaps /
|
|
173
|
+
> status (as of {version})" list. Include only if the subject has them.}
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Appendix — Raw pseudocode (no intention comments)
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
181
|
+
# {REALM A} — {what it covers}
|
|
182
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
183
|
+
{the §1–§N mechanism, intention prose STRIPPED — the build's quick-reference}
|
|
184
|
+
|
|
185
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
186
|
+
# {REALM B} — {what it covers}
|
|
187
|
+
# ════════════════════════════════════════════════════════════════════════════
|
|
188
|
+
{operationName}({inputs}):
|
|
189
|
+
{guard} # {one-line reason}
|
|
190
|
+
{the call} # ★ {the load-bearing call}
|
|
191
|
+
tx: {write} ; {flip state}
|
|
192
|
+
return {success}
|
|
193
|
+
on fail: persist NOTHING ; → {status} # safe retry
|
|
194
|
+
```
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Keep-or-Supersede Subagent Prompt — Inherited Shipped-Code Forcing Protocol
|
|
2
|
+
|
|
3
|
+
<!-- M87 D3 — the forcing keep-or-supersede protocol. PROSE PROTOCOL: its reliability
|
|
4
|
+
is bounded by how FORCING this prompt is, NOT by a deterministic gate. The
|
|
5
|
+
deterministic parseDivergence()/formatDivergence() grammar round-trip is M88.
|
|
6
|
+
Contract: .gsd-t/contracts/pseudocode-source-of-truth-contract.md §4. -->
|
|
7
|
+
|
|
8
|
+
You are the **keep-or-supersede** agent. Your sole job: for **every model inherited from shipped code** that the milestone is about to encode into its intention-first `PseudoCode-[Title].md`, FORCE an explicit **keep-or-supersede** decision from the user — and on every supersede, WRITE a `⚠ Divergence` flag into the doc. You write ZERO feature code.
|
|
9
|
+
|
|
10
|
+
## Why this exists (the real failure this prevents)
|
|
11
|
+
|
|
12
|
+
A real incident: a PayPal-first web app inherited a **stored-draft over-trust** model from shipped code — it assumed a draft already persisted on the PayPal server was still valid, and re-confirmed deletion against that stale draft instead of re-checking the live state. The shipped model was silently carried forward as if it were intention, and the bug shipped with it. The rescue was to STOP at each inherited model and ask the user, out loud: *"do we keep this behavior, or does a new intention supersede it?"* — and to RECORD every supersede so the divergence from shipped code is never silent.
|
|
13
|
+
|
|
14
|
+
This protocol bakes that rescue into the methodology. Inherited code is a HYPOTHESIS about intent, never a proven intent. Encoding it without asking is the deadly pattern.
|
|
15
|
+
|
|
16
|
+
## The Forcing Rule (non-negotiable)
|
|
17
|
+
|
|
18
|
+
**Never encode an inherited shipped-code model without an explicit keep-or-supersede decision.** "It already works this way" is NOT a keep decision — it is the un-asked assumption this protocol exists to break. For each inherited model:
|
|
19
|
+
|
|
20
|
+
1. **Surface it plainly.** State the inherited behavior in one jargon-free sentence: what the shipped code currently does, where it lives (the file/realm), and the model it embeds. Lead with a short concrete example of when that behavior bites, then map it to the code in one line — so the user can decide with understanding, not rubber-stamp.
|
|
21
|
+
2. **ASK, do not assume.** Present exactly two choices and require a decision:
|
|
22
|
+
- **KEEP** — the shipped behavior IS the intended behavior. The doc encodes it as-is. **No flag is written.**
|
|
23
|
+
- **SUPERSEDE** — a new intention replaces the shipped behavior. The doc encodes the NEW intention, and a `⚠ Divergence` flag is WRITTEN (see below).
|
|
24
|
+
3. **The doc prose is the USER'S intention, never your reasoning.** When you write the superseding intention, write what the USER decided and why, in their framing — not your justification for it.
|
|
25
|
+
4. **Default is NOT silent keep.** If the user has not decided, you do NOT proceed by silently keeping. You ASK. An un-asked inherited model is an open question, never a default-on behavior (`feedback_no_silent_degradation`, `feedback_unproven_assumption_stop_and_research`).
|
|
26
|
+
|
|
27
|
+
## On SUPERSEDE — write the Divergence flag (mandatory)
|
|
28
|
+
|
|
29
|
+
Every **supersede** WRITES a `⚠ Divergence` flag into `PseudoCode-[Title].md`, using the §4 grammar shape exactly:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
⚠ Divergence: <RULE-ID or section> — supersedes shipped <what>. Reason: <user intention>.
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- `<RULE-ID or section>` — the guard-map RULE-ID or the doc section the divergence attaches to.
|
|
36
|
+
- `<what>` — the shipped behavior being superseded, in one phrase.
|
|
37
|
+
- `<user intention>` — WHY, in the user's framing (the new intention that replaces it).
|
|
38
|
+
|
|
39
|
+
**KEEP writes no flag.** One supersede → exactly one flag. The flag is captured IN the doc so the divergence from shipped code is an explicit, visible artifact — never silent.
|
|
40
|
+
|
|
41
|
+
> **M87/M88 split.** In M87 this flag is WRITTEN by this prose protocol (the ASK + the write). The DETERMINISTIC `parseDivergence()` / `formatDivergence()` round-trip — making the divergence COUNT a code-checkable, byte-stable artifact that can feed the guard-map rule set — is **M88** (backlog #35). The §4 grammar is the spec for both; only the round-trip IMPLEMENTATION is deferred. Here, your obligation is: ask, and on supersede, write a well-formed flag.
|
|
42
|
+
|
|
43
|
+
## What to do (step by step)
|
|
44
|
+
|
|
45
|
+
1. **Enumerate inherited models.** From the brief / requirements / the shipped code being carried forward, list every behavioral model the milestone would inherit. If `$BRIEF_PATH` is set, read it first (the ≤2,500-token snapshot) before re-walking the repo.
|
|
46
|
+
2. **For each inherited model, run the Forcing Rule above** — surface it, ASK keep-or-supersede, get the decision.
|
|
47
|
+
3. **On KEEP:** encode the behavior as-is in the doc. No flag.
|
|
48
|
+
4. **On SUPERSEDE:** encode the NEW intention (in the user's framing) and WRITE the `⚠ Divergence` flag in §4 grammar.
|
|
49
|
+
5. **Report** the full keep/supersede ledger: every inherited model, the decision, and for each supersede the exact flag written.
|
|
50
|
+
|
|
51
|
+
## Report Format
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Inherited models reviewed: N
|
|
55
|
+
- <model>: KEEP — encoded as-is.
|
|
56
|
+
- <model>: SUPERSEDE — flag written: "⚠ Divergence: <ref> — supersedes shipped <what>. Reason: <intention>."
|
|
57
|
+
...
|
|
58
|
+
Divergence flags written: M (= number of supersedes)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If ANY inherited model was encoded WITHOUT an explicit keep-or-supersede decision, that is a protocol FAILURE — report it as such; do not mark the work complete.
|
|
@@ -8,6 +8,16 @@ You are the QA agent. Your sole job is test generation, execution, and gap repor
|
|
|
8
8
|
<!-- M55-D5: brief-first rule -->
|
|
9
9
|
**Brief first.** If you're about to grep, read, or run a test, check the brief first at `$BRIEF_PATH` (a ≤2,500-token JSON snapshot of CLAUDE.md + contracts + scope + constraints, generated by `bin/gsd-t-context-brief.cjs`). The brief replaces the 30–60k context re-read every parallel worker would otherwise perform — it's the dominant ITPM-relief lever in M55. If `$BRIEF_PATH` is unset or the file is missing, fall back to the legacy read-everything pattern, but log a note so the orchestrator can see the gap.
|
|
10
10
|
|
|
11
|
+
<!-- guard-map-ingest -->
|
|
12
|
+
**Guard-map rule ingest (A5 — contract-compliance frame).** When a guard-map is
|
|
13
|
+
present, the surfaced RULE-ID set reaches you via
|
|
14
|
+
`gsd-t rule-consume --map <guard-map.json> --json` → the `qa` array. INGEST that
|
|
15
|
+
set: treat **every RULE-ID as a required compliance assertion**. For each id, the
|
|
16
|
+
build/tests MUST demonstrate the rule's invariant holds — an unverified or
|
|
17
|
+
unasserted rule is a contract-compliance FAILURE, not a pass. Do NOT drop, skip,
|
|
18
|
+
or sample rules: every id in the `qa` frame is a mandatory check.
|
|
19
|
+
<!-- /guard-map-ingest -->
|
|
20
|
+
|
|
11
21
|
## What to Do
|
|
12
22
|
|
|
13
23
|
1. **Detect every configured test suite** in this project — vitest/jest/mocha config, `playwright.config.*`, `cypress.config.*`. Run EVERY suite that exists.
|
|
@@ -8,6 +8,18 @@ You are a Red Team QA adversary. Your job is to BREAK the code that was just wri
|
|
|
8
8
|
<!-- M55-D5: brief-first rule -->
|
|
9
9
|
**Brief first.** If you're about to grep, read, or run a test, check the brief first at `$BRIEF_PATH` (a ≤2,500-token JSON snapshot of CLAUDE.md + contracts + scope + constraints + recent commits, generated by `bin/gsd-t-context-brief.cjs --kind red-team`). The brief identifies high-risk surfaces, recent diffs, and contract status — your starting attack surface. The brief replaces the 30–60k context re-read every parallel worker would otherwise perform. If `$BRIEF_PATH` is unset or the file is missing, fall back to the legacy read-everything pattern, but log a note so the orchestrator can see the gap.
|
|
10
10
|
|
|
11
|
+
<!-- guard-map-ingest -->
|
|
12
|
+
**Guard-map rule ingest (A5 — attack-surface frame).** When a guard-map is
|
|
13
|
+
present, the surfaced RULE-ID set reaches you via
|
|
14
|
+
`gsd-t rule-consume --map <guard-map.json> --json` → the `redTeam` array. INGEST
|
|
15
|
+
that set: treat **every RULE-ID as an invariant to ATTACK**. For each id, mount a
|
|
16
|
+
concrete attack that tries to make the rule's invariant FALSE (boundary inputs,
|
|
17
|
+
race/double-submit, out-of-order state, error paths). A rule you cannot break
|
|
18
|
+
after an exhaustive attempt is a defended invariant; a rule you DROP from your
|
|
19
|
+
attack plan is a seam breach. Do NOT sample — every id in the `redTeam` frame
|
|
20
|
+
must be attacked.
|
|
21
|
+
<!-- /guard-map-ingest -->
|
|
22
|
+
|
|
11
23
|
## Hard Rules
|
|
12
24
|
|
|
13
25
|
- **Bugs found = value.** A short attack list is failure.
|
|
@@ -555,9 +555,19 @@ const _PROBE_SCHEMA = {
|
|
|
555
555
|
approaches: { type: "array", items: { type: "string" } },
|
|
556
556
|
},
|
|
557
557
|
};
|
|
558
|
-
async function runSolutionSpaceProbe(projectDir, phaseName, { milestone, briefPath, userInput, phaseNameOpt } = {}) {
|
|
558
|
+
async function runSolutionSpaceProbe(projectDir, phaseName, { milestone, briefPath, userInput, phaseNameOpt, altitude } = {}) {
|
|
559
|
+
// M87 altitude shift: when behavior is SPEC'D (the milestone is authored at the
|
|
560
|
+
// intention-first high-level-approach altitude), the milestone solution-space
|
|
561
|
+
// probe shifts UP — it competes over high-level APPROACHES (what/why/when, actors,
|
|
562
|
+
// one-breath thesis), NOT lower-altitude implementation detail. This changes WHAT
|
|
563
|
+
// the producers compete on, never WHO competes (producers stay opus, judge differs —
|
|
564
|
+
// M82 blindness invariant preserved) nor the probe's model (stays fable).
|
|
565
|
+
const atApproachAltitude = altitude === "high-level-approach";
|
|
559
566
|
const prompt = [
|
|
560
567
|
`You are the Solution-Space Probe for the ${phaseName} phase${milestone ? ` of ${milestone}` : ""}. Decide ONE thing: should this phase generate MULTIPLE competing candidates (then a judge picks the best), or is a single draft sufficient?`,
|
|
568
|
+
atApproachAltitude
|
|
569
|
+
? `ALTITUDE: HIGH-LEVEL APPROACH (M87 — behavior is spec'd intention-first). Compete over distinct high-level APPROACHES — what/why/when, the actors, the one-breath thesis — NOT over field-level implementation detail. The competition is about WHICH approach best serves the intention, decided before the detailed PseudoCode-[Title].md is authored.`
|
|
570
|
+
: "",
|
|
561
571
|
`**Brief:** ${briefPath || "(none — read the relevant .gsd-t docs/contracts/requirements directly)"}`,
|
|
562
572
|
userInput ? `\nUser input:\n${userInput}\n` : "",
|
|
563
573
|
`Compete WHEN there are ≥2 genuinely DIFFERENT, viable approaches whose trade-offs matter — different architectures, decomposition strategies, data models, sequencing, or design directions that a reasonable expert could disagree about. List them in "approaches".`,
|
|
@@ -690,9 +700,13 @@ if (_competitionEligible) {
|
|
|
690
700
|
// Automatic decision — the workflow probes and decides. Opus probe (or the
|
|
691
701
|
// partition-specific probe); biased toward competing.
|
|
692
702
|
phase("Probe");
|
|
703
|
+
// M87: the milestone phase is authored intention-first — behavior is spec'd at the
|
|
704
|
+
// high-level-approach altitude, so its solution-space probe shifts UP to compete over
|
|
705
|
+
// high-level approaches (not implementation detail). Other phases keep their altitude.
|
|
706
|
+
const _probeAltitude = phaseName === "milestone" ? "high-level-approach" : undefined;
|
|
693
707
|
const probe = phaseName === "partition"
|
|
694
708
|
? await runPartitionProbe(projectDir, { milestone, briefPath: brief.briefPath, userInput, phaseNameOpt: "Probe" })
|
|
695
|
-
: await runSolutionSpaceProbe(projectDir, phaseName, { milestone, briefPath: brief.briefPath, userInput, phaseNameOpt: "Probe" });
|
|
709
|
+
: await runSolutionSpaceProbe(projectDir, phaseName, { milestone, briefPath: brief.briefPath, userInput, phaseNameOpt: "Probe", altitude: _probeAltitude });
|
|
696
710
|
competitionOn = !!probe.compete;
|
|
697
711
|
competitionN = competitionOn ? AUTO_COMPETITION_N : 1;
|
|
698
712
|
log(`competition: AUTO → ${competitionOn ? `COMPETE (${AUTO_COMPETITION_N} producers)` : "single draft"} — ${probe.reason}${probe.approaches && probe.approaches.length ? ` [approaches: ${probe.approaches.join("; ")}]` : ""}`);
|
|
@@ -720,6 +734,12 @@ ${STATED_CLAIMS_INSTRUCTION}`,
|
|
|
720
734
|
${STATED_CLAIMS_INSTRUCTION}`,
|
|
721
735
|
milestone: `Define a new milestone — origin, goal, success criteria, falsifiable acceptance. Append to .gsd-t/progress.md. Defer partition/plan.
|
|
722
736
|
|
|
737
|
+
TWO-ALTITUDE INTENTION-FIRST FLOW (M87, default-ON — contract pseudocode-source-of-truth-contract.md §1). Author the milestone at two altitudes, IN ORDER:
|
|
738
|
+
ALTITUDE 1 — HIGH-LEVEL APPROACH (signed off FIRST): emit the high-level approach pseudocode — what/why/when (the user's intention, never agent reasoning), the actors, and a one-breath summary ("one call in one breath"). PRESENT this approach to the user for SIGN-OFF. The detailed doc is authored ONLY AFTER the approach is approved — the sign-off is the checkpoint between the two altitudes. (This is a PROSE flow: describe the checkpoint; do NOT assert a machine-checkable DEFINED-state predicate — that is M88.)
|
|
739
|
+
ALTITUDE 2 — DETAILED doc: only after the approach is signed off, author .gsd-t/pseudocode/PseudoCode-[Title].md at exemplar granularity (the §1 section set: Intention, Mechanism, one-breath table, Guard map, Divergence flags, Appendix). [Title] = the SUBJECT (e.g. PseudoCode-PayPal.md), never a milestone id; a milestone may produce several.
|
|
740
|
+
DEFAULT-ON; skip is a LOGGED decision in progress.md naming WHY — NEVER a silent default-off (feedback_no_silent_degradation).
|
|
741
|
+
KEEP-OR-SUPERSEDE: before encoding any model inherited from shipped code, run the keep-or-supersede protocol (templates/prompts/keep-or-supersede-subagent.md) — per inherited model ASK keep or supersede; each supersede WRITES a ⚠ Divergence flag (§4 grammar) into the doc. Keep = no flag.
|
|
742
|
+
|
|
723
743
|
${STATED_CLAIMS_INSTRUCTION}`,
|
|
724
744
|
prd: `Generate a product requirements doc at docs/prd.md. Functional + non-functional requirements traceable to acceptance criteria.`,
|
|
725
745
|
"design-decompose": `Decompose a design reference (Figma URL / images) into hierarchical contracts: elements -> widgets -> pages, each at .gsd-t/contracts/design/.`,
|
|
@@ -39,6 +39,7 @@ export const meta = {
|
|
|
39
39
|
{ title: "Auto-Research Gate", detail: "M89 §7 ENFORCE: scan for status=uncited markers (A4 — no silent guess)" },
|
|
40
40
|
{ title: "CI-Parity", detail: "M57 build-coverage + ci-parity (FAIL-blocking)" },
|
|
41
41
|
{ title: "Test-Data Purge", detail: "M58 test-data --purge (FAIL-blocking)" },
|
|
42
|
+
{ title: "Guard-Map Gate", detail: "M87 [RULE] guard-map gate (FAIL-blocking, §7 discovery)" },
|
|
42
43
|
{ title: "Orthogonal Triad", detail: "code-review ultra ∥ Red Team ∥ QA" },
|
|
43
44
|
{ title: "Synthesis", detail: "merge without collapsing categories" },
|
|
44
45
|
],
|
|
@@ -517,7 +518,105 @@ if (!td.ok) {
|
|
|
517
518
|
log(`M58 test-data purge FAIL exitCode=${td.exitCode} — halting (FAIL-blocking)`);
|
|
518
519
|
return { status: "test-data-purge-failed", overallVerdict: "VERIFY-FAILED", testDataPurge: td.envelope };
|
|
519
520
|
}
|
|
520
|
-
log(`M58 test-data purge green — proceeding to
|
|
521
|
+
log(`M58 test-data purge green — proceeding to guard-map gate`);
|
|
522
|
+
|
|
523
|
+
// ─── M87 D1 Guard-Map Gate (FAIL-blocking — A1 / SC1) ─────────────────────
|
|
524
|
+
// Turns each PseudoCode-[Title].md prose `[RULE]` guard map into a
|
|
525
|
+
// machine-checkable gate: a divergent (UNBACKED or CONTRADICTED) rule HALTS
|
|
526
|
+
// verify BEFORE the triad, at contract-breach severity, naming the RULE-ID —
|
|
527
|
+
// zero LLM judgment in the pass/fail decision (deterministic, like verify-gate /
|
|
528
|
+
// CI-parity / test-data).
|
|
529
|
+
//
|
|
530
|
+
// Discovery per contract §7 (deterministic, path-as-path):
|
|
531
|
+
// 1. glob `.gsd-t/pseudocode/PseudoCode-*.md` (multi-doc — a milestone may
|
|
532
|
+
// produce several subjects).
|
|
533
|
+
// 2. each doc's map = same basename with `.md` → `.map.json`, co-located.
|
|
534
|
+
// 3. pairing outcomes (each OBSERVABLE, never a silent pass):
|
|
535
|
+
// - doc + co-located map → FIRE the gate on that pair.
|
|
536
|
+
// - doc with no map → logged skip-with-reason `no-build-map`.
|
|
537
|
+
// - zero docs → logged skip-with-reason `no-pseudocode-docs`.
|
|
538
|
+
// 4. the fire path passes `--doc <doc> --map <map>` to gsd-t-guard-map.cjs; any
|
|
539
|
+
// FAIL-blocking non-zero HALTS verify before the triad.
|
|
540
|
+
// M81: no fs — a haiku discovery agent globs + pairs and returns the set; each
|
|
541
|
+
// fire-able pair is gated via the runCli helper (model: haiku, like the other gates).
|
|
542
|
+
// Contract: pseudocode-source-of-truth-contract.md §2 + §7.
|
|
543
|
+
phase("Guard-Map Gate");
|
|
544
|
+
const GUARD_MAP_DISCOVERY_SCHEMA = {
|
|
545
|
+
type: "object",
|
|
546
|
+
required: ["docsFound", "firePairs", "skips"],
|
|
547
|
+
additionalProperties: true,
|
|
548
|
+
properties: {
|
|
549
|
+
docsFound: { type: "integer" },
|
|
550
|
+
firePairs: {
|
|
551
|
+
type: "array",
|
|
552
|
+
items: {
|
|
553
|
+
type: "object",
|
|
554
|
+
required: ["doc", "map"],
|
|
555
|
+
properties: { doc: { type: "string" }, map: { type: "string" } },
|
|
556
|
+
},
|
|
557
|
+
},
|
|
558
|
+
skips: {
|
|
559
|
+
type: "array",
|
|
560
|
+
items: {
|
|
561
|
+
type: "object",
|
|
562
|
+
required: ["reason"],
|
|
563
|
+
properties: { reason: { type: "string" }, doc: { type: "string" } },
|
|
564
|
+
},
|
|
565
|
+
},
|
|
566
|
+
notes: { type: "string" },
|
|
567
|
+
},
|
|
568
|
+
};
|
|
569
|
+
const guardMapDiscovery = await agent(
|
|
570
|
+
[
|
|
571
|
+
`You are the M87 guard-map discovery scanner for the project at "${projectDir}". Discover the PseudoCode doc + build-map pairs per contract §7 (deterministic, path-as-path) and return JSON. Do NOT gate anything — only enumerate.`,
|
|
572
|
+
``,
|
|
573
|
+
`Steps:`,
|
|
574
|
+
`1. Glob the docs: \`ls ${projectDir}/.gsd-t/pseudocode/PseudoCode-*.md 2>/dev/null || true\`. These are the candidate docs (a milestone may produce SEVERAL — handle the multi-doc set).`,
|
|
575
|
+
`2. docsFound = the number of PseudoCode-*.md docs found.`,
|
|
576
|
+
`3. For EACH doc, the build-map is the SAME basename with \`.md\` replaced by \`.map.json\`, co-located in \`.gsd-t/pseudocode/\` (e.g. PseudoCode-Foo.md → PseudoCode-Foo.map.json). Check it exists: \`test -f <map> && echo EXISTS || echo ABSENT\`.`,
|
|
577
|
+
` - doc + co-located map EXISTS → add { "doc": "<abs-or-project-rel doc path>", "map": "<map path>" } to firePairs.`,
|
|
578
|
+
` - doc with map ABSENT → add { "reason": "no-build-map", "doc": "<doc path>" } to skips (a logged skip WITH A REASON, never a silent pass).`,
|
|
579
|
+
`4. If docsFound === 0 → firePairs is empty and skips = [ { "reason": "no-pseudocode-docs" } ] (the gate legitimately has nothing to gate, but the skip is SURFACED, not silent).`,
|
|
580
|
+
``,
|
|
581
|
+
`Return JSON per the schema: { "docsFound": N, "firePairs": [ { "doc", "map" } ], "skips": [ { "reason", "doc"? } ], "notes": "..." }. Discovery is path-as-path (glob + basename derivation), never substring.`,
|
|
582
|
+
].join("\n"),
|
|
583
|
+
{ label: "guard-map-discovery", phase: "Guard-Map Gate", schema: GUARD_MAP_DISCOVERY_SCHEMA, model: "haiku" }
|
|
584
|
+
).catch((e) => ({ docsFound: 0, firePairs: [], skips: [{ reason: "discovery-error" }], notes: `guard-map discovery agent error: ${e && e.message}` }));
|
|
585
|
+
|
|
586
|
+
const guardMapResults = [];
|
|
587
|
+
if (guardMapDiscovery.docsFound === 0 || (guardMapDiscovery.firePairs || []).length === 0) {
|
|
588
|
+
// No fire-able pair — surface the DISTINCT skip reason(s), never a silent pass.
|
|
589
|
+
const reasons = (guardMapDiscovery.skips || []).map((s) => s.reason).join(", ") || "no-pseudocode-docs";
|
|
590
|
+
log(`M87 guard-map gate: SKIP (no fire-able doc+map pair) — reason(s): ${reasons}`);
|
|
591
|
+
} else {
|
|
592
|
+
for (const pair of guardMapDiscovery.firePairs) {
|
|
593
|
+
const gmr = await runCli(
|
|
594
|
+
projectDir,
|
|
595
|
+
"guard-map",
|
|
596
|
+
["--doc", pair.doc, "--map", pair.map, "--json"],
|
|
597
|
+
"gsd-t-guard-map.cjs",
|
|
598
|
+
`m87:guard-map:${pair.doc.split("/").pop()}`,
|
|
599
|
+
true,
|
|
600
|
+
"Guard-Map Gate"
|
|
601
|
+
);
|
|
602
|
+
guardMapResults.push({ pair, envelope: gmr.envelope, ok: gmr.ok, exitCode: gmr.exitCode });
|
|
603
|
+
if (!gmr.ok) {
|
|
604
|
+
const env = gmr.envelope || {};
|
|
605
|
+
const named = (env.violations || []).map((v) => v.id).join(", ") || env.reason || "(rule id unavailable)";
|
|
606
|
+
log(`M87 guard-map gate FAIL exitCode=${gmr.exitCode} on ${pair.doc} — divergent RULE(s): ${named} — halting before triad`);
|
|
607
|
+
return {
|
|
608
|
+
status: "guard-map-gate-failed",
|
|
609
|
+
overallVerdict: "VERIFY-FAILED",
|
|
610
|
+
reason: `M87 guard-map divergence on ${pair.doc}: ${named} (unbacked or contradicted [RULE]) — back/cite the rule, then re-verify`,
|
|
611
|
+
guardMap: { discovery: guardMapDiscovery, results: guardMapResults },
|
|
612
|
+
verifyGate: vg.envelope,
|
|
613
|
+
autoResearchGate: arGate,
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
log(`M87 guard-map gate: PASS on ${pair.doc} — all ${(gmr.envelope && gmr.envelope.ruleCount) || "?"} [RULE]s backed, none contradicted`);
|
|
617
|
+
}
|
|
618
|
+
log(`M87 guard-map gate green (${guardMapResults.length} doc+map pair(s) fired) — proceeding to orthogonal triad`);
|
|
619
|
+
}
|
|
521
620
|
|
|
522
621
|
phase("Orthogonal Triad");
|
|
523
622
|
|
|
@@ -629,6 +728,7 @@ return {
|
|
|
629
728
|
buildCoverage: bc.envelope,
|
|
630
729
|
ciParity: cip.envelope,
|
|
631
730
|
testDataPurge: td.envelope,
|
|
731
|
+
guardMap: { discovery: guardMapDiscovery, results: guardMapResults },
|
|
632
732
|
triad: triadResults,
|
|
633
733
|
verdict,
|
|
634
734
|
};
|