@tekyzinc/gsd-t 5.0.14 → 5.1.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 +13 -0
- package/README.md +1 -1
- package/bin/gsd-t-doc-marker.cjs +65 -0
- package/bin/gsd-t-env-registry-check.cjs +318 -0
- package/bin/gsd-t-env-registry.cjs +1046 -0
- package/bin/gsd-t-logging-scaffolder.cjs +6 -18
- package/bin/gsd-t-verify-gate.cjs +2 -0
- package/bin/gsd-t.js +10 -0
- package/commands/gsd-t-architect.md +10 -0
- package/commands/gsd-t-init.md +6 -0
- package/commands/gsd-t-populate.md +5 -0
- package/package.json +1 -1
- package/scripts/gsd-t-architect-oversight-guard.js +5 -1
- package/templates/CLAUDE-global.md +32 -0
- package/templates/infrastructure.md +40 -0
- package/templates/workflows/gsd-t-phase.workflow.js +7 -0
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
const fs = require("fs");
|
|
22
22
|
const path = require("path");
|
|
23
23
|
const crypto = require("crypto");
|
|
24
|
+
// M102 — the marker-block idempotent doc-writer was extracted here into the ONE
|
|
25
|
+
// shared module both this scaffolder and bin/gsd-t-env-registry.cjs call.
|
|
26
|
+
const { upsertMarkedDocBlock } = require("./gsd-t-doc-marker.cjs");
|
|
24
27
|
|
|
25
28
|
const VALID_BACKENDS = ["db-table", "local-sqlite", "local-jsonl"];
|
|
26
29
|
|
|
@@ -182,24 +185,9 @@ function writeChoiceToProjectDocs(projectDir, envelope) {
|
|
|
182
185
|
const targetPath = candidates.find((p) => fs.existsSync(p)) || candidates[0];
|
|
183
186
|
|
|
184
187
|
const block = docBlock(envelope);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
} else {
|
|
189
|
-
const dir = path.dirname(targetPath);
|
|
190
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
if (content.includes(DOC_MARKER_START) && content.includes(DOC_MARKER_END)) {
|
|
194
|
-
const startIdx = content.indexOf(DOC_MARKER_START);
|
|
195
|
-
const endIdx = content.indexOf(DOC_MARKER_END) + DOC_MARKER_END.length;
|
|
196
|
-
content = content.slice(0, startIdx) + block + content.slice(endIdx);
|
|
197
|
-
} else {
|
|
198
|
-
content = content.replace(/\s*$/, "") + "\n\n" + block + "\n";
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
fs.writeFileSync(targetPath, content, "utf8");
|
|
202
|
-
return targetPath;
|
|
188
|
+
// M102 — delegate to the ONE shared marker-block writer (byte-identical logic
|
|
189
|
+
// to the former inline replace/append; extracted so env-registry reuses it).
|
|
190
|
+
return upsertMarkedDocBlock(targetPath, DOC_MARKER_START, DOC_MARKER_END, block);
|
|
203
191
|
}
|
|
204
192
|
|
|
205
193
|
// ─── Seam envelope builder ──────────────────────────────────────────────────
|
|
@@ -307,6 +307,8 @@ function _detectDefaultTrack2(projectDir, notes) {
|
|
|
307
307
|
|
|
308
308
|
plan.push({ id: 'logging-envelope', cmd: 'node', args: [path.join(__dirname, 'gsd-t-logging-envelope-check.cjs'), '--project', projectDir], timeoutMs: 30000 }); // M100 D3: structural trace+audit envelope gate, FAIL-CLOSED
|
|
309
309
|
|
|
310
|
+
plan.push({ id: 'env-registry', cmd: 'node', args: [path.join(__dirname, 'gsd-t-env-registry-check.cjs'), '--project', projectDir], timeoutMs: 30000 }); // M102 D3: no-secret-in-registry + rule-without-table gate, FAIL-CLOSED
|
|
311
|
+
|
|
310
312
|
// secrets — gitleaks (PATH detection deferred to runtime)
|
|
311
313
|
if (_hasOnPath('gitleaks')) {
|
|
312
314
|
plan.push({
|
package/bin/gsd-t.js
CHANGED
|
@@ -2999,6 +2999,16 @@ const PROJECT_BIN_TOOLS = [
|
|
|
2999
2999
|
// ship to every project or the consumers can't classify. Same propagation class as
|
|
3000
3000
|
// gsd-t-graph-store-resolver.cjs above. [[project_global_bin_propagation_gap]]
|
|
3001
3001
|
"gsd-t-graph-availability.cjs",
|
|
3002
|
+
// M102 — Environment Registry. The shared marker-block doc-writer + the
|
|
3003
|
+
// registry helper (record/lookup/detect/add-permission/ensure-gitignore).
|
|
3004
|
+
// Sandboxed workflows (init/populate + provisioning execute/quick tasks) call
|
|
3005
|
+
// the registry via project-local Bash, and the registry `require`s the shared
|
|
3006
|
+
// writer at load — BOTH must ship or the registry throws / the writer is a
|
|
3007
|
+
// second copy. Same propagation class as the graph tools above.
|
|
3008
|
+
// [[project_global_bin_propagation_gap]]
|
|
3009
|
+
// Plus the verify-gate lint (gsd-t-verify-gate.cjs invokes it via __dirname,
|
|
3010
|
+
// so it must sit alongside the gate in the project's bin/).
|
|
3011
|
+
"gsd-t-doc-marker.cjs", "gsd-t-env-registry.cjs", "gsd-t-env-registry-check.cjs",
|
|
3002
3012
|
];
|
|
3003
3013
|
|
|
3004
3014
|
// Files that older versions of this installer copied into project bin/ but
|
|
@@ -85,6 +85,12 @@ The subagent works through the six stages IN ORDER. Each can kill or reshape the
|
|
|
85
85
|
you can cite a confirmed reproducible case only a fallback catches. The straight-line process
|
|
86
86
|
that produces the result is the goal; where it can fail, prefer a **HALT** (stop + demand
|
|
87
87
|
fix), which is NOT a fallback. (See CLAUDE.md § No-Fallback-Ever Doctrine.)
|
|
88
|
+
7. **SIMPLY-STATED** (clarity gate — the review is NOT done until this passes) — state every
|
|
89
|
+
finding and the verdict SIMPLY: precise and complete, but every word load-bearing, the logic
|
|
90
|
+
in a straight line, ZERO jargon standing in for a clear idea, no nested clauses hiding a
|
|
91
|
+
tangle. If a finding cannot be stated simply, the thinking on it is not finished — RE-THINK
|
|
92
|
+
the muddled part, do not re-word it. "Too sophisticated to simplify" is a BANNED escape hatch
|
|
93
|
+
(simplify the expression, never the idea). (See CLAUDE.md § Simply Stated Doctrine.)
|
|
88
94
|
|
|
89
95
|
A stage the subagent cannot answer with evidence is a HALT — surface it as an open question for
|
|
90
96
|
the user, do not paper over it with a guess.
|
|
@@ -100,6 +106,10 @@ a summary table, near-zero preamble. For each CURRENT block, say **why it does w
|
|
|
100
106
|
successive fixes). Unless `--chat-only`, write it to `.gsd-t/pseudocode/PseudoCode-<Target>.md`.
|
|
101
107
|
|
|
102
108
|
**B — Session summary** (always printed, even under `--build`):
|
|
109
|
+
- **Simply Stated** (REQUIRED FIRST LINE — the clarity gate) — the verdict + the single most
|
|
110
|
+
important finding in ONE clean, jargon-free, straight-line statement a smart non-specialist
|
|
111
|
+
acts on without re-reading. If you cannot write this line cleanly, the review is NOT done —
|
|
112
|
+
re-think, don't re-word. Everything below is the depth for whoever wants it.
|
|
103
113
|
- **Core objective** (one line)
|
|
104
114
|
- **Is this a "complicated over time" issue?** (yes/no + the accretion history if yes)
|
|
105
115
|
- **What's reusable** (process or a stored output already available)
|
package/commands/gsd-t-init.md
CHANGED
|
@@ -296,6 +296,12 @@ docs/
|
|
|
296
296
|
|
|
297
297
|
These are the living documents that persist across milestones and keep institutional knowledge alive. The `infrastructure.md` is especially important — it captures the exact commands for provisioning cloud resources, setting up databases, managing secrets, and deploying, so this knowledge doesn't get lost between sessions.
|
|
298
298
|
|
|
299
|
+
The `infrastructure.md` template ships with a marker-delimited `## Environments` table (`<!-- gsd-t-env-registry:start -->` / `:end`) — the committed, **secret-free** connection registry (M102). It is created empty by init and populated by the two triggers:
|
|
300
|
+
- **Greenfield (record-at-create):** whenever this project later BUILDS/PROVISIONS/CONFIGURES an environment (a local test DB, a remote server, credentials, or hands the user setup instructions), record the map row in the SAME pass:
|
|
301
|
+
`node bin/gsd-t-env-registry.cjs record '{"projectDir":".","scope":"local","kind":"postgres","host":"localhost","port":"5432","name":"appdb","authMethod":"password","secretVault":"local (.env)","secretEnvVarName":"DATABASE_URL","fetchCommand":"read $DATABASE_URL from .env","connectCommand":"psql \"$DATABASE_URL\""}'`
|
|
302
|
+
Records only the MAP + vault pointer + env-var NAME + fetch/connect commands — **never a secret VALUE** (a literal password/token in a command is REJECTED). `scope=prod` defaults `read-only=YES`.
|
|
303
|
+
- Also run `node bin/gsd-t-env-registry.cjs ensure-gitignore .` so `.env` is gitignored (auto-add + report; secret-leak precheck).
|
|
304
|
+
|
|
299
305
|
## Step 8: Ensure README.md Exists
|
|
300
306
|
|
|
301
307
|
If no `README.md` exists, create one with:
|
|
@@ -29,6 +29,11 @@ Scan this codebase and populate the GSD-T documentation. Analyze the actual code
|
|
|
29
29
|
- List Credentials and Secrets from .env.example (local) and any secret manager configs
|
|
30
30
|
- Document Deployment from CI/CD configs, Dockerfiles, cloud configs
|
|
31
31
|
- Document Logging and Monitoring from any logging setup or dashboard configs
|
|
32
|
+
- **Back-fill the `## Environments` registry (M102 — brownfield capture).** For an existing project whose non-local environments are undocumented, back-fill the map-only Environments table WITHOUT ever reading or writing a secret VALUE:
|
|
33
|
+
1. `node bin/gsd-t-env-registry.cjs detect .` → proposes the vault + fetch command from `.env.example` var NAMES, connection-string SHAPES, and `vercel.json`/`.vercel` (Vercel) · `cloudbuild.yaml`/`.gcloudignore` (Google Secret Manager) · neon dep (Neon) · plain `.env` (local). Map only — never a value.
|
|
34
|
+
2. Confirm/fill gaps with the human, then `node bin/gsd-t-env-registry.cjs record '<json>'` per environment (upsert by `(scope, kind)` — a re-provision replaces the stale row).
|
|
35
|
+
3. `node bin/gsd-t-env-registry.cjs ensure-gitignore .` — auto-add `.env` to `.gitignore` if missing + report.
|
|
36
|
+
- **No-Fallback-Ever:** a genuinely undocumented environment reached for the first time HALTs → detect → ask → record → `add-permission` → proceed. NEVER guess a connection string, NEVER grep transcripts to rediscover.
|
|
32
37
|
|
|
33
38
|
## Graph Structural Slice — who-imports + cluster (M94-D10)
|
|
34
39
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.10",
|
|
4
4
|
"description": "GSD-T: Contract-Driven Development for Claude Code — 54 slash commands with headless-by-default workflow spawning, unattended supervisor relay with event stream, graph-powered code analysis, real-time agent dashboard, task telemetry, doc-ripple enforcement, backlog management, impact analysis, test sync, milestone archival, and PRD generation",
|
|
5
5
|
"author": "Tekyz, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,11 @@ const REMINDER =
|
|
|
45
45
|
"(catch-and-continue, || default, silent degrade, try-X-else-Y) — STOP and ask the " +
|
|
46
46
|
"user first, unless you can cite a confirmed reproducible case only a fallback catches. " +
|
|
47
47
|
"A HALT (stop + demand fix) is NOT a fallback and is the preferred move. " +
|
|
48
|
-
"
|
|
48
|
+
"SIMPLY-STATED: before presenting an architecture/plan/finding, state it SIMPLY — every " +
|
|
49
|
+
"word load-bearing, logic in a straight line, no jargon or nested clauses. If you can't " +
|
|
50
|
+
"state it cleanly the thinking isn't done (muddle in the words = muddle in the design) — " +
|
|
51
|
+
"RE-THINK, don't re-word. 'Too sophisticated to simplify' is a banned escape hatch. " +
|
|
52
|
+
"See ~/.claude/CLAUDE.md § Architect's Oversight + No-Fallback-Ever + Simply Stated Doctrines.";
|
|
49
53
|
|
|
50
54
|
// File extensions that are PROSE/config, not code — skip the reminder for these.
|
|
51
55
|
// (The doctrine's own artifacts are markdown; reminding while authoring them is noise.)
|
|
@@ -99,6 +99,26 @@ NEED TO UNDERSTAND SOMETHING?
|
|
|
99
99
|
└── Not documented? → Research, then DOCUMENT IT
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
### Environment Access — read-first, HALT-and-document (M102)
|
|
103
|
+
|
|
104
|
+
**EXTENDS No-Re-Research; obeys No-Fallback-Ever.** Before reaching ANY non-local environment (staging/prod DB, remote server, hosted API, SSH host), the connection MAP is read from the committed `## Environments` table in `docs/infrastructure.md` — NEVER re-discovered.
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
NEED TO REACH A NON-LOCAL ENVIRONMENT?
|
|
108
|
+
├── lookupEnvironment(scope, kind) FIRST → bin/gsd-t-env-registry.cjs lookup
|
|
109
|
+
├── Row exists → use its connect command; the secret is pulled at runtime via
|
|
110
|
+
│ the recorded env-var NAME from its vault (never a value in the doc).
|
|
111
|
+
│ scope=prod + read-only=YES + a WRITE op → HALT, confirm with human.
|
|
112
|
+
└── Row MISSING → HALT the connect. Do NOT guess a connection string. Do NOT grep
|
|
113
|
+
transcripts to rediscover. The brownfield path:
|
|
114
|
+
detectEnvConfig → ask human to confirm/fill → recordEnvironment
|
|
115
|
+
→ addPermissionEntry → THEN proceed. First need = last rediscovery.
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- The registry stores only the MAP + which vault holds the secret + the env-var NAME + the fetch/connect commands — **never a secret VALUE**.
|
|
119
|
+
- **Zero fallback.** A missing row resolves by DOCUMENTING (detect → ask → record → proceed), never by a guessed connstring or a transcript re-grep. That HALT-then-document is NOT a fallback.
|
|
120
|
+
- Greenfield: when GSD-T builds/provisions an env, it records the row in the SAME pass (`recordEnvironment`) — so staleness self-heals (a re-provision upserts by `(scope, kind)` and replaces the stale row).
|
|
121
|
+
|
|
102
122
|
|
|
103
123
|
# Versioning
|
|
104
124
|
|
|
@@ -473,6 +493,18 @@ See memory pointer: `feedback_auto_research_external_gaps`.
|
|
|
473
493
|
|
|
474
494
|
**§Enforcement (three layers):** (1) this doctrine = the definition; (2) the **Architect's Oversight Write/Edit trigger is extended** to challenge every fallback at the build moment ("is this a fallback? name the proven case or ask"); (3) mechanical guards where they already exist — the graph consumers' `try→catch→grep` fallback is caught by `bin/gsd-t-graph-anti-grep-lint.cjs` (reused, not duplicated); a general static "detect every fallback" lint is deliberately NOT built (it can't judge *proven-necessary* — that is the human/architect ask, which is layer 2).
|
|
475
495
|
|
|
496
|
+
### Simply Stated Doctrine (governed, enforced — clarity as a defect gate, not a writing-style reminder)
|
|
497
|
+
|
|
498
|
+
**Before GSD-T presents an architecture, plan, finding, review, or milestone definition, it must first state it *simply* — precise and complete, every word earning its place, the logic in a straight line, with no jargon standing in for a clear idea and no nested clauses hiding a tangle. If it cannot be stated simply, the thinking is NOT finished: the muddle in the words IS a muddle in the design. It goes back to be RE-THOUGHT, not re-worded.** The simply-stated version leads the deliverable; the technical depth sits below it for whoever wants it.
|
|
499
|
+
|
|
500
|
+
**This is a DIFFERENT mechanism from the Reader Contract / jargon lint.** Those treat verbosity as an OUTPUT-polish problem — trim the prose after the thinking is done — and repeatedly fail because a reminder loses to "but this case needs the detail" under load. Simply Stated treats verbosity as what it is: **a symptom of unclear thinking, and the same unclear thinking ships the bugs.** If you cannot express it cleanly, you do not yet understand it — and not-understanding is where defects come from. The inability to state it simply is a DEFECT SIGNAL, gated like the No-Fallback HALT, not a soft nudge.
|
|
501
|
+
|
|
502
|
+
**"Simply" is NOT "dumbed down."** The content stays as sophisticated as the problem demands — sophisticated IN its simplicity and conciseness (simplify the *expression*, never the *idea*). The test is NOT "could a novice understand the topic." The test is: **is every word load-bearing, and is the logic a straight line?** "This topic is too sophisticated to state simply" is the banned escape hatch.
|
|
503
|
+
|
|
504
|
+
**The HALT (the teeth):** a deliverable whose simply-stated lead cannot be written cleanly is NOT done — stop, surface it, re-think the muddled part. Do not ship the verbose version with an apology; the verbose version is evidence the design has a gap.
|
|
505
|
+
|
|
506
|
+
**§Enforcement (three layers):** (1) this doctrine = the definition; (2) the **Architect's Oversight Write/Edit trigger** carries a Simply-Stated line; (3) the **plan/milestone Six-Stage Pass + `/gsd-t-architect` gain a Simply-Stated gate** — the pass does not complete until its finding/plan has a clean simply-stated lead, and the validation protocols flag a deliverable that lacks one. The simply-stated lead is a REQUIRED ARTIFACT (like the pseudocode file), not advice.
|
|
507
|
+
|
|
476
508
|
### Phase Flow
|
|
477
509
|
- Upon completing a phase, automatically proceed to the next phase
|
|
478
510
|
- ONLY run Discussion phase if truly required (clear path → skip to Plan)
|
|
@@ -68,6 +68,46 @@ cp .env.example .env
|
|
|
68
68
|
{command}
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
<!-- gsd-t-env-registry:start -->
|
|
72
|
+
## Environments
|
|
73
|
+
|
|
74
|
+
> **Map, not secrets.** This table records only WHERE an environment lives and
|
|
75
|
+
> HOW to reach it — host/port/name/auth-method/which-vault-holds-the-secret/the
|
|
76
|
+
> env-var NAME/the fetch + connect commands. It NEVER stores a secret VALUE.
|
|
77
|
+
> The value lives in the vault (`.env` / Vercel / Neon / Google Secret Manager)
|
|
78
|
+
> and is pulled at runtime via the env-var NAME. A missing row → HALT and
|
|
79
|
+
> document (detect → ask → record → proceed); never guess a connection string,
|
|
80
|
+
> never grep transcripts to rediscover.
|
|
81
|
+
|
|
82
|
+
| id | scope | kind | host | port | db/name | auth method | secret vault | secret env-var NAME | fetch command | connect command | access gotchas | read-only default | recorded |
|
|
83
|
+
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
84
|
+
<!-- gsd-t-env-registry:end -->
|
|
85
|
+
|
|
86
|
+
<!--
|
|
87
|
+
Column notes:
|
|
88
|
+
scope = local | staging | prod
|
|
89
|
+
kind = postgres | mysql | redis | http-api | ssh-host | ...
|
|
90
|
+
secret vault = local (.env) | Vercel | Neon | Google Secret Manager | ...
|
|
91
|
+
secret env-var NAME = e.g. DATABASE_URL_PROD — the NAME only, NEVER the value
|
|
92
|
+
host = POSITIVE shape: localhost | IPv4 | dotted DNS hostname |
|
|
93
|
+
short lowercase service name (letters+hyphen, no digits, e.g. db, postgres)
|
|
94
|
+
db/name = short lowercase snake identifier (<=16, e.g. binvoice_prod, analytics)
|
|
95
|
+
auth method = ENUMERATED — password | iam | oauth | oauth2 | service-account |
|
|
96
|
+
ssh-key | api-key | none | scram-sha-256 | md5 | trust | token | key | ...
|
|
97
|
+
fetch command = how to pull the secret from its vault (e.g. `vercel env pull`)
|
|
98
|
+
POSITIVE allowlist: a token is accepted ONLY if it IS a $VAR/${VAR} ref,
|
|
99
|
+
a flag, a hostname/IP, a .env dotfile, or a curated CLI/db word.
|
|
100
|
+
Any OTHER bare literal is REJECTED (move it to an env var, reference as $VAR).
|
|
101
|
+
connect command = references the env-var by name: `psql "$DATABASE_URL_PROD"` (same allowlist)
|
|
102
|
+
access gotchas = ENUMERATED — vpn | ip-allowlist | ssh-tunnel | bastion | none,
|
|
103
|
+
optionally `via <hostname>` (e.g. `ssh-tunnel via bastion.example.com`).
|
|
104
|
+
Free prose is FORBIDDEN (nowhere for a secret to hide).
|
|
105
|
+
read-only default = YES for scope=prod unless a human explicitly recorded write-ok
|
|
106
|
+
There is deliberately NO secret-value column — a row is structurally incapable
|
|
107
|
+
of holding a secret. Populated by `gsd-t-env-registry` (record-at-create +
|
|
108
|
+
capture-on-first-need).
|
|
109
|
+
-->
|
|
110
|
+
|
|
71
111
|
## Credentials & Secrets
|
|
72
112
|
|
|
73
113
|
### Local (.env)
|
|
@@ -934,6 +934,13 @@ const _architectPassLine = _ARCHITECT_PHASES.has(phaseName)
|
|
|
934
934
|
` it in — UNLESS you can cite a confirmed reproducible case only a fallback catches. The`,
|
|
935
935
|
` straight-line process that produces the result is the goal; where it can fail, prefer a`,
|
|
936
936
|
` HALT (stop + demand fix), which is NOT a fallback. Fallbacks are rare and ask-first.`,
|
|
937
|
+
` 7. SIMPLY-STATED (clarity gate — the plan is NOT done until this passes) — write the ${phaseName}`,
|
|
938
|
+
` as a SIMPLY-STATED lead FIRST: precise and complete, but every word load-bearing, the logic`,
|
|
939
|
+
` in a straight line, ZERO jargon standing in for a clear idea, no nested clauses hiding a`,
|
|
940
|
+
` tangle. If you CANNOT state it simply, the thinking is not finished — the muddle in the`,
|
|
941
|
+
` words is a muddle in the design; RE-THINK the muddled part, do not re-word it. "Too`,
|
|
942
|
+
` sophisticated to simplify" is a BANNED escape hatch (simplify the expression, never the`,
|
|
943
|
+
` idea). This simply-stated lead heads the deliverable; the depth sits below it.`,
|
|
937
944
|
`A stage you cannot answer with evidence HALTS the plan (needs-human) — do not proceed on a hunch.`,
|
|
938
945
|
].join("\n")
|
|
939
946
|
: "";
|