Skip to content

Commit 8380710

Browse files
committed
fix: address PR governance and review feedback GIT-869c63k1e
Add Sonar auth/org support, GH token for gh api, pagination safeguards, doc naming alignment, and Sonar issue fixes. AI-Agent: Codex/0.92.0 AI-Model: gpt-5.3-codex AI-Gotcha: address PR governance and review feedback GIT-869c63k1e. Add Sonar auth/org support, GH token for gh api, pagination safeguards, doc naming alignment, and Sonar issue fixes. AI-Confidence: medium AI-Tags: infrastructure, services, tests, unit AI-Lifecycle: project AI-Memory-Id: bf652dba AI-Source: heuristic
1 parent 2786409 commit 8380710

4 files changed

Lines changed: 71 additions & 32 deletions

File tree

.github/workflows/pr-governance.yml

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,36 @@ jobs:
1616
- name: Enforce Sonar quality gate and new issues policy
1717
env:
1818
PR_NUMBER: ${{ github.event.pull_request.number }}
19-
SONAR_PROJECT_KEY: ${{ github.repository_owner }}_${{ github.event.repository.name }}
19+
SONAR_ORGANIZATION: ${{ vars.SONAR_ORGANIZATION || github.repository_owner }}
20+
SONAR_PROJECT_KEY: ${{ vars.SONAR_PROJECT_KEY || replace(github.repository, '/', '_') }}
21+
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
22+
SONAR_MAX_POLL_ITERATIONS: "30"
23+
SONAR_POLL_INTERVAL_SECONDS: "10"
2024
run: |
2125
set -euo pipefail
2226
27+
MAX_POLL_ITERATIONS="${SONAR_MAX_POLL_ITERATIONS:-30}"
28+
POLL_INTERVAL_SECONDS="${SONAR_POLL_INTERVAL_SECONDS:-10}"
29+
SONAR_AUTH_ARGS=()
30+
if [[ -n "${SONAR_TOKEN:-}" ]]; then
31+
SONAR_AUTH_ARGS=(-u "${SONAR_TOKEN}:")
32+
fi
33+
2334
quality_status=""
24-
for _ in {1..30}; do
25-
response="$(curl -sS "https://sonarcloud.io/api/project_pull_requests/list?project=${SONAR_PROJECT_KEY}")"
35+
attempt=1
36+
while (( attempt <= MAX_POLL_ITERATIONS )); do
37+
response="$(
38+
curl -sS "${SONAR_AUTH_ARGS[@]}" \
39+
"https://sonarcloud.io/api/project_pull_requests/list?organization=${SONAR_ORGANIZATION}&project=${SONAR_PROJECT_KEY}"
40+
)"
2641
quality_status="$(jq -r --arg pr "${PR_NUMBER}" '.pullRequests[]? | select(.key == $pr) | .status.qualityGateStatus' <<<"$response")"
2742
2843
if [[ -n "$quality_status" && "$quality_status" != "NONE" ]]; then
2944
break
3045
fi
3146
32-
sleep 10
47+
sleep "${POLL_INTERVAL_SECONDS}"
48+
attempt=$((attempt + 1))
3349
done
3450
3551
if [[ -z "$quality_status" || "$quality_status" == "NONE" ]]; then
@@ -42,7 +58,10 @@ jobs:
4258
exit 1
4359
fi
4460
45-
issues_response="$(curl -sS "https://sonarcloud.io/api/issues/search?componentKeys=${SONAR_PROJECT_KEY}&pullRequest=${PR_NUMBER}&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true&ps=1")"
61+
issues_response="$(
62+
curl -sS "${SONAR_AUTH_ARGS[@]}" \
63+
"https://sonarcloud.io/api/issues/search?organization=${SONAR_ORGANIZATION}&componentKeys=${SONAR_PROJECT_KEY}&pullRequest=${PR_NUMBER}&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true&ps=1"
64+
)"
4665
new_issues="$(jq -r '.total // 0' <<<"$issues_response")"
4766
4867
if [[ "$new_issues" != "0" ]]; then
@@ -57,6 +76,7 @@ jobs:
5776
PR_NUMBER: ${{ github.event.pull_request.number }}
5877
REPO_OWNER: ${{ github.repository_owner }}
5978
REPO_NAME: ${{ github.event.repository.name }}
79+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
6080
run: |
6181
set -euo pipefail
6282
@@ -66,11 +86,17 @@ jobs:
6686
pullRequest(number:$number) {
6787
author { login }
6888
reviewThreads(first:100) {
89+
pageInfo {
90+
hasNextPage
91+
}
6992
nodes {
7093
id
7194
isResolved
7295
isOutdated
7396
comments(first:100) {
97+
pageInfo {
98+
hasNextPage
99+
}
74100
nodes {
75101
author { login }
76102
}
@@ -84,6 +110,17 @@ jobs:
84110
response="$(gh api graphql -f query="$query" -F owner="$REPO_OWNER" -F repo="$REPO_NAME" -F number="$PR_NUMBER")"
85111
pr_author="$(jq -r '.data.repository.pullRequest.author.login' <<<"$response")"
86112
113+
has_more_threads="$(jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage' <<<"$response")"
114+
has_more_comments="$(jq -r '
115+
.data.repository.pullRequest.reviewThreads.nodes
116+
| any(.comments.pageInfo.hasNextPage == true)
117+
' <<<"$response")"
118+
119+
if [[ "$has_more_threads" == "true" || "$has_more_comments" == "true" ]]; then
120+
echo "Review thread pagination limit reached; increase pagination handling before enforcing this check." >&2
121+
exit 1
122+
fi
123+
87124
unresolved_count="$(jq -r '
88125
.data.repository.pullRequest.reviewThreads.nodes
89126
| map(select(.isOutdated | not))

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ Rules are automatically loaded as context. See `.claude/rules/`:
118118

119119
## Git Workflow
120120

121-
- Branch per ClickUp task, named with task ID (e.g. `GIT-123abc`)
121+
- Branch per ClickUp task, named using `codex/GIT-<taskId>_<taskName>` (e.g. `codex/GIT-123abc_fix-mcp-auth`)
122122
- Use the SKILL .claude/skills/github/SKILL.md for interacting with GitHub
123123
- PR workflow use the skill .claude/skills/pr/SKILL.md
124124
- Create the PR

src/infrastructure/services/AgentResolver.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import { resolveAgent, resolveModel } from '../detect-agent';
1717
*/
1818
export class AgentResolver implements IAgentResolver {
1919
/** Cached runtime data to avoid repeated file reads. */
20-
private cachedRuntimeData: IRuntimeData | undefined | null = null;
20+
private cachedRuntimeData: IRuntimeData | undefined;
21+
/** Tracks whether runtime data was read from disk. */
22+
private runtimeDataLoaded = false;
2123

2224
constructor(
2325
private readonly runtimeService?: IRuntimeService,
@@ -50,9 +52,9 @@ export class AgentResolver implements IAgentResolver {
5052
* Get cached runtime data, reading from file only once per instance.
5153
*/
5254
private getRuntimeData(): IRuntimeData | undefined {
53-
// null = not yet read, undefined = read but no data
54-
if (this.cachedRuntimeData === null) {
55-
this.cachedRuntimeData = this.runtimeService?.read(this.cwd);
55+
if (!this.runtimeDataLoaded) {
56+
this.cachedRuntimeData ??= this.runtimeService?.read(this.cwd);
57+
this.runtimeDataLoaded = true;
5658
}
5759
return this.cachedRuntimeData;
5860
}

tests/unit/infrastructure/services/RuntimeService.test.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,35 @@ import { tmpdir } from 'node:os';
66
import { RuntimeService } from '../../../../src/infrastructure/services/RuntimeService';
77
import type { IRuntimeData } from '../../../../src/domain/interfaces/IRuntimeService';
88

9+
function createRuntimeData(overrides?: Partial<IRuntimeData>): IRuntimeData {
10+
return {
11+
sessionId: 'test-session-123',
12+
agent: 'Claude-Code/2.1.0',
13+
model: 'claude-opus-4-5-20251101',
14+
timestamp: new Date().toISOString(),
15+
source: 'env:CLAUDECODE',
16+
...overrides,
17+
};
18+
}
19+
20+
function withTestDir(run: (testDir: string) => void): void {
21+
const testDir = mkdtempSync(join(tmpdir(), 'git-mem-runtime-test-'));
22+
try {
23+
run(testDir);
24+
} finally {
25+
if (existsSync(testDir)) {
26+
rmSync(testDir, { recursive: true, force: true });
27+
}
28+
}
29+
}
30+
931
describe('RuntimeService', () => {
1032
let service: RuntimeService;
1133

1234
before(() => {
1335
service = new RuntimeService();
1436
});
1537

16-
function createRuntimeData(overrides?: Partial<IRuntimeData>): IRuntimeData {
17-
return {
18-
sessionId: 'test-session-123',
19-
agent: 'Claude-Code/2.1.0',
20-
model: 'claude-opus-4-5-20251101',
21-
timestamp: new Date().toISOString(),
22-
source: 'env:CLAUDECODE',
23-
...overrides,
24-
};
25-
}
26-
27-
function withTestDir(run: (testDir: string) => void): void {
28-
const testDir = mkdtempSync(join(tmpdir(), 'git-mem-runtime-test-'));
29-
try {
30-
run(testDir);
31-
} finally {
32-
if (existsSync(testDir)) {
33-
rmSync(testDir, { recursive: true, force: true });
34-
}
35-
}
36-
}
37-
3838
describe('activate', () => {
3939
it('should create runtime.json with correct content', () => {
4040
withTestDir((testDir) => {

0 commit comments

Comments
 (0)