@ryuenn3123/agentic-senior-core 6.11.0 → 6.12.0

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-senior-core",
3
- "version": "6.11.0",
3
+ "version": "6.12.0",
4
4
  "description": "Universal AI coding rules. Write code like a staff engineer.",
5
5
  "skills": "./skills/",
6
6
  "hooks": "./hooks/hooks.json",
@@ -57,16 +57,50 @@ const {
57
57
  } = require('./constants.cjs');
58
58
 
59
59
  // Module-level counter for Claude Code path (resets per process spawn)
60
- let sourceEditCount = 0;
60
+ function logHook(event, msg) {
61
+ try {
62
+ const os = require('os');
63
+ const logDir = path.join(os.homedir(), '.asc');
64
+ if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
65
+ const logFile = path.join(logDir, 'hooks.log');
66
+ try {
67
+ if (fs.existsSync(logFile)) {
68
+ const stats = fs.statSync(logFile);
69
+ if (stats.size > 100 * 1024) {
70
+ const content = fs.readFileSync(logFile, 'utf8');
71
+ const trimmed = content.slice(-50 * 1024);
72
+ const firstNewline = trimmed.indexOf('\n');
73
+ fs.writeFileSync(logFile, firstNewline >= 0 ? trimmed.slice(firstNewline + 1) : trimmed, 'utf8');
74
+ }
75
+ }
76
+ } catch (_) {}
77
+ fs.appendFileSync(logFile, `[${new Date().toISOString()}] [post-edit-enforce] [${event}] ${msg}\n`, 'utf8');
78
+ } catch (_) {}
79
+ }
80
+
81
+ logHook('ScriptStart', `pid=${process.pid} cwd=${process.cwd()}`);
82
+
83
+ if (process.stdin.isTTY) {
84
+ logHook('StdinTTY', 'Exiting because stdin is a TTY');
85
+ process.exit(0);
86
+ }
87
+
88
+ const stdinTimeout = setTimeout(() => {
89
+ logHook('StdinTimeout', `No input after 3s, exiting bufferLen=${inputBuffer.length}`);
90
+ process.exit(0);
91
+ }, 3000);
61
92
 
62
93
  let inputBuffer = '';
63
94
  process.stdin.setEncoding('utf8');
64
95
  process.stdin.on('data', chunk => {
65
96
  inputBuffer += chunk;
97
+ logHook('DataChunk', `len=${chunk.length} preview=${chunk.slice(0, 100).replace(/[\r\n]+/g, ' ')}`);
66
98
  try {
67
99
  const data = JSON.parse(inputBuffer);
100
+ clearTimeout(stdinTimeout);
101
+ logHook('StdinData', 'keys=' + Object.keys(data).join(','));
68
102
 
69
- if (data.invocationNum !== undefined && data.transcriptPath) {
103
+ if (data.transcriptPath) {
70
104
  handleAntigravityPostInvocation(data);
71
105
  return;
72
106
  }
@@ -82,6 +116,32 @@ process.stdin.on('data', chunk => {
82
116
  }
83
117
  });
84
118
 
119
+ process.stdin.on('end', () => {
120
+ clearTimeout(stdinTimeout);
121
+ logHook('StdinEnd', `bufferLen=${inputBuffer.length}`);
122
+ if (inputBuffer.trim()) {
123
+ try {
124
+ const data = JSON.parse(inputBuffer);
125
+ logHook('ParsedInputOnEnd', 'keys=' + Object.keys(data).join(','));
126
+ if (data.transcriptPath) {
127
+ handleAntigravityPostInvocation(data);
128
+ return;
129
+ }
130
+ const toolName = data.tool_name || '';
131
+ const toolInput = data.tool_input || {};
132
+ processSingleEdit(toolName, toolInput, function (nudge) {
133
+ emitClaude(nudge);
134
+ });
135
+ process.exit(0);
136
+ } catch (e) {
137
+ logHook('JsonParseErrorOnEnd', e.message);
138
+ process.exit(0);
139
+ }
140
+ } else {
141
+ process.exit(0);
142
+ }
143
+ });
144
+
85
145
  function handleAntigravityPostInvocation(data) {
86
146
  try {
87
147
  if (!fs.existsSync(data.transcriptPath)) return;
@@ -133,22 +193,32 @@ function handleAntigravityPostInvocation(data) {
133
193
  + '(2) Does the codebase already have this? (3) Stdlib/native? (4) Existing dependency?');
134
194
  }
135
195
 
136
- if (findings.length > 0) {
196
+ const isPostToolUse = data.stepIdx !== undefined;
197
+ logHook('AntigravityResult', `sourceEdits=${sourceEditsSinceStart}, findings=${findings.length}, isPostToolUse=${isPostToolUse}`);
198
+
199
+ if (isPostToolUse) {
200
+ process.stdout.write(JSON.stringify({}) + '\n');
201
+ } else if (findings.length > 0) {
137
202
  const injectSteps = findings.map(function (f) { return { ephemeralMessage: f }; });
138
203
  process.stdout.write(JSON.stringify({ injectSteps: injectSteps }) + '\n');
139
204
  } else {
140
205
  process.stdout.write(JSON.stringify({}) + '\n');
141
206
  }
142
207
  } catch (e) {
208
+ logHook('AntigravityError', e.message);
143
209
  process.stdout.write(JSON.stringify({}) + '\n');
144
210
  }
211
+ process.exit(0);
145
212
  }
146
213
 
147
214
  function processSingleEdit(toolName, toolInput, emitFn, skipArray) {
148
- const filePath = toolInput.file_path || '';
215
+ const filePath = toolInput.file_path || toolInput.TargetFile || toolInput.filePath || '';
149
216
  if (!filePath) return;
150
217
  const findings = [];
151
218
 
219
+ const isEdit = toolName === 'Edit' || toolName === 'replace_file_content' || toolName === 'multi_replace_file_content';
220
+ const isWrite = toolName === 'Write' || toolName === 'write_to_file';
221
+
152
222
  // Increment per-process counter for Claude Code drift detection
153
223
  const ext = path.extname(filePath).slice(1);
154
224
  if (SOURCE_EXTENSIONS.has(ext)) {
@@ -168,9 +238,9 @@ function processSingleEdit(toolName, toolInput, emitFn, skipArray) {
168
238
  }
169
239
 
170
240
  if (SOURCE_EXTENSIONS.has(ext)) {
171
- if (toolName === 'Edit') {
241
+ if (isEdit) {
172
242
  checkLocDelta(toolInput, filePath, findings);
173
- } else if (toolName === 'Write') {
243
+ } else if (isWrite) {
174
244
  checkNewFileSize(toolInput, filePath, findings);
175
245
  }
176
246
  }
@@ -191,6 +261,8 @@ function processSingleEdit(toolName, toolInput, emitFn, skipArray) {
191
261
  checkWorkflowGate(toolName, filePath, ext, findings);
192
262
  }
193
263
 
264
+ logHook('ProcessSingleEdit', 'tool=' + toolName + ', file=' + filePath + ', findings=' + findings.length);
265
+
194
266
  if (findings.length === 0) return;
195
267
 
196
268
  const nudge = '[ASC enforcement] ' + findings.join(' ') + ' Review the decision ladder before continuing.';
@@ -256,8 +328,8 @@ function extractDeps(text, pattern) {
256
328
  }
257
329
 
258
330
  function checkLocDelta(toolInput, filePath, findings) {
259
- var newLines = (toolInput.new_string || '').split('\n').length;
260
- var oldLines = (toolInput.old_string || '').split('\n').length;
331
+ var newLines = (toolInput.new_string || toolInput.ReplacementContent || '').split('\n').length;
332
+ var oldLines = (toolInput.old_string || toolInput.TargetContent || '').split('\n').length;
261
333
  var delta = newLines - oldLines;
262
334
  if (delta > LOC_DELTA_THRESHOLD) {
263
335
  findings.push(
@@ -268,7 +340,7 @@ function checkLocDelta(toolInput, filePath, findings) {
268
340
  }
269
341
 
270
342
  function checkNewFileSize(toolInput, filePath, findings) {
271
- var lines = (toolInput.content || '').split('\n').length;
343
+ var lines = (toolInput.content || toolInput.CodeContent || '').split('\n').length;
272
344
  if (lines > NEW_FILE_LINE_THRESHOLD) {
273
345
  // Step 1-2 coverage ("does this already exist?") moved to dedup-gate.js
274
346
  // which provides concrete file-name + overlap-percentage feedback.
@@ -327,7 +399,7 @@ function checkSecurityPatterns(toolName, toolInput, filePath, findings) {
327
399
  }
328
400
 
329
401
  function checkUiSlopPatterns(toolName, toolInput, filePath, findings) {
330
- var target = toolName === 'Edit' ? (toolInput.new_string || '') : (toolInput.content || '');
402
+ var target = toolName === 'Edit' ? (toolInput.new_string || toolInput.ReplacementContent || '') : (toolInput.content || toolInput.CodeContent || '');
331
403
  if (!target) return;
332
404
 
333
405
  if (UI_SLOP_PATTERNS.patterns) {
@@ -1,121 +1,4 @@
1
1
  {
2
- "agentic-senior-core": {
3
- "SessionStart": [
4
- {
5
- "matcher": "startup|resume|clear|compact",
6
- "hooks": [
7
- {
8
- "type": "command",
9
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','session-start.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','session-start.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','session-start.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','session-start.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
10
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\session-start.js\" }",
11
- "timeout": 5,
12
- "statusMessage": "Loading ASC rules..."
13
- }
14
- ]
15
- }
16
- ],
17
- "SubagentStart": [
18
- {
19
- "hooks": [
20
- {
21
- "type": "command",
22
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','subagent-start.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','subagent-start.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','subagent-start.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','subagent-start.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
23
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\subagent-start.js\" }",
24
- "timeout": 5,
25
- "statusMessage": "Loading ASC rules..."
26
- }
27
- ]
28
- }
29
- ],
30
- "PreToolUse": [
31
- {
32
- "matcher": "Edit|Write|replace_file_content|write_to_file|write_file|multi_replace_file_content",
33
- "hooks": [
34
- {
35
- "type": "command",
36
- "if": "Edit(**/package.json)|Edit(**/requirements.txt)|Edit(**/pyproject.toml)|Edit(**/go.mod)|Edit(**/Cargo.toml)|Edit(**/Gemfile)",
37
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
38
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\pre-tool-dependency-gate.js\" }",
39
- "timeout": 5,
40
- "statusMessage": "ASC Pre-tool dependency check (Edit)..."
41
- },
42
- {
43
- "type": "command",
44
- "if": "Write(**/package.json)|Write(**/requirements.txt)|Write(**/pyproject.toml)|Write(**/go.mod)|Write(**/Cargo.toml)|Write(**/Gemfile)",
45
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
46
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\pre-tool-dependency-gate.js\" }",
47
- "timeout": 5,
48
- "statusMessage": "ASC Pre-tool dependency check (Write)..."
49
- }
50
- ]
51
- },
52
- {
53
- "matcher": "Bash|run_command|run_shell_command|terminal|execute_command",
54
- "hooks": [
55
- {
56
- "type": "command",
57
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
58
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\pre-tool-dependency-gate.js\" }",
59
- "timeout": 5,
60
- "statusMessage": "ASC Pre-tool dependency check (Terminal)..."
61
- }
62
- ]
63
- }
64
- ],
65
- "PostToolUse": [
66
- {
67
- "matcher": "Edit|Write|replace_file_content|write_to_file|write_file|multi_replace_file_content",
68
- "hooks": [
69
- {
70
- "type": "command",
71
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','post-edit-enforce.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
72
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\post-edit-enforce.js\" }",
73
- "timeout": 5,
74
- "statusMessage": "ASC ladder & spec gate check..."
75
- },
76
- {
77
- "type": "command",
78
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','dedup-gate.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','dedup-gate.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','dedup-gate.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','dedup-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
79
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\dedup-gate.js\" }",
80
- "timeout": 15,
81
- "statusMessage": "ASC duplicate-code scan..."
82
- }
83
- ]
84
- }
85
- ],
86
- "PreInvocation": [
87
- {
88
- "type": "command",
89
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','session-pulse.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','session-pulse.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','session-pulse.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','session-pulse.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
90
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\session-pulse.js\" }",
91
- "timeout": 5,
92
- "statusMessage": "Loading ASC rules..."
93
- },
94
- {
95
- "type": "command",
96
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','ladder-pulse.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','ladder-pulse.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','ladder-pulse.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','ladder-pulse.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
97
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\ladder-pulse.js\" }",
98
- "timeout": 5,
99
- "statusMessage": "ASC ladder pulse..."
100
- }
101
- ],
102
- "PostInvocation": [
103
- {
104
- "type": "command",
105
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','post-edit-enforce.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
106
- "timeout": 15
107
- }
108
- ],
109
- "PreCompact": [
110
- {
111
- "type": "command",
112
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','pre-compact-pin.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','pre-compact-pin.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','pre-compact-pin.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-compact-pin.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
113
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\pre-compact-pin.js\" }",
114
- "timeout": 5,
115
- "statusMessage": "ASC constraint pinning..."
116
- }
117
- ]
118
- },
119
2
  "hooks": {
120
3
  "SessionStart": [
121
4
  {
@@ -123,8 +6,8 @@
123
6
  "hooks": [
124
7
  {
125
8
  "type": "command",
126
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','session-start.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','session-start.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','session-start.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','session-start.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
127
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\session-start.js\" }",
9
+ "command": "node hooks/session-start.js",
10
+ "commandWindows": "node hooks/session-start.js",
128
11
  "timeout": 5,
129
12
  "statusMessage": "Loading ASC rules..."
130
13
  }
@@ -136,8 +19,8 @@
136
19
  "hooks": [
137
20
  {
138
21
  "type": "command",
139
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','subagent-start.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','subagent-start.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','subagent-start.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','subagent-start.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
140
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\subagent-start.js\" }",
22
+ "command": "node hooks/subagent-start.js",
23
+ "commandWindows": "node hooks/subagent-start.js",
141
24
  "timeout": 5,
142
25
  "statusMessage": "Loading ASC rules..."
143
26
  }
@@ -151,16 +34,16 @@
151
34
  {
152
35
  "type": "command",
153
36
  "if": "Edit(**/package.json)|Edit(**/requirements.txt)|Edit(**/pyproject.toml)|Edit(**/go.mod)|Edit(**/Cargo.toml)|Edit(**/Gemfile)",
154
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
155
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\pre-tool-dependency-gate.js\" }",
37
+ "command": "node hooks/pre-tool-dependency-gate.js",
38
+ "commandWindows": "node hooks/pre-tool-dependency-gate.js",
156
39
  "timeout": 5,
157
40
  "statusMessage": "ASC Pre-tool dependency check (Edit)..."
158
41
  },
159
42
  {
160
43
  "type": "command",
161
44
  "if": "Write(**/package.json)|Write(**/requirements.txt)|Write(**/pyproject.toml)|Write(**/go.mod)|Write(**/Cargo.toml)|Write(**/Gemfile)",
162
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
163
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\pre-tool-dependency-gate.js\" }",
45
+ "command": "node hooks/pre-tool-dependency-gate.js",
46
+ "commandWindows": "node hooks/pre-tool-dependency-gate.js",
164
47
  "timeout": 5,
165
48
  "statusMessage": "ASC Pre-tool dependency check (Write)..."
166
49
  }
@@ -171,8 +54,8 @@
171
54
  "hooks": [
172
55
  {
173
56
  "type": "command",
174
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-tool-dependency-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
175
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\pre-tool-dependency-gate.js\" }",
57
+ "command": "node hooks/pre-tool-dependency-gate.js",
58
+ "commandWindows": "node hooks/pre-tool-dependency-gate.js",
176
59
  "timeout": 5,
177
60
  "statusMessage": "ASC Pre-tool dependency check (Terminal)..."
178
61
  }
@@ -185,15 +68,15 @@
185
68
  "hooks": [
186
69
  {
187
70
  "type": "command",
188
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','post-edit-enforce.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
189
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\post-edit-enforce.js\" }",
71
+ "command": "node hooks/post-edit-enforce.js",
72
+ "commandWindows": "node hooks/post-edit-enforce.js",
190
73
  "timeout": 5,
191
74
  "statusMessage": "ASC ladder & spec gate check..."
192
75
  },
193
76
  {
194
77
  "type": "command",
195
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','dedup-gate.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','dedup-gate.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','dedup-gate.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','dedup-gate.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
196
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\dedup-gate.js\" }",
78
+ "command": "node hooks/dedup-gate.js",
79
+ "commandWindows": "node hooks/dedup-gate.js",
197
80
  "timeout": 15,
198
81
  "statusMessage": "ASC duplicate-code scan..."
199
82
  }
@@ -203,15 +86,15 @@
203
86
  "PreInvocation": [
204
87
  {
205
88
  "type": "command",
206
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','session-pulse.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','session-pulse.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','session-pulse.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','session-pulse.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
207
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\session-pulse.js\" }",
89
+ "command": "node hooks/session-pulse.js",
90
+ "commandWindows": "node hooks/session-pulse.js",
208
91
  "timeout": 5,
209
92
  "statusMessage": "Loading ASC rules..."
210
93
  },
211
94
  {
212
95
  "type": "command",
213
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','ladder-pulse.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','ladder-pulse.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','ladder-pulse.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','ladder-pulse.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
214
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\ladder-pulse.js\" }",
96
+ "command": "node hooks/ladder-pulse.js",
97
+ "commandWindows": "node hooks/ladder-pulse.js",
215
98
  "timeout": 5,
216
99
  "statusMessage": "ASC ladder pulse..."
217
100
  }
@@ -219,15 +102,16 @@
219
102
  "PostInvocation": [
220
103
  {
221
104
  "type": "command",
222
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','post-edit-enforce.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','post-edit-enforce.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
105
+ "command": "node hooks/post-edit-enforce.js",
106
+ "commandWindows": "node hooks/post-edit-enforce.js",
223
107
  "timeout": 15
224
108
  }
225
109
  ],
226
110
  "PreCompact": [
227
111
  {
228
112
  "type": "command",
229
- "command": "node -e \"const p=require('path'),fs=require('fs'),os=require('os');const local=p.join(process.cwd(),'.agents','plugins','agentic-senior-core','hooks','pre-compact-pin.js');const g1=p.join(os.homedir(),'.agents','plugins','agentic-senior-core','hooks','pre-compact-pin.js');const g2=p.join(os.homedir(),'.gemini','config','plugins','agentic-senior-core','hooks','pre-compact-pin.js');const g3=p.join(os.homedir(),'.gemini','antigravity-cli','plugins','agentic-senior-core','hooks','pre-compact-pin.js');require(fs.existsSync(local)?local:(fs.existsSync(g1)?g1:(fs.existsSync(g2)?g2:g3)));\"",
230
- "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { $root = if ($env:PLUGIN_ROOT) { $env:PLUGIN_ROOT } else { if ($env:CLAUDE_PLUGIN_ROOT) { $env:CLAUDE_PLUGIN_ROOT } else { if (Test-Path \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\") { \"$env:USERPROFILE\\.agents\\plugins\\agentic-senior-core\" } else { \"$env:USERPROFILE\\.gemini\\config\\plugins\\agentic-senior-core\" } } }; node \"$root\\hooks\\pre-compact-pin.js\" }",
113
+ "command": "node hooks/pre-compact-pin.js",
114
+ "commandWindows": "node hooks/pre-compact-pin.js",
231
115
  "timeout": 5,
232
116
  "statusMessage": "ASC constraint pinning..."
233
117
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-senior-core",
3
- "version": "6.11.0",
3
+ "version": "6.12.0",
4
4
  "description": "Universal AI coding rules. Because your AI writes code like it gets paid by the line.",
5
5
  "contextFileName": "rules/agentic-senior-core.md",
6
6
  "rules": [
@@ -19,6 +19,8 @@ When picking step 5 or 6 (unless trivial):
19
19
  - One-line comment noting rationale and upgrade trigger if there is a ceiling.
20
20
  - One runnable check (assertion, test, or demo) proving it works.
21
21
  - Never simulate success or return hardcoded values mimicking live integrations. Temporary stubs must be marked with `// ASC-STUB: [reason] [ASC-DEBT-xxx]` and tracked in the debt ledger.
22
+ - Action Feedback Invariant: Every mutation/user action (submit, delete, update) MUST have a pending state (disabled button/spinner) and visible completion feedback (toast or banner with actionable retry). Never silently swallow errors.
23
+ - Resilience Invariant: Soft dependencies must have defined fallbacks (static safe default, stale cache, or degraded mode). Protect UI sections with localized error boundaries.
22
24
 
23
25
  ## Security (never skip)
24
26
  - Validate and normalize ALL inputs at trust boundaries.
@@ -50,9 +52,11 @@ When picking step 5 or 6 (unless trivial):
50
52
  - UI component reuse: Component re-invention across pages is forbidden. Before declaring markup or styling, check existing components in `components/` or `ui/`. Compose or extend existing components via props/variants. Do not introduce single-page clones or ad-hoc styling that diverges from established pages.
51
53
 
52
54
  ## Error Handling & Observability
53
- - Fail fast on invalid input at trust boundaries. Handle only errors that can actually occur.
55
+ - Fail fast on invalid input at trust boundaries; fail safe with graceful fallback for external runtime dependencies.
54
56
  - Structured error responses with safe details (RFC 9457). Distinguish client (4xx) from server (5xx) errors.
55
- - Frontend data-fetching components must explicitly handle loading, error (toast/notification with retry), and data states.
57
+ - Action feedback: mutations (create, update, delete, submit) must provide immediate pending state (disable button/spinner) and clear success/error notification (toast or banner with retry). Never silently swallow mutation errors.
58
+ - UI data states: handle the state quad — loading skeleton/spinner, empty state, error state with retry, and populated data. Wrap critical UI widgets in localized error boundaries.
59
+ - External dependency resilience: classify dependencies as hard (fail fast + actionable error) or soft (fallback to static safe default, stale cache, or degraded mode).
56
60
  - Surface every operational error with context. Empty catch blocks mask production issues.
57
61
  - Structured key-value logging for significant events. Never leak stack traces or credentials in production logs.
58
62
 
@@ -14,9 +14,13 @@ Universal AI coding rules. Write code like a staff engineer.
14
14
  - `/asc-add-feature` -- Brownfield workflow (Research -> Plan -> Implement)
15
15
  - `/asc-refactor` -- Structured refactoring workflow with pre-checks and validation
16
16
  - `/asc-review` -- Production-risk code review with severity-ordered findings
17
- - `/asc-audit` -- Security and architecture audit
17
+ - `/asc-audit` -- Security, architecture, and resilience audit
18
18
  - `/asc-reference` -- Domain-specific rules (testing, API, database, frontend, infra, resilience)
19
+ - `/asc-fingerprint` -- Map repository conventions, notification providers, and error patterns
20
+ - `/asc-dedup` -- Token-level clone and duplicate code audit via jscpd
19
21
  - `/asc-debt` -- Track deferred enforcement violations (add, list, resolve, summary)
22
+ - `/asc-learn` -- Mine adaptive user preferences from session history
23
+ - `/asc-bootstrap` -- Seed baseline UI and notification preferences
20
24
  - `/asc-help` -- Show this help
21
25
 
22
26
  ## Enforcement
@@ -30,8 +30,8 @@ Format:
30
30
 
31
31
  1. Write `workflow-gate.json` with phase `research`.
32
32
  2. Map existing code: patterns, utilities, dependencies already in use. Locate at least one analogous feature/module and record its file paths.
33
- 3. **UI Component & Design Token Inventory**: For any UI or frontend work, scan existing shared components (e.g. `components/`, `ui/`) and existing page patterns. Explicitly identify what must NOT be rebuilt (buttons, cards, modals, inputs, tables, layout wrappers, validation helpers).
34
- 4. Output a factual research summary that separates what exists from what is proposed, including an explicit `[REUSED COMPONENTS & HELPERS]` list, with file paths for the two or three claims that drive the plan.
33
+ 3. **UI Component, Notification & Error Pattern Inventory**: Scan existing shared components (`components/`, `ui/`), page patterns, active toast/notification providers (e.g., Sonner, react-hot-toast, custom alert banners), and existing fallback/error boundary conventions. Explicitly identify what must NOT be rebuilt.
34
+ 4. Output a factual research summary that separates what exists from what is proposed, including explicit `[REUSED COMPONENTS & HELPERS]` and `[NOTIFICATION & ERROR CONVENTIONS]` lists, with file paths for the two or three claims that drive the plan.
35
35
  5. **STOP and wait for user approval.** Do not plan or implement.
36
36
 
37
37
  ## Phase 2: Plan
@@ -41,14 +41,20 @@ Format:
41
41
  3. Check if `.github/workflows/asc-quality-gate.yml` exists. If not, include scaffolding it in your plan (must run linter, type-check, and audit) and remind the user to enable Branch Protection.
42
42
  4. Create a numbered, step-by-step implementation plan with specific files, functions, and line references.
43
43
  5. Include a "Don't Build" list and a "Reused Components" list from the research phase. For any proposed new UI component, justify why existing components cannot be reused or extended via props/variants.
44
- 6. **Callout: Plan-Reading Illusion.** Ask the user to explicitly verify the two or three critical plan claims against the referenced files, not just skim it.
45
- 7. Output the plan.
46
- 8. **STOP and wait for user approval.** Do not implement.
44
+ 6. **Mandatory Failure Modes, Fallbacks & User Feedback Matrix**: Define concrete behavior for every failure scenario:
45
+ - What happens on network/API failure or timeout? (Specify exact fallback data or state).
46
+ - What feedback does the user see? (Specify pending spinner, field error, or toast/banner message).
47
+ - How can the user recover? (Retry button, offline warning, or fallback form).
48
+ 7. **Callout: Plan-Reading Illusion.** Ask the user to explicitly verify the critical plan claims against the referenced files, not just skim it.
49
+ 8. Output the plan.
50
+ 9. **STOP and wait for user approval.** Do not implement.
47
51
 
48
52
  ## Phase 3: Implement
49
53
 
50
54
  1. On approval of Phase 2, update `workflow-gate.json` phase to `implement`.
51
55
  2. Recommend a fresh context (intentional compaction) at the phase boundary or after roughly 20-30 tool calls. Do not wait until degradation is subjectively noticeable.
52
56
  3. Execute the approved plan.
53
- 4. **Mandatory Verification**: Execute the project test suite (`ascx test`, `npm test`, `pytest`, `go test ./...`, or `cargo test`). The test runner MUST exit with code 0. Do NOT declare completion or weaken test assertions if tests fail.
57
+ 4. **Mandatory Verification**:
58
+ - Execute the project test suite (`ascx test`, `npm test`, `pytest`, `go test ./...`, or `cargo test`). The test runner MUST exit with code 0. Do NOT declare completion or weaken test assertions if tests fail.
59
+ - Verify failure and edge-case behavior: ensure pending indicators resolve, mutation errors show user-facing notifications with retry, and non-critical dependency failures trigger defined fallbacks instead of white-screens or unhandled rejections.
54
60
  5. On verified pass, give a short comprehension summary of what changed and why, then clear the state in `workflow-gate.json` by overwriting it with `{}`.
@@ -24,21 +24,22 @@ Grounded in: OWASP Top 10 (2025), OWASP ASVS v5.0, OWASP Top 10 for Agentic Appl
24
24
  4. **Architecture boundaries**: Business logic in transport layer, shared databases between services, circular dependencies, internal model leakage through public APIs.
25
25
  5. **Dependency health**: Known vulnerabilities, unmaintained packages, excessive dependency surface.
26
26
  6. **Error exposure**: Stack traces, internal paths, or implementation details exposed to clients.
27
+ 7. **Reliability & Fault-Tolerance**: Unhandled async promises, unbounded network timeouts, empty catch blocks, missing fallbacks on non-critical dependencies, and silent mutation failures without user notifications.
27
28
 
28
29
  ## Agentic Risk Scope (OWASP Top 10 for Agentic Applications)
29
30
 
30
31
  When the target is an AI agent system, MCP server, or plugin:
31
32
 
32
- 7. **Agent Goal Hijack (ASI01)**: Content read by the agent that could override instructions.
33
- 8. **Tool Misuse (ASI02)**: Tools callable without adequate validation of parameters.
34
- 9. **Identity & Privilege Abuse (ASI03)**: Agent running with broader permissions than needed.
35
- 10. **Agentic Supply Chain (ASI04)**: Untrusted plugins, MCP servers, or dependencies.
36
- 11. **Unexpected Code Execution (ASI05)**: Agent-generated code running without sandbox.
37
- 12. **Memory & Context Poisoning (ASI06)**: State files or persistent memory injectable by untrusted sources. Note: ASC's own `debt-ledger.json` and `workflow-gate.json` are potential targets — treat as untrusted input at load time.
38
- 13. **Inter-Agent Communication (ASI07)**: Agent-to-agent messages without integrity checks.
39
- 14. **Cascading Failures (ASI08)**: Multi-agent chains where one failure propagates.
40
- 15. **Human-Agent Trust Exploitation (ASI09)**: UI/UX that misleads user about agent actions.
41
- 16. **Rogue Agents (ASI10)**: Agent behavior diverging from intended purpose.
33
+ 8. **Agent Goal Hijack (ASI01)**: Content read by the agent that could override instructions.
34
+ 9. **Tool Misuse (ASI02)**: Tools callable without adequate validation of parameters.
35
+ 10. **Identity & Privilege Abuse (ASI03)**: Agent running with broader permissions than needed.
36
+ 11. **Agentic Supply Chain (ASI04)**: Untrusted plugins, MCP servers, or dependencies.
37
+ 12. **Unexpected Code Execution (ASI05)**: Agent-generated code running without sandbox.
38
+ 13. **Memory & Context Poisoning (ASI06)**: State files or persistent memory injectable by untrusted sources. Note: ASC's own `debt-ledger.json` and `workflow-gate.json` are potential targets — treat as untrusted input at load time.
39
+ 14. **Inter-Agent Communication (ASI07)**: Agent-to-agent messages without integrity checks.
40
+ 15. **Cascading Failures (ASI08)**: Multi-agent chains where one failure propagates.
41
+ 16. **Human-Agent Trust Exploitation (ASI09)**: UI/UX that misleads user about agent actions.
42
+ 17. **Rogue Agents (ASI10)**: Agent behavior diverging from intended purpose.
42
43
 
43
44
  ## For Every Finding
44
45
 
@@ -18,7 +18,8 @@ generic architecture score.
18
18
 
19
19
  1. Read local instructions and list the active runtime, test, and deployment surfaces.
20
20
  2. Find two or more analogous modules. Record their layer split, naming, validation,
21
- error handling, tests, and documentation conventions with file paths.
21
+ error handling, user notification/toast provider, fallback patterns, tests, and
22
+ documentation conventions with file paths.
22
23
  3. Inspect recent Git history (up to 200 commits, or since the last fingerprint) for
23
24
  repeated review corrections, reverts, and release conventions.
24
25
  4. Read a debt ledger only when it exists. Treat repeated entries as a possible
@@ -15,8 +15,8 @@ Grounded in: Spec-Driven Development (SDD) with scaffolding-spec approach. Core
15
15
  This workflow nudges the agent to stop at each phase boundary, same enforcement tier as the existing decision ladder — not a hard block. Bypasses are logged to the debt ledger.
16
16
 
17
17
  For greenfield projects (`asc-new-project`), Phase 2 requires creating the **3 Core SDD Documents** in `docs/` or project root:
18
- 1. `PRD.md` — Product intent, goals, non-goals, and user problems.
19
- 2. `Architecture.md` — System structure, module boundaries, and tech stack choices.
18
+ 1. `PRD.md` — Product intent, goals, non-goals, user problems, and user feedback expectations.
19
+ 2. `Architecture.md` — System structure, module boundaries, tech stack choices, and global error handling, fallback, and notification strategy.
20
20
  3. `Schema.md` — Data contracts, database entities, API endpoints.
21
21
 
22
22
  *Note on Rules:* Coding conventions and project constraints are automatically loaded from global plugin rules (`agentic-senior-core.md`) or workspace `AGENTS.md`. No duplicate `docs/Rules.md` file is required.
@@ -38,6 +38,7 @@ After reading the target code, classify the refactor:
38
38
  ## Refactor Rules
39
39
 
40
40
  - Improve clarity, boundaries, naming, validation, error handling, and tests.
41
+ - Action Feedback & Fallbacks: Preserve all user feedback indicators, error toasts, and dependency fallbacks. Refactoring must never silently drop error-handling branches.
41
42
  - Prioritize maintainability over compressed one-liners.
42
43
  - Keep the main flow traceable. Use early returns where they reduce nesting.
43
44
  - Introduce abstractions only when the repeated pattern is real and visible.