forgedock 1.0.1 → 1.0.2
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/README.md +19 -2
- package/bin/forgedock.mjs +286 -10
- package/commands/analytics.md +129 -52
- package/commands/audit-agents.md +29 -5
- package/commands/audit.md +55 -17
- package/commands/autopilot.md +11 -6
- package/commands/cleanup.md +44 -24
- package/commands/deploy-info.md +7 -3
- package/commands/forge-stats.md +13 -4
- package/commands/forgedock-init.md +534 -0
- package/commands/geo-audit.md +114 -26
- package/commands/incident-response.md +4 -1
- package/commands/issue.md +26 -25
- package/commands/milestone.md +80 -59
- package/commands/orchestrate.md +38 -21
- package/commands/pipeline-health.md +26 -15
- package/commands/qa-sweep.md +68 -5
- package/commands/quality-gate.md +18 -0
- package/commands/review-pr-agents.md +58 -90
- package/commands/review-pr.md +58 -16
- package/commands/rollback.md +8 -5
- package/commands/security-audit.md +68 -12
- package/commands/sync-ecosystem.md +196 -156
- package/commands/work-on/build/architect.md +2 -2
- package/commands/work-on/build/context.md +2 -2
- package/commands/work-on/build/implement.md +3 -3
- package/commands/work-on/build/validate.md +3 -3
- package/commands/work-on/build.md +6 -6
- package/commands/work-on/close.md +59 -30
- package/commands/work-on/decompose.md +6 -6
- package/commands/work-on/investigate.md +16 -20
- package/commands/work-on/review.md +4 -4
- package/commands/work-on-monolithic.md +15 -16
- package/commands/work-on.md +45 -33
- package/docs/CODEX.md +1 -1
- package/docs/CONFIG.md +398 -0
- package/docs/WORKFLOW.md +115 -72
- package/package.json +1 -1
- package/scripts/verify-host-headers.sh +14 -9
- package/scripts/verify-route-registration.sh +10 -10
- package/commands/failure-recon.md +0 -375
package/commands/orchestrate.md
CHANGED
|
@@ -25,17 +25,35 @@ You are the top-level orchestrator. Your job is to take a batch of issues, plan
|
|
|
25
25
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
+
## Config Resolution
|
|
29
|
+
|
|
30
|
+
Read `forge.yaml` at the project root to resolve all project-specific variables before running any commands:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Parse forge.yaml for project context
|
|
34
|
+
GH_REPO=$(yq '.project.owner + "/" + .project.repo' forge.yaml)
|
|
35
|
+
GH_FLAG="-R $GH_REPO"
|
|
36
|
+
REPO_PATH=$(yq '.paths.root' forge.yaml)
|
|
37
|
+
PROJECT_NAME=$(yq '.project.name' forge.yaml)
|
|
38
|
+
STAGING_BRANCH=$(yq '.branches.staging' forge.yaml)
|
|
39
|
+
# Build satellite repo map from repos.satellites list
|
|
40
|
+
# Each satellite: { prefix, repo, staging_branch }
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
All `{GH_REPO}`, `{GH_FLAG}`, `{REPO_PATH}`, `{PROJECT_NAME}`, and `{STAGING_BRANCH}` references below are populated from `forge.yaml`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
28
47
|
## Multi-Repo Support
|
|
29
48
|
|
|
30
|
-
This orchestrator supports issues across
|
|
49
|
+
This orchestrator supports issues across multiple repositories. See `/work-on` → "Multi-Repo Support" and `forge.yaml → repos` for the full project registry and context variables.
|
|
31
50
|
|
|
32
51
|
### Cross-Repo Issue References
|
|
33
52
|
|
|
34
|
-
Issues can be prefixed with a project shorthand
|
|
35
|
-
- `#123` or `123` →
|
|
36
|
-
- `
|
|
37
|
-
- `
|
|
38
|
-
- `all-repos` → Scan all ecosystem repos for open issues (combine and prioritize)
|
|
53
|
+
Issues can be prefixed with a project shorthand derived from `forge.yaml → repos.satellites`:
|
|
54
|
+
- `#123` or `123` → default repo (`{GH_REPO}`)
|
|
55
|
+
- `{satellite_prefix}:5` → satellite repo (e.g., `mcp:5`, `n8n:12`) — prefixes and repos come from `forge.yaml → repos.satellites`
|
|
56
|
+
- `all-repos` → Scan all configured repos for open issues (combine and prioritize)
|
|
39
57
|
|
|
40
58
|
When spawning sub-agents, pass the project prefix so `/work-on` resolves to the correct repo.
|
|
41
59
|
|
|
@@ -63,20 +81,20 @@ Parse `$ARGUMENTS` to determine which issues to work on:
|
|
|
63
81
|
For each resolved repo, use the appropriate `-R` flag:
|
|
64
82
|
|
|
65
83
|
```bash
|
|
66
|
-
#
|
|
67
|
-
gh issue list --milestone "{TITLE}" --state open --limit 500 --json number,title,labels,milestone --jq '.[] | {number, title, labels: [.labels[].name], milestone: .milestone.title}'
|
|
84
|
+
# Default repo (no flag needed if using GH_FLAG="" for the default):
|
|
85
|
+
gh issue list {GH_FLAG} --milestone "{TITLE}" --state open --limit 500 --json number,title,labels,milestone --jq '.[] | {number, title, labels: [.labels[].name], milestone: .milestone.title}'
|
|
68
86
|
|
|
69
|
-
# Satellite repos — always include -R flag:
|
|
70
|
-
|
|
71
|
-
gh issue list -R
|
|
87
|
+
# Satellite repos — always include -R flag with the satellite repo from forge.yaml:
|
|
88
|
+
# For each satellite in forge.yaml → repos.satellites:
|
|
89
|
+
gh issue list -R {SATELLITE_REPO} --milestone "{TITLE}" --state open --limit 500 --json number,title,labels,milestone
|
|
72
90
|
|
|
73
91
|
# For specific numbers with repo prefix:
|
|
74
|
-
gh issue view
|
|
92
|
+
gh issue view {NUMBER} -R {SATELLITE_REPO} --json number,title,labels,state,milestone,body
|
|
75
93
|
|
|
76
|
-
# For "all-repos" — fetch from all and combine:
|
|
77
|
-
gh issue list --state open --limit {N} --json number,title,labels,milestone
|
|
78
|
-
|
|
79
|
-
gh issue list -R
|
|
94
|
+
# For "all-repos" — fetch from all configured repos and combine:
|
|
95
|
+
gh issue list {GH_FLAG} --state open --limit {N} --json number,title,labels,milestone
|
|
96
|
+
# For each satellite in forge.yaml → repos.satellites:
|
|
97
|
+
gh issue list -R {SATELLITE_REPO} --state open --limit 500 --json number,title,labels,milestone
|
|
80
98
|
# Combine, sort by priority, take top N
|
|
81
99
|
```
|
|
82
100
|
|
|
@@ -526,7 +544,7 @@ Agent(
|
|
|
526
544
|
model="sonnet",
|
|
527
545
|
description="Work on {PROJECT_PREFIX}#{NUMBER}",
|
|
528
546
|
run_in_background=true,
|
|
529
|
-
prompt="You are working on GitHub issue #{NUMBER} for the
|
|
547
|
+
prompt="You are working on GitHub issue #{NUMBER} for the {PROJECT_NAME} project.
|
|
530
548
|
|
|
531
549
|
**Project**: {PROJECT_NAME}
|
|
532
550
|
**Repository**: {GH_REPO}
|
|
@@ -540,11 +558,10 @@ Agent(
|
|
|
540
558
|
|
|
541
559
|
**IMPORTANT RULES**:
|
|
542
560
|
- **MANDATORY**: You MUST use the Skill tool to invoke 'work-on' with args '{PROJECT_PREFIX}{NUMBER}'. Do NOT implement manually — /work-on handles the full pipeline including label state machine (workflow:investigating → workflow:building → workflow:in-review → workflow:merged), investigation reports, PR creation, and cleanup.
|
|
543
|
-
- For
|
|
544
|
-
- For
|
|
545
|
-
- For n8n issues: `Skill(skill='work-on', args='n8n:{NUMBER}')`
|
|
561
|
+
- For default repo issues: `Skill(skill='work-on', args='{NUMBER}')`
|
|
562
|
+
- For satellite repo issues: `Skill(skill='work-on', args='{SATELLITE_PREFIX}:{NUMBER}')` (prefix from forge.yaml → repos.satellites)
|
|
546
563
|
- NEVER bypass /work-on with manual git/gh commands — the label updates and structured comments are critical for tracking
|
|
547
|
-
- NEVER target `main` for
|
|
564
|
+
- NEVER target `main` for PRs targeting the default repo. Use `{STAGING_BRANCH}` for fast-lane issues, or `milestone/{slug}` for milestone issues.
|
|
548
565
|
- Satellite repos (MCP, n8n) have no staging branch — fast-lane PRs go to `main` for those.
|
|
549
566
|
- If the issue is INVALID after investigation, close it with a comment explaining why
|
|
550
567
|
- If you hit merge conflicts or blockers, post a comment on the issue and STOP — do not force anything
|
|
@@ -9,7 +9,18 @@ argument-hint: [project repo slug or "all"]
|
|
|
9
9
|
|
|
10
10
|
You are the Forge pipeline's self-awareness layer. Your job is to measure how well the pipeline is performing, correlate performance with recent prompt changes, identify weak spots, and propose concrete improvements.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
## Config
|
|
13
|
+
|
|
14
|
+
Read project identity from `forge.yaml` before running any phase:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
FORGE_REPO=$(yq e '.project.owner + "/" + .project.repo' forge.yaml)
|
|
18
|
+
FORGE_HOME=$(yq e '.paths.root' forge.yaml)
|
|
19
|
+
echo "Forge repo: $FORGE_REPO"
|
|
20
|
+
echo "Forge home: $FORGE_HOME"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**FORGE_HOME**: `$FORGE_HOME` (set from `forge.yaml` → `paths.root`)
|
|
13
24
|
**This command is READ-ONLY on the target project.** It creates issues in the Forge repo only.
|
|
14
25
|
|
|
15
26
|
---
|
|
@@ -32,7 +43,7 @@ echo "Target project: $REPO"
|
|
|
32
43
|
|
|
33
44
|
```bash
|
|
34
45
|
# Last pipeline-health run (if any) — from Forge git log
|
|
35
|
-
LAST_RUN=$(cd
|
|
46
|
+
LAST_RUN=$(cd "$FORGE_HOME" && git log --all --oneline --grep="pipeline-health: $REPO" --format="%aI" -1 2>/dev/null || echo "")
|
|
36
47
|
if [ -z "$LAST_RUN" ]; then
|
|
37
48
|
# First run — analyze last 30 days
|
|
38
49
|
SINCE=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
|
|
@@ -49,7 +60,7 @@ Fetch the most recent `health-report` issue for the same repo and parse its metr
|
|
|
49
60
|
|
|
50
61
|
```bash
|
|
51
62
|
# Find the most recent health-report issue for this repo
|
|
52
|
-
PRIOR_REPORT=$(gh issue list -R
|
|
63
|
+
PRIOR_REPORT=$(gh issue list -R $FORGE_REPO \
|
|
53
64
|
--state all --label "health-report" --limit 10 \
|
|
54
65
|
--json number,title,body \
|
|
55
66
|
--jq "[.[] | select(.title | contains(\"$REPO\"))] | sort_by(.number) | last")
|
|
@@ -154,7 +165,7 @@ Run ALL of these in parallel (note: Phase 2I fetches trajectory comments per iss
|
|
|
154
165
|
### 2A: Forge changelog (what changed in the pipeline itself)
|
|
155
166
|
|
|
156
167
|
```bash
|
|
157
|
-
cd
|
|
168
|
+
cd "$FORGE_HOME"
|
|
158
169
|
git log --oneline --since="$SINCE" -- commands/ docs/
|
|
159
170
|
```
|
|
160
171
|
|
|
@@ -366,7 +377,7 @@ echo "Investigation invalidation rate: $INVALID_ISSUES / $TOTAL_INVESTIGATED"
|
|
|
366
377
|
|
|
367
378
|
# 2. ISSUE_SPEC audit findings
|
|
368
379
|
# Count of /audit findings classified as ISSUE_SPEC in the window
|
|
369
|
-
ISSUE_SPEC_COUNT=$(gh issue list -R
|
|
380
|
+
ISSUE_SPEC_COUNT=$(gh issue list -R $FORGE_REPO --state all --label "audit-finding" --limit 200 \
|
|
370
381
|
--json number,title,body,createdAt \
|
|
371
382
|
--jq "[.[] | select(.createdAt > \"$SINCE\" and (.body | contains(\"ISSUE_SPEC\")))] | length")
|
|
372
383
|
|
|
@@ -416,13 +427,13 @@ echo "Issue structure compliance: $STRUCTURED_ISSUES / $TOTAL_ISSUES"
|
|
|
416
427
|
```bash
|
|
417
428
|
# Persisted audit-agents summaries are posted to a designated Forge tracking issue
|
|
418
429
|
# tagged with label "orchestration-metrics". Find it:
|
|
419
|
-
TRACKING_ISSUE=$(gh issue list -R
|
|
430
|
+
TRACKING_ISSUE=$(gh issue list -R $FORGE_REPO \
|
|
420
431
|
--state open --label "orchestration-metrics" --limit 1 \
|
|
421
432
|
--json number --jq '.[0].number' 2>/dev/null)
|
|
422
433
|
|
|
423
434
|
if [ -n "$TRACKING_ISSUE" ]; then
|
|
424
435
|
# Fetch all FORGE:AUDIT-AGENTS comments posted since $SINCE
|
|
425
|
-
AUDIT_COMMENTS=$(gh api repos/
|
|
436
|
+
AUDIT_COMMENTS=$(gh api repos/$FORGE_REPO/issues/$TRACKING_ISSUE/comments \
|
|
426
437
|
--jq "[.[] | select(.body | contains(\"FORGE:AUDIT-AGENTS\")) | select(.created_at > \"$SINCE\")]")
|
|
427
438
|
echo "Found $(echo "$AUDIT_COMMENTS" | jq 'length') persisted audit-agents summaries in window"
|
|
428
439
|
else
|
|
@@ -530,7 +541,7 @@ fi
|
|
|
530
541
|
|
|
531
542
|
```bash
|
|
532
543
|
# All audit-finding issues created in the window
|
|
533
|
-
AUDIT_FINDINGS=$(gh issue list -R
|
|
544
|
+
AUDIT_FINDINGS=$(gh issue list -R $FORGE_REPO --state all --label "audit-finding" \
|
|
534
545
|
--limit 200 --json number,title,body,createdAt \
|
|
535
546
|
--jq "[.[] | select(.createdAt > \"$SINCE\")]")
|
|
536
547
|
|
|
@@ -573,7 +584,7 @@ echo "$AUDIT_FINDINGS" | jq -r \
|
|
|
573
584
|
# Severity breakdown (P1 vs P2 vs P3 from labels)
|
|
574
585
|
echo ""
|
|
575
586
|
echo "Severity breakdown:"
|
|
576
|
-
gh issue list -R
|
|
587
|
+
gh issue list -R $FORGE_REPO --state all --label "audit-finding" \
|
|
577
588
|
--limit 200 --json number,createdAt,labels \
|
|
578
589
|
--jq "[.[] | select(.createdAt > \"$SINCE\")] | group_by(.labels | map(.name) | map(select(startswith(\"P\"))) | .[0]) | map({severity: .[0].labels | map(.name) | map(select(startswith(\"P\"))) | .[0], count: length})"
|
|
579
590
|
```
|
|
@@ -598,9 +609,9 @@ This phase locates conversation JSONL files for pipeline command sessions (work-
|
|
|
598
609
|
# Convert a Claude Code project path to its ~/.claude/projects/ directory name
|
|
599
610
|
# Rule: both '/' and '.' are replaced with '-'
|
|
600
611
|
# Examples:
|
|
601
|
-
# /home/user/projects/
|
|
602
|
-
# /home/user/projects/
|
|
603
|
-
#
|
|
612
|
+
# /home/user/projects/myproject -> -home-user-projects-myproject
|
|
613
|
+
# /home/user/projects/myproject/.claude/worktrees/fix-branch
|
|
614
|
+
# -> -home-user-projects-myproject--claude-worktrees-fix-branch
|
|
604
615
|
# (note: .claude -> --claude because '.' becomes '-' and surrounding '/' also become '-')
|
|
605
616
|
_session_project_dir() {
|
|
606
617
|
echo "$1" | sed 's|[/.]|-|g'
|
|
@@ -2035,7 +2046,7 @@ For each of the top 3 defect categories:
|
|
|
2035
2046
|
Create a detailed health report as a GitHub issue in the Forge repo:
|
|
2036
2047
|
|
|
2037
2048
|
```bash
|
|
2038
|
-
gh issue create -R
|
|
2049
|
+
gh issue create -R $FORGE_REPO \
|
|
2039
2050
|
--title "Pipeline Health: $REPO — $(date +%Y-%m-%d) — Score: [SCORE]/100" \
|
|
2040
2051
|
--label "health-report" \
|
|
2041
2052
|
--body "$(cat <<'EOF'
|
|
@@ -2264,7 +2275,7 @@ For each improvement proposal, create a trackable issue using the `/issue` struc
|
|
|
2264
2275
|
- Metric < 10% below target → `P3`
|
|
2265
2276
|
|
|
2266
2277
|
```bash
|
|
2267
|
-
gh issue create -R
|
|
2278
|
+
gh issue create -R $FORGE_REPO \
|
|
2268
2279
|
--title "fix([command]): [description]" \
|
|
2269
2280
|
--label "[bug|enhancement|feature],[P1|P2|P3]" \
|
|
2270
2281
|
--body "## Problem
|
|
@@ -2338,6 +2349,6 @@ Pipeline health signals:
|
|
|
2338
2349
|
|
|
2339
2350
|
Top defect categories: [CAT1] ([N]), [CAT2] ([N]), [CAT3] ([N])
|
|
2340
2351
|
|
|
2341
|
-
Proposed improvements: [N] issues created in
|
|
2352
|
+
Proposed improvements: [N] issues created in $FORGE_REPO
|
|
2342
2353
|
Health report: [ISSUE_URL]
|
|
2343
2354
|
```
|
package/commands/qa-sweep.md
CHANGED
|
@@ -95,9 +95,72 @@ Depth-first, workflow-driven:
|
|
|
95
95
|
|
|
96
96
|
## Phase 0: Setup & Auth
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
**Load config from forge.yaml** before running any checks:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
QA_CONFIG=$(python3 - <<'PYEOF'
|
|
102
|
+
import yaml, sys, os
|
|
103
|
+
|
|
104
|
+
config_path = os.environ.get('FORGE_CONFIG', 'forge.yaml')
|
|
105
|
+
try:
|
|
106
|
+
cfg = yaml.safe_load(open(config_path))
|
|
107
|
+
except FileNotFoundError:
|
|
108
|
+
print('ERROR: forge.yaml not found. Run `cp forge.yaml.example forge.yaml` and configure your project.')
|
|
109
|
+
sys.exit(1)
|
|
110
|
+
|
|
111
|
+
paths = cfg.get('paths', {})
|
|
112
|
+
root = paths.get('root', '')
|
|
113
|
+
if not root:
|
|
114
|
+
print('ERROR: forge.yaml missing paths.root — qa-sweep needs the project root to discover pages and start services.')
|
|
115
|
+
sys.exit(1)
|
|
116
|
+
|
|
117
|
+
svc = cfg.get('services', {})
|
|
118
|
+
app_url = svc.get('app_url', 'http://localhost:3000')
|
|
119
|
+
api_url = svc.get('api_url', 'http://localhost:8000')
|
|
120
|
+
|
|
121
|
+
# Read QA credentials from credentials file (graceful: empty string if not set)
|
|
122
|
+
creds = paths.get('credentials', {})
|
|
123
|
+
creds_file = creds.get('file', '') if isinstance(creds, dict) else ''
|
|
124
|
+
qa_user = ''
|
|
125
|
+
qa_pass = ''
|
|
126
|
+
if creds_file:
|
|
127
|
+
try:
|
|
128
|
+
creds_data = yaml.safe_load(open(creds_file))
|
|
129
|
+
qa_section = creds_data.get('qa', {})
|
|
130
|
+
qa_user = qa_section.get('username', '')
|
|
131
|
+
qa_pass = qa_section.get('password', '')
|
|
132
|
+
except (FileNotFoundError, AttributeError):
|
|
133
|
+
pass # Credentials file optional — will surface as empty QA_USER/QA_PASS
|
|
134
|
+
|
|
135
|
+
print(root)
|
|
136
|
+
print(app_url)
|
|
137
|
+
print(api_url)
|
|
138
|
+
print(qa_user)
|
|
139
|
+
print(qa_pass)
|
|
140
|
+
PYEOF
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
if echo "$QA_CONFIG" | grep -q '^ERROR:'; then
|
|
144
|
+
echo "$QA_CONFIG"
|
|
145
|
+
exit 1
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
PROJECT_ROOT=$(echo "$QA_CONFIG" | sed -n '1p')
|
|
149
|
+
APP_URL=$(echo "$QA_CONFIG" | sed -n '2p')
|
|
150
|
+
API_URL=$(echo "$QA_CONFIG" | sed -n '3p')
|
|
151
|
+
QA_USER=$(echo "$QA_CONFIG" | sed -n '4p')
|
|
152
|
+
QA_PASS=$(echo "$QA_CONFIG" | sed -n '5p')
|
|
153
|
+
|
|
154
|
+
if [ -z "$QA_USER" ] || [ -z "$QA_PASS" ]; then
|
|
155
|
+
echo "WARNING: QA credentials not configured in credentials file."
|
|
156
|
+
echo "Add qa.username and qa.password to the credentials file at paths.credentials.file in forge.yaml."
|
|
157
|
+
echo "Proceeding — login step will require manual credential entry."
|
|
158
|
+
fi
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
1. Verify services: `curl -s -o /dev/null -w "%{http_code}" $APP_URL` and `$API_URL/api/v1/health`
|
|
162
|
+
2. If down: `cd $PROJECT_ROOT && docker compose up -d --build web api`
|
|
163
|
+
3. Login via Playwright: navigate `/auth/login`, fill `$QA_USER` / `$QA_PASS`, submit
|
|
101
164
|
4. Capture baseline data from dashboard/sidebar: balance (exact number + format), user info, plan/tier, notification counts, nav items
|
|
102
165
|
|
|
103
166
|
---
|
|
@@ -107,7 +170,7 @@ Depth-first, workflow-driven:
|
|
|
107
170
|
**Dynamic discovery from filesystem** (NOT hardcoded):
|
|
108
171
|
|
|
109
172
|
```bash
|
|
110
|
-
cd /
|
|
173
|
+
cd $PROJECT_ROOT/web/src/app
|
|
111
174
|
find . -name "page.tsx" -o -name "page.js" | sort
|
|
112
175
|
```
|
|
113
176
|
|
|
@@ -229,7 +292,7 @@ Next steps: `/orchestrate` on P0/P1 issues before deploy.
|
|
|
229
292
|
## Safety Rules
|
|
230
293
|
|
|
231
294
|
1. NEVER modify production data — only read/interact. CAN create+delete test data.
|
|
232
|
-
2. Test user only: `
|
|
295
|
+
2. Test user only: `$QA_USER` (configured in credentials file via forge.yaml)
|
|
233
296
|
3. Handle dialogs gracefully (accept/dismiss, don't leave hanging)
|
|
234
297
|
4. Snapshot before interactions
|
|
235
298
|
5. Check console after EVERY interaction
|
package/commands/quality-gate.md
CHANGED
|
@@ -330,6 +330,24 @@ For shell scripts that make HTTP requests to internal services:
|
|
|
330
330
|
2. Check that auth tokens are included if the endpoint requires them
|
|
331
331
|
3. Verify the URL path actually exists
|
|
332
332
|
|
|
333
|
+
Before running host-header checks on shell scripts, read project-specific internal service patterns from `forge.yaml` and export them so `verify-host-headers.sh` can append them to its generic defaults:
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
# Read project-specific internal service patterns from forge.yaml (if present)
|
|
337
|
+
FORGE_INTERNAL_PATTERNS=""
|
|
338
|
+
if [ -f "{WORKTREE_PATH}/forge.yaml" ]; then
|
|
339
|
+
# Extract internal_service_patterns list (one pattern per line) and join with |
|
|
340
|
+
FORGE_INTERNAL_PATTERNS=$(grep -A 999 'internal_service_patterns:' "{WORKTREE_PATH}/forge.yaml" \
|
|
341
|
+
| grep -E '^\s*-\s+' \
|
|
342
|
+
| sed 's/^\s*-\s*//' \
|
|
343
|
+
| tr -d '"'"'" \
|
|
344
|
+
| paste -sd '|' -)
|
|
345
|
+
fi
|
|
346
|
+
export FORGE_INTERNAL_PATTERNS
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
The script `verify-host-headers.sh` checks for the `FORGE_INTERNAL_PATTERNS` env var and appends any project-specific patterns to its generic defaults (localhost, 127.0.0.1, api-, worker-, 172.x.x.x, IP env vars) when the var is set.
|
|
350
|
+
|
|
333
351
|
### 2G.5: Hardcoded localhost in DB connection functions (shell scripts)
|
|
334
352
|
|
|
335
353
|
**Triggered when**: changed `*.sh` files contain DB connection patterns (`PGPASSWORD`, `createdb`, `psql`, `pg_dump`, `pg_restore`).
|
|
@@ -157,12 +157,13 @@ Append this block at the very end of your comment (after the `---` footer line,
|
|
|
157
157
|
|
|
158
158
|
**Prompt template:**
|
|
159
159
|
```
|
|
160
|
-
You are performing a security and code quality scan on PR #[PR_NUMBER] for
|
|
160
|
+
You are performing a security and code quality scan on PR #[PR_NUMBER] for [PROJECT_NAME].
|
|
161
161
|
|
|
162
162
|
## Context
|
|
163
163
|
- PR title: [TITLE]
|
|
164
164
|
- Services touched: [SERVICES]
|
|
165
165
|
- Files changed: [FILE_LIST]
|
|
166
|
+
[PROJECT_CONTEXT]
|
|
166
167
|
|
|
167
168
|
## Your Mission
|
|
168
169
|
Scan ALL changed code for security vulnerabilities and code quality issues. This is the baseline review that runs on every PR.
|
|
@@ -538,34 +539,24 @@ EOF
|
|
|
538
539
|
|
|
539
540
|
**Prompt template:**
|
|
540
541
|
```
|
|
541
|
-
You are auditing PR #[PR_NUMBER] for authentication and authorization correctness in
|
|
542
|
+
You are auditing PR #[PR_NUMBER] for authentication and authorization correctness in [PROJECT_NAME].
|
|
542
543
|
|
|
543
544
|
## CRITICAL PROJECT CONVENTIONS — You MUST enforce these:
|
|
544
545
|
|
|
545
|
-
|
|
546
|
-
AlterLab has TWO auth dependencies. Using the wrong one is a security bug:
|
|
547
|
-
|
|
548
|
-
| Route Type | Auth Dependency | How It Works | Example Routes |
|
|
549
|
-
|-----------|----------------|-------------|----------------|
|
|
550
|
-
| **Dashboard routes** (browser sessions) | `SessionUser` | JWT from httpOnly cookie, set by NextAuth | `/api/dashboard/*`, `/api/billing/*`, `/api/keys/*`, `/api/users/*` |
|
|
551
|
-
| **Public API routes** (SDK/external) | `CurrentUser` | API key from `X-API-Key` header | `/api/v1/scrape`, `/api/v1/jobs/*` |
|
|
552
|
-
| **Admin routes** | `CurrentUser` + `is_admin` check | API key with admin flag | `/api/v1/admin/*` |
|
|
553
|
-
|
|
554
|
-
**VIOLATION**: Using `CurrentUser` on a dashboard route exposes API key auth to browser sessions.
|
|
555
|
-
**VIOLATION**: Using `SessionUser` on a public API route breaks SDK authentication.
|
|
546
|
+
[DOMAIN_CONTEXT]
|
|
556
547
|
|
|
557
548
|
### How to Check
|
|
558
549
|
```bash
|
|
559
550
|
# Find auth patterns in changed files
|
|
560
|
-
grep -rn "
|
|
551
|
+
grep -rn "auth\|Depends\|get_current\|verify_token\|require_auth" [CHANGED_FILES]
|
|
561
552
|
|
|
562
553
|
# Cross-reference with route path to determine if correct
|
|
563
|
-
grep -rn "@router\.(get\|post\|put\|delete)" [CHANGED_FILES]
|
|
554
|
+
grep -rn "@router\.(get\|post\|put\|delete)\|app\.(get\|post\|put\|delete)" [CHANGED_FILES]
|
|
564
555
|
```
|
|
565
556
|
|
|
566
557
|
### What to Verify
|
|
567
558
|
1. Every new endpoint has an auth dependency (no unauthenticated endpoints without justification)
|
|
568
|
-
2. The CORRECT auth dependency is used based on route type (
|
|
559
|
+
2. The CORRECT auth dependency is used based on route type (refer to project conventions in [DOMAIN_CONTEXT] above — if absent, derive from surrounding code patterns)
|
|
569
560
|
3. Multi-tenancy: resource access checks `user_id` ownership BEFORE returning data
|
|
570
561
|
4. Rate limiting is applied to public-facing endpoints
|
|
571
562
|
5. No IDOR vulnerabilities (different error codes for 404 vs 403 leak existence)
|
|
@@ -675,30 +666,15 @@ EOF
|
|
|
675
666
|
|
|
676
667
|
**Prompt template:**
|
|
677
668
|
```
|
|
678
|
-
You are auditing PR #[PR_NUMBER] for billing integrity in
|
|
669
|
+
You are auditing PR #[PR_NUMBER] for billing integrity in [PROJECT_NAME].
|
|
679
670
|
|
|
680
|
-
CRITICAL:
|
|
671
|
+
CRITICAL: Any billing bug = revenue loss or user overcharging.
|
|
681
672
|
|
|
682
673
|
## Project Billing Architecture
|
|
683
674
|
|
|
684
|
-
|
|
685
|
-
```
|
|
686
|
-
API (pre-scrape):
|
|
687
|
-
1. Check user balance (services/api/app/core/credits.py or billing/)
|
|
688
|
-
2. Pre-debit estimated cost based on requested tier
|
|
689
|
-
3. Queue job to Redis
|
|
690
|
-
|
|
691
|
-
Worker (post-scrape):
|
|
692
|
-
4. Execute scrape, determine actual tier used
|
|
693
|
-
5. Reconcile: refund difference if actual tier < estimated tier
|
|
694
|
-
6. Record final billing in job result
|
|
695
|
-
```
|
|
675
|
+
[DOMAIN_CONTEXT]
|
|
696
676
|
|
|
697
|
-
|
|
698
|
-
- Pricing: `services/api/app/core/pricing.py` (TIER_COSTS)
|
|
699
|
-
- Credits: `services/api/app/core/credits.py` or `billing/`
|
|
700
|
-
- Reconciliation: `services/worker/worker/queues.py` (search for "reconcil")
|
|
701
|
-
- Tier determination: `services/worker/worker/unified_consumer.py`
|
|
677
|
+
If no billing context is configured above, derive the billing flow from the changed files: trace credit check → debit → execution → reconciliation paths.
|
|
702
678
|
|
|
703
679
|
## What to Verify
|
|
704
680
|
1. **Trace the full flow**: credit check → pre-debit → execution → reconciliation
|
|
@@ -781,7 +757,7 @@ EOF
|
|
|
781
757
|
|
|
782
758
|
**Prompt template:**
|
|
783
759
|
```
|
|
784
|
-
You are auditing PR #[PR_NUMBER] for concurrency bugs and race conditions in
|
|
760
|
+
You are auditing PR #[PR_NUMBER] for concurrency bugs and race conditions in [PROJECT_NAME].
|
|
785
761
|
|
|
786
762
|
CRITICAL: This is a billing system. Double-spend = revenue loss.
|
|
787
763
|
|
|
@@ -915,19 +891,13 @@ EOF
|
|
|
915
891
|
|
|
916
892
|
**Prompt template:**
|
|
917
893
|
```
|
|
918
|
-
You are auditing PR #[PR_NUMBER] for
|
|
894
|
+
You are auditing PR #[PR_NUMBER] for domain-specific logic correctness in [PROJECT_NAME].
|
|
919
895
|
|
|
920
|
-
##
|
|
921
|
-
AlterLab uses tiered scraping with escalation:
|
|
922
|
-
T1 (curl/httpx) → T2 (HTTP with headers) → T3 (stealth proxy) → T3.5 (light JS) → T4 (full browser/Playwright)
|
|
896
|
+
## Project Domain Architecture
|
|
923
897
|
|
|
924
|
-
|
|
898
|
+
[DOMAIN_CONTEXT]
|
|
925
899
|
|
|
926
|
-
|
|
927
|
-
- **Tier escalation**: `services/worker/worker/unified_consumer.py` — `_parallel_tier_race()`, `get_optimal_starting_tier()`
|
|
928
|
-
- **Domain playbooks**: `services/worker/config/domain_playbooks.json` — domain-specific tier overrides
|
|
929
|
-
- **Cortex intelligence**: `services/worker/worker/cortex_client.py` — learned headers/cookies/min_tier
|
|
930
|
-
- **Content validation**: Determines if scrape succeeded or needs tier escalation
|
|
900
|
+
If no domain context is configured above, derive the architecture from the changed files: read the primary consumer/worker/handler files to understand the execution model.
|
|
931
901
|
|
|
932
902
|
## What to Verify
|
|
933
903
|
1. **Tier selection**: Is the starting tier correct? Does escalation logic work?
|
|
@@ -1096,14 +1066,13 @@ EOF
|
|
|
1096
1066
|
|
|
1097
1067
|
**Prompt template:**
|
|
1098
1068
|
```
|
|
1099
|
-
You are auditing PR #[PR_NUMBER] for frontend quality in
|
|
1069
|
+
You are auditing PR #[PR_NUMBER] for frontend quality in [PROJECT_NAME].
|
|
1100
1070
|
|
|
1101
1071
|
## Project Frontend Conventions
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
- **Auth in frontend**: NextAuth sessions, not direct API key usage
|
|
1072
|
+
|
|
1073
|
+
[DOMAIN_CONTEXT]
|
|
1074
|
+
|
|
1075
|
+
If no frontend context is configured above, derive conventions from the changed files: check package.json for framework versions and look at existing patterns in surrounding components.
|
|
1107
1076
|
|
|
1108
1077
|
## What to Check
|
|
1109
1078
|
1. **Server vs Client components**: Is 'use client' used appropriately? Could this be a Server Component instead?
|
|
@@ -1176,15 +1145,13 @@ EOF
|
|
|
1176
1145
|
|
|
1177
1146
|
**Prompt template:**
|
|
1178
1147
|
```
|
|
1179
|
-
You are auditing PR #[PR_NUMBER] for API design consistency in
|
|
1148
|
+
You are auditing PR #[PR_NUMBER] for API design consistency in [PROJECT_NAME].
|
|
1180
1149
|
|
|
1181
1150
|
## Project API Conventions
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
- **Auth**: Every endpoint must have explicit auth dependency (see Auth Conventions)
|
|
1187
|
-
- **Naming**: snake_case for Python, camelCase for JSON response fields (if using alias)
|
|
1151
|
+
|
|
1152
|
+
[DOMAIN_CONTEXT]
|
|
1153
|
+
|
|
1154
|
+
If no API context is configured above, derive conventions from the changed files: read the main router/application entrypoint to understand registration patterns, and check neighboring endpoints for error format and naming conventions.
|
|
1188
1155
|
|
|
1189
1156
|
## What to Check
|
|
1190
1157
|
1. **Registration**: Is the new router registered in main.py?
|
|
@@ -1302,7 +1269,9 @@ EOF
|
|
|
1302
1269
|
|
|
1303
1270
|
**Prompt template:**
|
|
1304
1271
|
```
|
|
1305
|
-
You are auditing PR #[PR_NUMBER] for database changes in
|
|
1272
|
+
You are auditing PR #[PR_NUMBER] for database changes in [PROJECT_NAME].
|
|
1273
|
+
|
|
1274
|
+
[DOMAIN_CONTEXT]
|
|
1306
1275
|
|
|
1307
1276
|
## What to Check
|
|
1308
1277
|
1. **SQL correctness** (CHECK FIRST — a query that errors at runtime breaks the whole page):
|
|
@@ -1393,14 +1362,13 @@ EOF
|
|
|
1393
1362
|
|
|
1394
1363
|
**Prompt template:**
|
|
1395
1364
|
```
|
|
1396
|
-
You are auditing PR #[PR_NUMBER] for infrastructure and deployment safety in
|
|
1365
|
+
You are auditing PR #[PR_NUMBER] for infrastructure and deployment safety in [PROJECT_NAME].
|
|
1397
1366
|
|
|
1398
1367
|
## Deployment Architecture
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
- **CI/CD**: GitHub Actions deploys on merge to `main`
|
|
1368
|
+
|
|
1369
|
+
[DOMAIN_CONTEXT]
|
|
1370
|
+
|
|
1371
|
+
If no infra context is configured above, derive the deployment model from the changed files: read docker-compose files, GitHub Actions workflows, and entrypoint scripts to understand the deployment pipeline.
|
|
1404
1372
|
|
|
1405
1373
|
## What to Check
|
|
1406
1374
|
1. **Downtime risk**: Will this change cause container restarts during deploy?
|
|
@@ -1516,27 +1484,21 @@ You are auditing PR #[PR_NUMBER] for infrastructure and deployment safety in Alt
|
|
|
1516
1484
|
- Common siblings: `ci.yml` ↔ `deploy-production.yml` (share `test-api`, `test-web` jobs), `deploy-production.yml` ↔ `hotfix-deploy.yml` (share build/deploy logic)
|
|
1517
1485
|
**This is the #1 cause of "CI passed but deploy failed" incidents.** Do not skip this check.
|
|
1518
1486
|
10. **Deploy scope awareness** (ALWAYS run — checks whether ALL changed services will actually be deployed):
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|-------------|-----------------|-----------------|
|
|
1522
|
-
| `web/**`, `services/api/**` | `deploy-production.yml` | Yes (on merge to main) |
|
|
1523
|
-
| `services/worker/**` | `deploy-production.yml` | Yes (on merge to main) |
|
|
1524
|
-
| `services/session-ingest/**` | `deploy-e2e.yml` | **NO — manual trigger required** |
|
|
1525
|
-
| `services/herald/**` | `deploy-e2e.yml` | **NO — manual trigger required** |
|
|
1526
|
-
| `services/cortex/**` | `deploy-e2e.yml` | **NO — manual trigger required** |
|
|
1527
|
-
|
|
1528
|
-
**Steps**:
|
|
1487
|
+
This project may have multiple deploy pipelines. Check which pipelines cover which services:
|
|
1488
|
+
|
|
1529
1489
|
```bash
|
|
1490
|
+
# List all GitHub Actions workflows
|
|
1491
|
+
ls .github/workflows/
|
|
1530
1492
|
# List all paths changed in this PR
|
|
1531
1493
|
gh pr diff [PR_NUMBER] --name-only
|
|
1532
1494
|
```
|
|
1533
|
-
|
|
1534
|
-
- If
|
|
1535
|
-
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1495
|
+
|
|
1496
|
+
For each changed path: read the `.github/workflows/` directory to determine which workflow deploys it and whether it's auto-triggered or requires manual action. If any changed service path is NOT covered by an auto-triggered workflow on merge:
|
|
1497
|
+
- Flag as CONFIRMED BLOCKING finding
|
|
1498
|
+
- Verdict: `BLOCK — Deploy prerequisite: manually trigger [workflow] for [service names] before or after this deploy`
|
|
1499
|
+
- Rationale: Services not covered by the auto-deploy pipeline will not be updated on merge, leaving a broken contract between deployed and un-deployed layers.
|
|
1500
|
+
|
|
1501
|
+
If `[DOMAIN_CONTEXT]` above contains a deploy pipeline table, use it as the authoritative source for path → pipeline mapping.
|
|
1540
1502
|
11. **Config field type/doc-comment consistency** (when the diff introduces new pydantic-settings fields):
|
|
1541
1503
|
When a new env var is introduced as a pydantic-settings field with a collection type (`list[str]`, `List[str]`, `set[str]`, `Set[str]`), cross-reference the field's type annotation against the format hint documented in `.env.example`. **pydantic-settings v2 parses collection-type fields via `json.loads()` — they require JSON array format like `["a","b"]`, NOT comma-separated format like `a,b`**. A doc comment saying "comma-separated" on a `list[str]` field is a CONFIRMED HIGH finding — it guarantees a startup crash for anyone following the documented format.
|
|
1542
1504
|
```bash
|
|
@@ -1610,7 +1572,13 @@ You are auditing PR #[PR_NUMBER] for infrastructure and deployment safety in Alt
|
|
|
1610
1572
|
The INFRA agent's secret delivery chain verification (env var present in ENV_MAPPING) does NOT verify whether the fallback value is safe. A config file can pass all delivery chain checks while still containing an active placeholder credential for any environment where the env var is absent. This check targets that gap. <!-- Added: forge#301 -->
|
|
1611
1573
|
|
|
1612
1574
|
14. **Host-side database tool invocations in deploy scripts** (when `.github/workflows/*.yml`, `scripts/deploy*.sh`, or any deploy entrypoint script in the diff invokes `psql`, `createdb`, or `pg_dump` directly on the host):
|
|
1613
|
-
Host-side `psql`, `createdb`, and `pg_dump` connect to PostgreSQL via the host's port binding (`localhost:5432` or `127.0.0.1:5432`). In SSH-based deploy contexts, the host-side port mapping to PostgreSQL's Docker container is unreliable — the listening address may differ from what the SSH session can reach, and the mapping is not guaranteed to be present at all in hardened host configurations. The
|
|
1575
|
+
Host-side `psql`, `createdb`, and `pg_dump` connect to PostgreSQL via the host's port binding (`localhost:5432` or `127.0.0.1:5432`). In SSH-based deploy contexts, the host-side port mapping to PostgreSQL's Docker container is unreliable — the listening address may differ from what the SSH session can reach, and the mapping is not guaranteed to be present at all in hardened host configurations. The safe pattern is to use `docker exec <postgres-container>` for all database operations in deploy scripts.
|
|
1576
|
+
|
|
1577
|
+
To find the correct container name, grep the project's docker-compose files:
|
|
1578
|
+
```bash
|
|
1579
|
+
grep -n "container_name" docker-compose*.yml | grep -i "postgres\|pg\|db"
|
|
1580
|
+
```
|
|
1581
|
+
Use the container name found (referred to as `[DB_CONTAINER]` in examples below).
|
|
1614
1582
|
|
|
1615
1583
|
**Scan for host-side database tool invocations:**
|
|
1616
1584
|
```bash
|
|
@@ -1623,13 +1591,13 @@ You are auditing PR #[PR_NUMBER] for infrastructure and deployment safety in Alt
|
|
|
1623
1591
|
**For each match, classify:**
|
|
1624
1592
|
- `psql`, `createdb`, `pg_dump`, or `pg_restore` called directly (not via `docker exec`): **CONFIRMED HIGH**
|
|
1625
1593
|
- Rationale: Host-side port mappings to PostgreSQL's Docker container are unreliable in SSH deploy contexts. A deploy step that fails to connect leaves post-deploy database setup incomplete — silently if `continue-on-error: true` is set, or rolls back a successful deploy if not.
|
|
1626
|
-
- The safe pattern
|
|
1627
|
-
- `psql`/`createdb`/`pg_dump` called inside `docker exec
|
|
1594
|
+
- The safe pattern: `docker exec [DB_CONTAINER] psql -U $POSTGRES_USER -d $POSTGRES_DB -c "..."` or `docker exec [DB_CONTAINER] createdb -U $POSTGRES_USER $DB_NAME`
|
|
1595
|
+
- `psql`/`createdb`/`pg_dump` called inside `docker exec [DB_CONTAINER] ...`: **OK** — this is the safe pattern.
|
|
1628
1596
|
|
|
1629
|
-
**Recommended fix**: Replace host-side invocations with `docker exec
|
|
1630
|
-
- `psql -h localhost -U $USER -d $DB -c "..."` → `docker exec
|
|
1631
|
-
- `createdb -h localhost -U $USER $DB_NAME` → `docker exec
|
|
1632
|
-
- `pg_dump -h localhost -U $USER $DB` → `docker exec
|
|
1597
|
+
**Recommended fix**: Replace host-side invocations with `docker exec [DB_CONTAINER]` equivalents:
|
|
1598
|
+
- `psql -h localhost -U $USER -d $DB -c "..."` → `docker exec [DB_CONTAINER] psql -U $USER -d $DB -c "..."`
|
|
1599
|
+
- `createdb -h localhost -U $USER $DB_NAME` → `docker exec [DB_CONTAINER] createdb -U $USER $DB_NAME`
|
|
1600
|
+
- `pg_dump -h localhost -U $USER $DB` → `docker exec [DB_CONTAINER] pg_dump -U $USER $DB`
|
|
1633
1601
|
|
|
1634
1602
|
Host-side port bindings to Docker containers are unreliable in SSH deploy contexts. Deploy scripts that invoke database tools directly cannot depend on `localhost:5432` being reachable; the same operation via `docker exec` bypasses the host network entirely and always succeeds when the container is running. <!-- Added: forge#322 -->
|
|
1635
1603
|
|