@tekyzinc/gsd-t 4.20.11 → 5.0.11

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 CHANGED
@@ -2,6 +2,27 @@
2
2
 
3
3
  All notable changes to GSD-T are documented here. Updated with each release.
4
4
 
5
+ ## [5.0.11] - 2026-07-12
6
+
7
+ ### Fixed — architect hook now ships + installs; de-flaked its test
8
+
9
+ The M101/v5.0.10 architect-oversight hook existed only in `~/.claude/scripts/` — it was never copied into the repo, so `install`/`update-all` would not propagate it to other machines (the "propagate a tool without shipping it = silent no-op" class). This release ships it properly and hardens it.
10
+
11
+ - `scripts/gsd-t-architect-oversight-guard.js`: the hook now lives in the repo (ships with the package). Added an unref'd 1500ms watchdog + a stdin `error` handler so the hook can NEVER hang and block a Write/Edit (fail-open guaranteed even if stdin never EOFs).
12
+ - `bin/gsd-t.js`: `configureArchitectHook()` registers the PreToolUse `Write|Edit` hook via the global-package command pattern (same as the graph/read-intercept hooks); wired into `install`; removed on `uninstall`. Exported for tests.
13
+ - `test/m101-architect-oversight-hook.test.js`: replaced the two spawn-per-assertion fail-open tests (which false-failed under full-suite CPU starvation, not a hook bug) with a source-guard assertion + installer idempotency/coexistence tests. Test resolves the repo hook copy.
14
+ - `.gsd-t/contracts/graph-metrics-contract.md`: refreshed the `doMetrics` line citation (shifted by the installer addition).
15
+
16
+ ## [5.0.10] - 2026-07-12
17
+
18
+ ### Added — `/gsd-t-architect`: run the Architect's Oversight pass on demand
19
+
20
+ A standalone command that runs the M101 Six-Stage Pass (Objective → Conflict → Reuse → Simplicity → Reuse-forecast → Risk) on **existing** work — an already-frozen plan, a messy subsystem, or a pasted tangle of problems — and produces the simplest correct solution as plain-English pseudocode, plus the traps each stage surfaces and what's already reusable. Major bump: this is the on-demand front door to design-first, reuse-first development, not an incremental command. Where the plan/milestone workflow runs the pass *while generating* a plan, this runs it as a standalone audit over work that already exists.
21
+
22
+ - `commands/gsd-t-architect.md`: prose-driven command — spawns one opus analysis subagent, reasons from the code graph (reuse/duplication) and real code, not memory. Default = plan-only then offers to build; `--build` auto-builds the simplest fix; `--chat-only` skips writing a file. Always prints a findings + plan summary to the session, even in auto-build. Reuses the existing prose-command + Task-subagent pattern (`/gsd-t-status`, `/gsd-t-impact`) — no new workflow file (the doctrine obeying its own Stage 3/4).
23
+ - `README.md` + `commands/gsd-t-help.md`: command documented.
24
+ - `test/filesystem.test.js`: command counts 54→55 / 48→49.
25
+
5
26
  ## [4.20.11] - 2026-07-12
6
27
 
7
28
  ### Added — M101: Architect's Oversight Doctrine
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # GSD-T: Contract-Driven Development for Claude Code
2
2
 
3
- **v4.20.11** - A methodology for reliable, parallelizable development using Claude Code with optional Agent Teams support.
3
+ **v5.0.11** - A methodology for reliable, parallelizable development using Claude Code with optional Agent Teams support.
4
4
 
5
5
  **Eliminates context rot** — task-level fresh dispatch (one subagent per task, ~10-20% context each) means compaction never triggers.
6
6
  **Compaction-proof debug loops** — `gsd-t headless --debug-loop` runs test-fix-retest cycles as separate `claude -p` sessions. A JSONL debug ledger persists all hypothesis/fix/learning history across fresh sessions. Anti-repetition preamble injection prevents retrying failed hypotheses. Escalation tiers (sonnet → opus → human) and a hard iteration ceiling enforced externally.
@@ -200,6 +200,7 @@ This will replace changed command files, back up your CLAUDE.md if customized, a
200
200
  | `/gsd-t-partition` | Decompose into domains + contracts | In wave |
201
201
  | `/gsd-t-plan` | Create atomic task lists per domain (tasks auto-split to fit one context window) | In wave |
202
202
  | `/gsd-t-impact` | Analyze downstream effects | In wave |
203
+ | `/gsd-t-architect` | Run the Architect's Oversight Six-Stage Pass on existing work — finds the simplest solution + reuse + traps, as plain-English pseudocode (plan-only; `--build` to auto-build) | Manual |
203
204
  | `/gsd-t-execute` | Run tasks — task-level fresh dispatch, worktree isolation, adaptive replanning | In wave |
204
205
  | `/gsd-t-test-sync` | Sync tests with code changes | In wave |
205
206
  | `/gsd-t-qa` | QA agent — test generation, execution, gap reporting | Auto-spawned |
package/bin/gsd-t.js CHANGED
@@ -485,6 +485,15 @@ const READ_INTERCEPT_HOOK_MARKER = "gsd-t-read-intercept";
485
485
  const READ_INTERCEPT_HOOK_COMMAND =
486
486
  'bash -c \'[ -f "$(npm root -g)/@tekyzinc/gsd-t/scripts/gsd-t-read-intercept.js" ] && node "$(npm root -g)/@tekyzinc/gsd-t/scripts/gsd-t-read-intercept.js" || true\'';
487
487
 
488
+ // M101 — architect-oversight PreToolUse hook on Write|Edit. Same global-package
489
+ // pattern as the intercept hooks: runs the script from the globally-installed
490
+ // package, and the script fails-open (no-op) in non-GSD-T projects / prose writes,
491
+ // so it is safe to register globally with a Write|Edit matcher. It reminds the model
492
+ // to run the Architect's Oversight Six-Stage Pass before writing code (M101).
493
+ const ARCHITECT_HOOK_MARKER = "gsd-t-architect-oversight-guard";
494
+ const ARCHITECT_HOOK_COMMAND =
495
+ 'bash -c \'[ -f "$(npm root -g)/@tekyzinc/gsd-t/scripts/gsd-t-architect-oversight-guard.js" ] && node "$(npm root -g)/@tekyzinc/gsd-t/scripts/gsd-t-architect-oversight-guard.js" || true\'';
496
+
488
497
  // Append entries to {projectDir}/.gitignore. Each entry added only if absent.
489
498
  // Idempotent. Returns true if any entries were added, false otherwise.
490
499
  function ensureGitignoreEntries(projectDir, entries) {
@@ -928,6 +937,60 @@ function configureReadInterceptHook(settingsPath) {
928
937
  return { installed: true, action };
929
938
  }
930
939
 
940
+ // M101 — register the architect-oversight PreToolUse hook (matcher "Write|Edit").
941
+ // Idempotent: find-by-marker, refresh stale command, else add. Mirrors the M98
942
+ // read-intercept installer but on PreToolUse. The script fails-open so this is
943
+ // safe globally (silent in non-GSD-T projects and on prose/doc writes).
944
+ function configureArchitectHook(settingsPath) {
945
+ const targetPath = settingsPath || SETTINGS_JSON;
946
+ let settings = {};
947
+ if (fs.existsSync(targetPath)) {
948
+ try {
949
+ settings = JSON.parse(fs.readFileSync(targetPath, "utf8"));
950
+ if (!settings || typeof settings !== "object") settings = {};
951
+ } catch {
952
+ warn("settings.json has invalid JSON — cannot configure architect hook");
953
+ return { installed: false, action: "noop" };
954
+ }
955
+ }
956
+ if (!settings.hooks) settings.hooks = {};
957
+ if (!Array.isArray(settings.hooks.PreToolUse)) settings.hooks.PreToolUse = [];
958
+
959
+ const cmd = ARCHITECT_HOOK_COMMAND;
960
+ let action = "noop";
961
+ let found = false;
962
+ for (const entry of settings.hooks.PreToolUse) {
963
+ if (!entry || !Array.isArray(entry.hooks)) continue;
964
+ for (const h of entry.hooks) {
965
+ if (!h || typeof h.command !== "string") continue;
966
+ if (h.command === cmd || h.command.includes(ARCHITECT_HOOK_MARKER)) {
967
+ found = true;
968
+ if (h.command !== cmd) { h.command = cmd; action = "updated"; }
969
+ if (entry.matcher !== "Write|Edit") { entry.matcher = "Write|Edit"; action = action === "noop" ? "updated" : action; }
970
+ }
971
+ }
972
+ }
973
+ if (!found) {
974
+ settings.hooks.PreToolUse.push({
975
+ matcher: "Write|Edit",
976
+ hooks: [{ type: "command", command: cmd }],
977
+ });
978
+ action = "added";
979
+ }
980
+ if (action === "noop") return { installed: true, action: "noop" };
981
+ if (isSymlink(targetPath)) {
982
+ warn("Skipping settings.json write — target is a symlink");
983
+ return { installed: false, action: "noop" };
984
+ }
985
+ try {
986
+ fs.writeFileSync(targetPath, JSON.stringify(settings, null, 2));
987
+ } catch (e) {
988
+ warn(`Failed to write settings.json: ${e.message}`);
989
+ return { installed: false, action: "noop" };
990
+ }
991
+ return { installed: true, action };
992
+ }
993
+
931
994
  // M98 — remove the GSD-T intercept PostToolUse hooks (grep + read) from settings.json.
932
995
  // Used during uninstall. Marker-based; leaves all other hooks intact. Idempotent.
933
996
  function removeInterceptHooks(settingsPath) {
@@ -941,17 +1004,28 @@ function removeInterceptHooks(settingsPath) {
941
1004
  warn("settings.json has invalid JSON — cannot remove intercept hooks");
942
1005
  return false;
943
1006
  }
944
- if (!settings.hooks || !Array.isArray(settings.hooks.PostToolUse)) return false;
1007
+ if (!settings.hooks) return false;
945
1008
 
946
- const markers = [GRAPH_INTERCEPT_HOOK_MARKER, READ_INTERCEPT_HOOK_MARKER];
947
- const before = settings.hooks.PostToolUse.length;
948
- settings.hooks.PostToolUse = settings.hooks.PostToolUse.filter((entry) => {
949
- if (!entry || !Array.isArray(entry.hooks)) return true;
950
- return !entry.hooks.some(
951
- (h) => h && typeof h.command === "string" && markers.some((m) => h.command.includes(m))
952
- );
953
- });
954
- if (before - settings.hooks.PostToolUse.length === 0) return false;
1009
+ const postMarkers = [GRAPH_INTERCEPT_HOOK_MARKER, READ_INTERCEPT_HOOK_MARKER];
1010
+ const preMarkers = [ARCHITECT_HOOK_MARKER];
1011
+ let removed = 0;
1012
+
1013
+ const stripByMarkers = (arr, markers) => {
1014
+ if (!Array.isArray(arr)) return arr;
1015
+ const before = arr.length;
1016
+ const out = arr.filter((entry) => {
1017
+ if (!entry || !Array.isArray(entry.hooks)) return true;
1018
+ return !entry.hooks.some(
1019
+ (h) => h && typeof h.command === "string" && markers.some((m) => h.command.includes(m))
1020
+ );
1021
+ });
1022
+ removed += before - out.length;
1023
+ return out;
1024
+ };
1025
+
1026
+ settings.hooks.PostToolUse = stripByMarkers(settings.hooks.PostToolUse, postMarkers);
1027
+ settings.hooks.PreToolUse = stripByMarkers(settings.hooks.PreToolUse, preMarkers);
1028
+ if (removed === 0) return false;
955
1029
 
956
1030
  if (isSymlink(targetPath)) {
957
1031
  warn("Skipping settings.json write — target is a symlink");
@@ -1915,6 +1989,13 @@ async function doInstall(opts = {}) {
1915
1989
  else info("Read-intercept hook already configured");
1916
1990
  }
1917
1991
 
1992
+ const archHook = configureArchitectHook(SETTINGS_JSON);
1993
+ if (archHook.installed) {
1994
+ if (archHook.action === "added") success("Architect-oversight hook added (Six-Stage Pass reminder before writing code — M101)");
1995
+ else if (archHook.action === "updated") success("Architect-oversight hook refreshed");
1996
+ else info("Architect-oversight hook already configured");
1997
+ }
1998
+
1918
1999
  heading("Graph Engine (CGC)");
1919
2000
  installCgc();
1920
2001
 
@@ -4909,6 +4990,7 @@ module.exports = {
4909
4990
  // M97/M98: intercept hook installers
4910
4991
  configureGraphInterceptHook,
4911
4992
  configureReadInterceptHook,
4993
+ configureArchitectHook,
4912
4994
  removeInterceptHooks,
4913
4995
  promptForApiKeyIfMissing,
4914
4996
  resolveApiKeyEnvVar,
@@ -0,0 +1,135 @@
1
+ # GSD-T: Architect — Run the Architect's Oversight Six-Stage Pass on Existing Work
2
+
3
+ You are the **architect**. You assess a plan, a subsystem, or a tangle of problems the user
4
+ points you at, and you find the **simplest correct solution** — reusing what already exists,
5
+ surfacing the traps, and writing the answer in plain-English pseudocode the user can approve
6
+ BEFORE any code is written.
7
+
8
+ This is a standalone, on-demand run of the **Architect's Oversight Doctrine** (see the project
9
+ `CLAUDE.md` / `~/.claude/CLAUDE.md` § Architect's Oversight, and
10
+ `.gsd-t/contracts/architects-oversight-contract.md`). Use it when a plan already exists (or is
11
+ half-formed) and you want it interrogated for simplicity + reuse before building.
12
+
13
+ Unlike the plan/milestone workflow (which runs the Six-Stage Pass *while generating* a plan),
14
+ this command runs it as a **standalone pass over existing work** — an already-frozen plan, a
15
+ messy subsystem, or a pasted description of problems.
16
+
17
+ ---
18
+
19
+ ## Argument Parsing
20
+
21
+ Parse `$ARGUMENTS`:
22
+ - **First positional** = `$TARGET` — what to assess. Either a plain-English description
23
+ ("the feed scanning stalls — image watcher, completeness, comment finder"), a file/dir path,
24
+ or "the current plan" (→ read `.gsd-t/domains/*/tasks.md` + `.gsd-t/progress.md` Current Milestone).
25
+ - **`--build`** — after producing the plan, AUTO-BUILD the simplest solution in the same run
26
+ (do not stop to ask). Default (no flag): produce the plan, then OFFER to build.
27
+ - **`--chat-only`** — report in the session only; do NOT write a pseudocode file to disk.
28
+ Default: write the pseudocode artifact to `.gsd-t/pseudocode/`.
29
+
30
+ If `$TARGET` is empty, ask the user what to assess. Do not guess.
31
+
32
+ ---
33
+
34
+ ## Behavior contract (what the user asked for)
35
+
36
+ 1. **Default = plan, then offer to build.** Produce the assessment + plan, then end by asking
37
+ "Build the simplest solution now?" — unless `--build` was passed, in which case auto-build.
38
+ 2. **`--build` = auto-build after planning.** Assess → plan → implement the simplest solution,
39
+ without pausing — BUT still print the findings + plan summary to the session first.
40
+ 3. **ALWAYS print a session summary** — even in auto-build mode. The user scans the conversation
41
+ for anything that catches their eye while it keeps moving. Never silently proceed.
42
+
43
+ ---
44
+
45
+ ## Step 1: Launch the architect via a Task subagent
46
+
47
+ Give the assessment a fresh context window. Spawn ONE Task subagent (`model: opus`) — this is
48
+ high-stakes design judgment, top tier. Pass it the target and this protocol.
49
+
50
+ **Before spawning**, gather evidence sources so the subagent reasons from facts, not memory:
51
+ - If a code graph exists (`.gsd-t/graph.db`), the subagent uses `gsd-t graph` for reuse/caller
52
+ queries (Stage 3 + Stage 5 duplication check). If absent, it greps/reads and says so LOUDLY
53
+ (reuse-detection is reduced — never a silent "nothing found").
54
+ - Read the relevant code the target names (or the plan files).
55
+
56
+ ---
57
+
58
+ ## Step 2: The subagent runs the Six-Stage Pass (with EVIDENCE, never conviction)
59
+
60
+ The subagent works through the six stages IN ORDER. Each can kill or reshape the plan. Every
61
+ "am I sure?" is answered by looking (grep / Read / graph), not by asserting.
62
+
63
+ 1. **OBJECTIVE** — What is the core objective, in one plain sentence? Why is it the objective?
64
+ (Often the traps shrink once this is pinned down.)
65
+ 2. **CONFLICT** — Does solving this conflict with another objective? Must a previously-built
66
+ piece be re-examined/re-planned?
67
+ 3. **REUSE** — Have I already accomplished any piece of this? Can I reuse the **process**, or
68
+ the **output** (a value already computed/stored)? *Query the graph, not memory.* This is the
69
+ stage that kills redundant work (e.g. re-deriving a value already stored locally).
70
+ **Also — is this the SAME pattern repeated elsewhere?** If the target is one instance of a
71
+ bug, search for the other instances (one fix for a class beats N fixes for N instances).
72
+ 4. **SIMPLICITY** — What is the simplest, most efficient solution? Prove it's simplest.
73
+ 5. **REUSE FORECAST & DUPLICATION** — For anything new: HIGH reuse likelihood (core entity /
74
+ recurs / pure transform) → build clean + extractable; LOW → simplest inline. If a similar
75
+ thing exists: same JOB → extract a shared core (do NOT mutate the working original); if
76
+ stability forbids touching it → build new BUT register the duplication (a "reuse-candidate"
77
+ note) so it's visible, never a silent rogue twin.
78
+ 6. **RISK** — Does the proposed fix create security or stability/scalability risk? **And prove
79
+ the simpler fix actually works** — if the plan says "handled elsewhere / picked up later,"
80
+ VERIFY that's true; do not assert self-healing you haven't checked. (This is the trap that
81
+ hides inside a simplification.)
82
+
83
+ A stage the subagent cannot answer with evidence is a HALT — surface it as an open question for
84
+ the user, do not paper over it with a guess.
85
+
86
+ ---
87
+
88
+ ## Step 3: The subagent produces the output
89
+
90
+ **A — Plain-English pseudocode** (the artifact), in the house style — title + one-line purpose,
91
+ `CURRENT` (what it does today) and `PROPOSED` (the simplest fix) blocks, `# plain comment` inline,
92
+ a summary table, near-zero preamble. For each CURRENT block, say **why it does what it does now**
93
+ — and flag explicitly if it's a "got complicated over time" accretion (mechanisms stacked by
94
+ successive fixes). Unless `--chat-only`, write it to `.gsd-t/pseudocode/PseudoCode-<Target>.md`.
95
+
96
+ **B — Session summary** (always printed, even under `--build`):
97
+ - **Core objective** (one line)
98
+ - **Is this a "complicated over time" issue?** (yes/no + the accretion history if yes)
99
+ - **What's reusable** (process or a stored output already available)
100
+ - **Same pattern elsewhere?** (other instances of the bug/design found)
101
+ - **Simplest solution** (one paragraph)
102
+ - **Traps surfaced** (each stage's kill/risk finding — especially the Stage-6 "does the fix
103
+ really self-heal?" check)
104
+ - **Open questions** (any HALT stages needing the user)
105
+
106
+ ---
107
+
108
+ ## Step 4: Build decision
109
+
110
+ - **No `--build` flag (default):** end with the plain-English summary and ask:
111
+ *"Build the simplest solution now?"* — offer it, do not proceed.
112
+ - **`--build` flag:** proceed to implement the simplest solution immediately (still after
113
+ printing the summary). Follow the normal build discipline — smallest change that hits the
114
+ crux, Pre-Commit Gate, run affected tests. Report what was built.
115
+
116
+ ---
117
+
118
+ ## Document Ripple
119
+
120
+ The underlying assessment updates (when it writes / when a build follows):
121
+ - `.gsd-t/pseudocode/PseudoCode-<Target>.md` — the plain-English design (unless `--chat-only`).
122
+ - `.gsd-t/progress.md` Decision Log — one line recording the architect run + verdict.
123
+ - If `--build` produced code: the standard Pre-Commit Gate ripple (contracts, docs, tests) for
124
+ whatever the build touched.
125
+
126
+ ---
127
+
128
+ ## Notes
129
+
130
+ - **This command plans; it does not force a build.** The default keeps the architect a pure
131
+ "think before build" gate. `--build` is the explicit opt-in to act on its own conclusion.
132
+ - **Reuse over rebuild** (the doctrine obeying itself): this command is prose-driven and spawns
133
+ a single analysis subagent — it does NOT add a new workflow file. It reuses the existing
134
+ prose-command + Task-subagent pattern (like `/gsd-t-status`, `/gsd-t-impact`).
135
+ - Standalone command — no successor in the Next-Up map.
@@ -241,6 +241,13 @@ Use these when user asks for help on a specific command:
241
241
  - **Creates**: `.gsd-t/impact-report.md`
242
242
  - **Use when**: Before making changes, to understand what might break
243
243
 
244
+ ### architect
245
+ - **Summary**: Run the Architect's Oversight Six-Stage Pass (Objective → Conflict → Reuse → Simplicity → Reuse-forecast → Risk) on existing work — an already-frozen plan, a messy subsystem, or a pasted tangle of problems. Finds the simplest correct solution, what's already reusable (process or a stored value), and the traps each stage surfaces — written as plain-English pseudocode you can approve before any code.
246
+ - **Auto-invoked**: No (standalone, on-demand)
247
+ - **Args**: `/gsd-t-architect "<what to assess>"` — plus `--build` (auto-build the simplest fix after planning) and `--chat-only` (report in session, don't write a pseudocode file).
248
+ - **Creates**: `.gsd-t/pseudocode/PseudoCode-<Target>.md` (unless `--chat-only`)
249
+ - **Use when**: A plan already exists (or is half-formed) and you want it interrogated for simplicity + reuse before building; or a subsystem "got complicated over time" and you want the simplest version. Default is plan-only, then it offers to build.
250
+
244
251
  ### execute
245
252
  - **Summary**: Run tasks from plan, solo or with agent teams
246
253
  - **Auto-invoked**: Yes (in wave, after impact)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekyzinc/gsd-t",
3
- "version": "4.20.11",
3
+ "version": "5.0.11",
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",
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * GSD-T PreToolUse hook (Write|Edit) — Architect's Oversight trigger (M101).
4
+ *
5
+ * The Architect's Oversight Doctrine lives in ~/.claude/CLAUDE.md, but a doctrine
6
+ * carried in context can be MISSED under load. This hook fires the TRIGGER at the
7
+ * exact moment it matters — right before code is written/edited — and injects a
8
+ * ONE-LINE pointer back to the doctrine. It carries NO nuance (that stays in
9
+ * CLAUDE.md); it only guarantees the Six-Stage Pass is CONSIDERED before a build.
10
+ *
11
+ * Design (mirrors the doctrine's §Enforcement three-layer split):
12
+ * - CLAUDE.md = the definition (what the doctrine IS)
13
+ * - THIS hook = the trigger (don't forget it right now, no context bloat)
14
+ * - workflow = the execution (actually run the stages, with evidence)
15
+ *
16
+ * Receives JSON on stdin: { tool_name, tool_input: { file_path, ... }, cwd, ... }
17
+ * Emits (stdout, PreToolUse additionalContext): a single reminder line, or nothing.
18
+ *
19
+ * NON-NEGOTIABLE: fail-open. Never block the tool, never throw, never exit non-zero.
20
+ * A malformed payload, missing field, or any error → silent pass-through (exit 0,
21
+ * no output). This hook can only ADD a reminder; it can never stop a write.
22
+ *
23
+ * Scope gates (emit ONLY when all hold), to avoid noise:
24
+ * 1. GSD-T project — cwd contains .gsd-t/ (matches the auto-route/profile gate).
25
+ * 2. The target is CODE, not prose — skip .md / .txt / pseudocode / docs, since
26
+ * the doctrine's own artifacts (CLAUDE.md, pseudocode, contracts) are writes
27
+ * too, and reminding while writing the reminder is noise. Code = the moment
28
+ * the Binvoice waste would have been caught.
29
+ *
30
+ * Zero-dep by design: runs from ~/.claude/scripts where the package may be absent.
31
+ */
32
+
33
+ const fs = require("fs");
34
+ const path = require("path");
35
+
36
+ // One line. A pointer, not the doctrine. Keeps context cost ~nil.
37
+ const REMINDER =
38
+ "[GSD-T ARCHITECT] About to write/edit code — run the Architect's Oversight " +
39
+ "Six-Stage Pass FIRST (Objective → Conflict → Reuse[query the graph] → " +
40
+ "Simplicity → Reuse-forecast → Risk), each answered with evidence not conviction. " +
41
+ "Is this the simplest design, and does something reusable already exist? " +
42
+ "See ~/.claude/CLAUDE.md § Architect's Oversight Doctrine.";
43
+
44
+ // File extensions that are PROSE/config, not code — skip the reminder for these.
45
+ // (The doctrine's own artifacts are markdown; reminding while authoring them is noise.)
46
+ const PROSE_EXT = new Set([
47
+ ".md", ".markdown", ".txt", ".rst", ".adoc",
48
+ ".json", ".yaml", ".yml", ".toml", ".lock",
49
+ ".csv", ".tsv", ".svg", ".png", ".jpg", ".jpeg", ".gif", ".pdf",
50
+ ]);
51
+
52
+ /**
53
+ * Decide whether to emit the reminder for a given cwd + target file.
54
+ * Pure + total: never throws, returns a boolean.
55
+ * @param {string} cwd
56
+ * @param {string} filePath — the Write/Edit target
57
+ * @returns {boolean}
58
+ */
59
+ function shouldRemind(cwd, filePath) {
60
+ try {
61
+ if (!cwd || typeof cwd !== "string") return false;
62
+ // Gate 1 — GSD-T project only.
63
+ if (!fs.existsSync(path.join(cwd, ".gsd-t"))) return false;
64
+ // No target path → can't classify → stay quiet (fail-open toward silence).
65
+ if (!filePath || typeof filePath !== "string") return false;
66
+
67
+ const lower = filePath.toLowerCase();
68
+ const ext = path.extname(lower);
69
+
70
+ // Gate 2 — skip prose/config/asset writes (incl. the doctrine's own artifacts).
71
+ if (PROSE_EXT.has(ext)) return false;
72
+ // Skip anything under a docs/ or pseudocode/ tree even if code-extensioned.
73
+ if (/[\\/](docs|pseudocode)[\\/]/.test(lower)) return false;
74
+
75
+ return true;
76
+ } catch (_) {
77
+ return false; // fail-open: any error → no reminder, never block
78
+ }
79
+ }
80
+
81
+ if (require.main === module) {
82
+ let input = "";
83
+ let done = false;
84
+ process.stdin.setEncoding("utf8");
85
+ process.stdin.on("data", (c) => { input += c; });
86
+
87
+ const finish = () => {
88
+ if (done) return; // run once — whichever of end/error/watchdog fires first
89
+ done = true;
90
+ try {
91
+ let data = null;
92
+ try { data = JSON.parse(input); } catch (_) { process.exit(0); }
93
+ if (!data || typeof data !== "object") process.exit(0);
94
+
95
+ const cwd = (typeof data.cwd === "string" && data.cwd) ? data.cwd : process.cwd();
96
+ const ti = data.tool_input && typeof data.tool_input === "object" ? data.tool_input : {};
97
+ const filePath = typeof ti.file_path === "string" ? ti.file_path : "";
98
+
99
+ if (shouldRemind(cwd, filePath)) {
100
+ process.stdout.write(REMINDER + "\n");
101
+ }
102
+ } catch (_) {
103
+ // Belt-and-suspenders: never block a write.
104
+ }
105
+ process.exit(0);
106
+ };
107
+
108
+ process.stdin.on("end", finish);
109
+ process.stdin.on("error", finish); // stdin read error → fail-open, never hang
110
+ // Watchdog: if stdin never delivers EOF (spawn-under-load edge, closed pipe),
111
+ // proceed anyway with whatever we have. A hook must NEVER hang and block a write.
112
+ const wd = setTimeout(finish, 1500);
113
+ if (wd.unref) wd.unref(); // don't keep the event loop alive solely for the watchdog
114
+ }
115
+
116
+ module.exports = { shouldRemind, REMINDER };