@tekyzinc/gsd-t 5.1.12 → 5.2.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 +31 -0
- package/README.md +1 -1
- package/bin/gsd-t-doc-marker.cjs +65 -0
- package/bin/gsd-t-env-registry-check.cjs +449 -0
- package/bin/gsd-t-env-registry.cjs +1384 -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-init.md +6 -0
- package/commands/gsd-t-populate.md +7 -0
- package/package.json +1 -1
- package/scripts/gsd-t-auto-route.js +1 -0
- package/templates/CLAUDE-global.md +14 -1
- package/templates/infrastructure.md +40 -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
|
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,13 @@ 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.
|
|
37
|
+
- **Leak remediation (secret found in a URL/command):** before recording, run `node bin/gsd-t-env-registry.cjs propose-remediation . '<command>'`. If it reports `needed:true`, STOP and tell the user *what* leaked (`detectedSecret`), then ASK **rotate or move** (`choices.rotate` / `choices.move`) — each time, do not assume. On their answer: store the (rotated or existing) secret into the detected `vault` (directly where possible — e.g. `vercel env add`, write to `.env`; else instruct the user to create/rotate it), then record the row with the `rewrittenCommand` ($VAR form). NEVER commit the literal secret.
|
|
38
|
+
- **Local-literal switch (per project):** by default every row uses `$VAR` (strict). A project may opt in via `.gsd-t/env-registry-config.json` → `{"allowLocalLiteral": true}` to allow a LITERAL secret in `scope=local` rows only (testing convenience). `staging`/`prod` are ALWAYS strict. A relaxed local write returns a one-time "committed file → git history" warning — surface it.
|
|
32
39
|
|
|
33
40
|
## Graph Structural Slice — who-imports + cluster (M94-D10)
|
|
34
41
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.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",
|
|
@@ -32,6 +32,7 @@ const path = require("path");
|
|
|
32
32
|
const READER_CONTRACT = [
|
|
33
33
|
"[GSD-T READER CONTRACT] Before sending ANY reply, assume your first draft is too wordy and rewrite it tight. Rules:",
|
|
34
34
|
"• Answer FIRST. No preamble, no restating the question, no narrating what you're about to do (\"let me check…\"). Do the work silently, then give the result.",
|
|
35
|
+
"• NO PREAMBLES — start with the answer, never a framing phrase. BANNED openers (and anything like them): \"One thing I owe you honestly:\", \"To be honest\", \"Here's the thing\", \"The honest truth is\", \"I'll be straight with you\", \"Let me level with you\", \"Full transparency:\", \"Real talk\", \"What's worth noting here\", \"The key insight is\", \"Here's what's happening\". Delete the opener and lead with the actual point — if a sentence only announces that a point is coming, cut it.",
|
|
35
36
|
"• Exception — when you're about to CHANGE code/files: state intent in one line first, so the user can stop a wrong direction.",
|
|
36
37
|
"• Gloss every code/jargon term in plain words on first use. No bare IDs or acronyms the reader must decode.",
|
|
37
38
|
"• Bullets/tables over paragraphs. Cut hedging and meta-commentary. Expand only if asked.",
|
|
@@ -96,9 +96,22 @@ NEED TO UNDERSTAND SOMETHING?
|
|
|
96
96
|
├── Is it about what to build? → Read docs/requirements.md
|
|
97
97
|
├── Is it about how to deploy/operate? → Read docs/infrastructure.md
|
|
98
98
|
├── Is it about domain interfaces? → Read .gsd-t/contracts/
|
|
99
|
+
├── Is it about reaching a non-local environment (DB/server URL, creds, perms)? → Read the `## Environments` registry in docs/infrastructure.md
|
|
99
100
|
└── Not documented? → Research, then DOCUMENT IT
|
|
100
101
|
```
|
|
101
102
|
|
|
103
|
+
## Environment Access — read-first, HALT-and-document (M102)
|
|
104
|
+
|
|
105
|
+
**Before reaching ANY non-local environment (a staging/prod DB, a remote server, an external service), READ the `## Environments` registry in `docs/infrastructure.md` FIRST.** It is the committed, **secret-free** map of every environment — host, port, db, auth method, which vault holds the secret, the env-var NAME, and the connect command (secrets referenced as `$VAR`, never as a literal value).
|
|
106
|
+
|
|
107
|
+
- **On a HIT** — use the recorded connect command; the secret is pulled from its vault at runtime via the env-var NAME.
|
|
108
|
+
- **On a MISS (no row) — HALT and document. NEVER guess a connection string, NEVER grep transcripts to rediscover it** (No-Fallback-Ever). Then: `detect` the env → ask the human to confirm/fill → `record` the map row → `add-permission` (broad-glob) → proceed. The registry self-heals staleness because a re-provision upserts by `(scope, kind)`.
|
|
109
|
+
- **Record-at-create (greenfield):** whenever GSD-T BUILDS/PROVISIONS an environment, record its map row in the SAME pass (it has the URL + creds right then).
|
|
110
|
+
- **Never write a secret VALUE into the registry** — only the vault name + env-var NAME + a `$VAR`-referencing command. A literal secret is rejected by `recordEnvironment` and by the `gsd-t-verify` env-registry gate. If a secret is found in a URL/command, the capture flow OFFERS to move it into the vault (rotate-or-move, user's choice) and replace it with a `$VAR`.
|
|
111
|
+
- **Local-literal switch:** a project may opt in via `.gsd-t/env-registry-config.json` `{"allowLocalLiteral": true}` to allow a literal secret in `scope=local` rows only; `staging`/`prod` stay strict.
|
|
112
|
+
|
|
113
|
+
Details: `.gsd-t/contracts/env-registry-contract.md`.
|
|
114
|
+
|
|
102
115
|
|
|
103
116
|
|
|
104
117
|
# Versioning
|
|
@@ -484,7 +497,7 @@ See memory pointer: `feedback_auto_research_external_gaps`.
|
|
|
484
497
|
|
|
485
498
|
**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.
|
|
486
499
|
|
|
487
|
-
**§Applies to conversational narration too, not just formal deliverables.** The doctrine governs REPLIES and mid-work talk-to-the-user, not only architectures/plans/findings. Two extra rules there: **NO
|
|
500
|
+
**§Applies to conversational narration too, not just formal deliverables.** The doctrine governs REPLIES and mid-work talk-to-the-user, not only architectures/plans/findings. Two extra rules there: **NO PREAMBLES** (no throat-clearing, no "let me…", no narrating-the-explanation like "it matters that I say why…" — give the point, not a description of the point; BANNED openers and anything like them: "One thing I owe you honestly:", "To be honest", "Here's the thing", "The honest truth is", "I'll be straight with you", "Full transparency:", "What's worth noting", "The key insight is" — delete the opener, lead with the point), and **load-bearing point FIRST** (never buried after justification clauses). No clever phrase that obscures where a plain one is clearer.
|
|
488
501
|
|
|
489
502
|
**§Enforcement (four 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 (a REQUIRED ARTIFACT like the pseudocode file, not advice); (4) the **every-turn Reader Contract** (injected by the UserPromptSubmit hook `scripts/gsd-t-auto-route.js`) carries the Simply-Stated rule + a before/after example, so it governs conversational output each reply — the channel that reaches mid-work narration, which layers 2-3 do not.
|
|
490
503
|
|
|
@@ -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)
|