Skip to content

ci: add shared<->Hub REST contract gate (A#3; completes P2 gate set) - #1467

Merged
DeliciousBuding merged 1 commit into
masterfrom
ci/gate-p2-audit
Jul 29, 2026
Merged

ci: add shared<->Hub REST contract gate (A#3; completes P2 gate set)#1467
DeliciousBuding merged 1 commit into
masterfrom
ci/gate-p2-audit

Conversation

@DeliciousBuding

@DeliciousBuding DeliciousBuding commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds the only remaining architecture gate from the audit's 5 gate-ifiable
candidates (audit-A "门禁化候选" list): A#3 — shared<->Hub REST contract
validator
.

Heads-up for reviewers: the original P2 task listed 3 gates
(A#5 hub-layering / A#3 shared-rest contract / A#4 edge-export ban), but
A#4 (verify-shared-barrel.ps1) and A#5 (verify-hub-layering.ps1) were
already shipped in #1463 (d4b6c13)
. This PR therefore completes the set by
adding the one that was genuinely missing.

A#3 — scripts/verify/verify-shared-rest-contract.ps1

Hub-frontend hubClient call paths (app/shared/src/hubClient*.ts,
notably hubClientPayloadPaths.ts) must each resolve to a route registered in
hub-server/internal/router/router.go. Catches client<->server REST contract
drift (client calling an endpoint the Hub does not serve).

Normalization (compared on route shape):

  • router gin :id / :user_id -> {param}
  • client ${encodeURIComponent(x)} / ${id} -> {param}
  • ${qs(...)} query builders and ?... stripped
  • client RPC :action (:read-all, :cancel, :register, ...) -> /action
    (the Hub registers the slash form; client uses requestWithFallback)

Empirical baseline (current master)

  • 0 drift across 89 unique client paths — all resolve to a registered hub
    route (incl. the /edge/* paths the hub itself serves).
  • Negative test: injecting buildNonexistentPath() -> /client/this-does-not-exist-on-hub
    makes the gate fail (exit 1) as expected. Gate logic verified.

Wiring (follows #1463 convention)

  • New script under scripts/verify/
  • New validate job step in .github/workflows/checks.yml
  • New policy assertion in scripts/verify/verify-ci-gates.ps1
  • verify-ci-gates.ps1 passes green with the new step.

Test plan

  • pwsh ./scripts/verify/verify-shared-rest-contract.ps1 -> PASS (0 drift)
  • Injected bogus path -> FAIL (exit 1)
  • pwsh ./scripts/verify/verify-ci-gates.ps1 -> ci gate policy ok

Follow-ups (recommendations, not blocking)

  • health.go (A#5 known defect): handler/health.go:13 still imports
    repository directly (whitelisted in ci: 门禁扩展 — shared 边界/barrel/handler 分层/conventions #1463's gate). Suggest routing the
    migration-status health probe through a HealthService to clear the
    known-defect note.
  • Edge apiClient<->edge-server /v1 contract: out of scope here. The edge
    surface is a net/http mux with wildcard sub-paths + gateway rewriting, so a
    strict check is higher-effort; recommend a dedicated follow-up gate.

Summary by CodeRabbit

  • Bug Fixes

    • Added automated validation to detect mismatches between frontend API requests and server REST routes.
    • CI now blocks changes when the shared REST contract is out of sync.
  • Quality Improvements

    • Verification reports scanned paths, detected mismatches, and pass/fail results for clearer diagnostics.

Hub-frontend hubClient call paths (app/shared/src/hubClient*.ts) must each
resolve to a route registered in hub-server/internal/router/router.go.
Catches client<->server REST contract drift.

Empirical baseline (current master): 0 drift across 89 unique client paths
- all resolve to a registered hub route (incl. /edge/* paths the hub serves).
Negative test (injected /client/this-does-not-exist-on-hub) fails exit 1.

Wired into checks.yml validate job and asserted in verify-ci-gates.ps1,
following the #1463 gate convention (scripts/verify/ + validate step + policy
assertion).

Note: A#4 (verify-shared-barrel, Edge-export ban) and A#5
(verify-hub-layering, handler!=repository) were already shipped in #1463
(d4b6c13); this PR completes the audit's 5 gate candidates by adding the
only remaining one, A#3.
Copilot AI review requested due to automatic review settings July 29, 2026 13:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a PowerShell verifier for Hub server/client REST route drift, wires it into the validate workflow, and requires the workflow step to remain hard-blocking.

Changes

Shared REST contract verification

Layer / File(s) Summary
Route extraction and client normalization
scripts/verify/verify-shared-rest-contract.ps1
Resolves grouped server routes and normalizes REST paths discovered in Hub-frontend client modules.
Drift detection and exit status
scripts/verify/verify-shared-rest-contract.ps1
Compares normalized paths, applies known-defect exceptions, reports results, and exits non-zero for failures.
Validate job enforcement
.github/workflows/checks.yml, scripts/verify/verify-ci-gates.ps1
Runs the verifier in validate and asserts that the step is present and hard-blocking.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Validate as GitHub Actions validate job
  participant Verifier as verify-shared-rest-contract.ps1
  participant Router as hub-server router.go
  participant Clients as Hub-frontend hubClient*.ts
  Validate->>Verifier: run contract verification
  Verifier->>Router: read declared routes
  Verifier->>Clients: scan client paths
  Verifier->>Verifier: normalize and compare paths
  Verifier-->>Validate: return PASS or exit 1
Loading

Possibly related PRs

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding the shared-to-Hub REST contract CI gate and wiring it into the gate set.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/gate-p2-audit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@DeliciousBuding
DeliciousBuding merged commit 6d3875c into master Jul 29, 2026
19 of 20 checks passed
@DeliciousBuding
DeliciousBuding deleted the ci/gate-p2-audit branch July 29, 2026 13:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/verify/verify-ci-gates.ps1`:
- Around line 189-191: Update Assert-StepContinueOnError and its validation of
the hard-blocking “Verify Shared REST contract Hub-client to Hub-router” step so
any presence of continue-on-error causes failure, regardless of whether its
value is literal or expression-based. Preserve the existing checks for steps
that are explicitly allowed to continue on error.

In `@scripts/verify/verify-shared-rest-contract.ps1`:
- Around line 134-153: The drift counter in the route comparison loop only
tracks failures, making the allowlisted success message unreachable and
misreporting results. Update the loop around $KnownDefects to maintain a
separate count for allowlisted entries, increment it in the $allowed branch, and
use that count in the final success reporting while preserving $drift for actual
unmatched paths.
- Around line 59-85: Add Go-aware block-comment stripping to the router source
before processing it, removing /* ... */ content while preserving line
boundaries so both the iterative prefix resolution and route extraction loops
operate only on active code. Replace the current //-only filtering in the
$prefix and $hubRoutes parsing paths with the cleaned router lines, while
retaining support for line comments.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cdff300e-5294-4354-89e8-8fedb6943f87

📥 Commits

Reviewing files that changed from the base of the PR and between 727e145 and f6c99b5.

📒 Files selected for processing (3)
  • .github/workflows/checks.yml
  • scripts/verify/verify-ci-gates.ps1
  • scripts/verify/verify-shared-rest-contract.ps1

Comment on lines +189 to +191
Assert-Contains $validate "Verify Shared REST contract Hub-client to Hub-router" "validate job must run the Shared REST contract verifier"
Assert-Contains $validate "scripts/verify/verify-shared-rest-contract\.ps1" "validate job must call scripts/verify/verify-shared-rest-contract.ps1"
Assert-StepContinueOnError $validate "Verify Shared REST contract Hub-client to Hub-router" $false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== git status/diff =="
git status --short --branch || true
git diff --check || true

echo "== target file outline/sections =="
sed -n '1,240p' scripts/verify/verify-ci-gates.ps1 | nl -ba | sed -n '1,240p'

echo "== Assert-StepContinueOnError definitions/usages =="
rg -n "Assert-StepContinueOnError|continue-on-error" scripts/verify/verify-ci-gates.ps1 .github 2>/dev/null || true

Repository: TokenDanceLab/AgentHub

Length of output: 277


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== git status/diff =="
git status --short --branch || true
git diff --check || true

echo "== target file outline/sections =="
sed -n '1,240p' scripts/verify/verify-ci-gates.ps1 | nl -ba | sed -n '1,240p'

echo "== Assert-StepContinueOnError definitions/usages =="
rg -n "Assert-StepContinueOnError|continue-on-error" scripts/ verify scripts/verify/verify-ci-gates.ps1 .github 2>/dev/null || true

Repository: TokenDanceLab/AgentHub

Length of output: 277


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== status/diff =="
git status --short --branch || true
git diff --check || true

echo "== file sizes =="
wc -l scripts/verify/verify-ci-gates.ps1 2>/dev/null || true

echo "== target section =="
sed -n '1,240p' scripts/verify/verify-ci-gates.ps1

echo "== Assert-StepContinueOnError definitions/usages =="
rg -n "Assert-StepContinueOnError|continue-on-error" scripts/verify/verify-ci-gates.ps1 .github 2>/dev/null || true

echo "== behavioral probe: current pattern outcomes =="
python3 - <<'PY'
import re, pathlib
p = pathlib.Path('scripts/verify/verify-ci-gates.ps1')
s = p.read_text() if p.exists() else ''
print("script_exists:", p.exists())
print("contains_assert", "Assert-StepContinueOnError" in s)
print("contains_expected_lines:")
for pat in [
    r'Assert-Contains \$validate "Verify Shared REST contract Hub-client to Hub-router".*validate job must run the Shared REST contract verifier',
    r'Assert-Contains \$validate "scripts/verify/verify-shared-rest-contract\.ps1".*validate job must call scripts/verify/verify-shared-rest-contract.ps1',
    r'Assert-StepContinueOnError \$validate "Verify Shared REST contract Hub-client to Hub-router" \$false'
]:
    print(" pattern", pat)
    print("  matches", bool(re.search(pat, s, re.S)))
for step in ['literal false', 'github expression true', 'dynamic property expression true', 'literal true']:
    step_header = 'Some REST contract check'
    line = {
        'literal false': 'continue-on-error: false',
        'github expression true': 'continue-on-error: ${{ true }}',
        'dynamic property expression true': 'continue-on-error: ${{ true }}',
        'literal true': 'continue-on-error: true',
    }[step]
    yaml = f' - name: {step_header}\n    uses: some/action\n    continue-on-error: {line}'
    # Mimic current pattern-ish check: it should not match if contains literal true
    matches = re.search(r'current step should not have continue-on-error: true', yaml) is not None
    print(step, "-> current check would pass:", not matches, "value:", line)
PY

Repository: TokenDanceLab/AgentHub

Length of output: 21967


Reject any continue-on-error value for hard-blocking steps.

Assert-StepContinueOnError(..., $false) only checks continue-on-error: true, so an expression like continue-on-error: ${{ true }} can still pass while disabling the gate. For expected hard-blocking steps, fail whenever continue-on-error is present instead of only rejecting the literal true.

🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'verify-ci-gates.ps1'

(PSUseBOMForUnicodeEncodedFile)


[info] 189-189: Cmdlet 'Assert-Contains' has positional parameter. Please use named parameters instead of positional parameters when calling a command.

(PSAvoidUsingPositionalParameters)


[info] 190-190: Cmdlet 'Assert-Contains' has positional parameter. Please use named parameters instead of positional parameters when calling a command.

(PSAvoidUsingPositionalParameters)


[info] 191-191: Cmdlet 'Assert-StepContinueOnError' has positional parameter. Please use named parameters instead of positional parameters when calling a command.

(PSAvoidUsingPositionalParameters)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/verify/verify-ci-gates.ps1` around lines 189 - 191, Update
Assert-StepContinueOnError and its validation of the hard-blocking “Verify
Shared REST contract Hub-client to Hub-router” step so any presence of
continue-on-error causes failure, regardless of whether its value is literal or
expression-based. Preserve the existing checks for steps that are explicitly
allowed to continue on error.

Comment on lines +59 to +85
$routerSrc = Get-Content -LiteralPath $routerPath -Raw
$routerLines = $routerSrc -split "`r?`n"

# Resolve group prefixes iteratively (public := r.Group(...), child := parent.Group(...))
$prefix = @{}
$changed = $true
while ($changed) {
$changed = $false
foreach ($ln in $routerLines) {
if ($ln -match '^\s*//') { continue }
if ($ln -match '(\w+)\s*:=\s*(\w+)\.Group\("([^"]*)"') {
$var = $Matches[1]; $parent = $Matches[2]; $seg = $Matches[3]
if ($prefix.ContainsKey($var)) { continue }
if ($parent -eq 'r') { $prefix[$var] = $seg; $changed = $true }
elseif ($prefix.ContainsKey($parent)) { $prefix[$var] = $prefix[$parent] + $seg; $changed = $true }
}
}
}

$hubRoutes = @{}
foreach ($ln in $routerLines) {
if ($ln -match '^\s*//') { continue }
foreach ($m in [regex]::Matches($ln, '(\w+)\.(GET|POST|PUT|DELETE|PATCH)\("([^"]*)"')) {
$var = $m.Groups[1].Value; $path = $m.Groups[3].Value
$base = if ($var -eq 'r') { '' } elseif ($prefix.ContainsKey($var)) { $prefix[$var] } else { continue }
$full = ($base + $path) -replace ':(\w+)', '{param}'
$hubRoutes[$full] = $true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Git status/check:\n'
git status --short --branch || true
git diff --check || true

printf '\nTarget file outline/size:\n'
if [ -f scripts/verify/verify-shared-rest-contract.ps1 ]; then
  wc -l scripts/verify/verify-shared-rest-contract.ps1
  sed -n '1,140p' scripts/verify/verify-shared-rest-contract.ps1
else
  fd -i 'verify-shared-rest-contract' .
fi

printf '\nSearch for comment stripping or regex related to group/route extraction:\n'
rg -n "Get-Content|Comment|strip|///|(/\*|\\\*\/)|group\.GET|\.Group\(|(\w+)\.GET|(\w+)\.POST|(\w+)\.PUT|(\w+)\.DELETE|(\w+)\.PATCH" scripts/verify/verify-shared-rest-contract.ps1 || true

Repository: TokenDanceLab/AgentHub

Length of output: 8010


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Router file relevant sections:\n'
if [ -f hub-server/internal/router/router.go ]; then
  wc -l hub-server/internal/router/router.go
  rg -n 'Group\(|GET|POST|PUT|DELETE|PATCH|//|/\*' hub-server/internal/router/router.go || true
else
  fd -i '^router\.go$' hub-server /
fi

printf '\nPowerShell regex behavior probe for line-skipping/regex matching on Go comment forms:\n'
powershell -NoProfile -NonInteractive -Command '
$routerSrc = @'
// line-commented group := r.Group("admin")
/* block-commented group.GET("/admin-users") */
commented line with group.GET("/admin-users") in it
group := r.Group("admin")
group.GET("/users")
'@
$routerLines = $routerSrc -split "`r?`n"
$prefix = @{}
$changed = $true
while ($changed) {
    $changed = $false
    foreach ($ln in $routerLines) {
        if ($ln -match '^\s*//') { continue }
        if ($ln -match '(\w+)\s*:=\s*(\w+)\.Group\("([^"]*)")') {
            $var = $Matches[1]; $parent = $Matches[2]; $seg = $Matches[3]
            if ($prefix.ContainsKey($var)) { continue }
            if ($parent -eq "r") { $prefix[$var] = $seg; $changed = $true }
            elseif ($prefix.ContainsKey($parent)) { $prefix[$var] = $prefix[$parent] + $seg; $changed = $true }
        }
    }
}
Print = out StringJoin New-Object system.StringJoin `n
$prefix.GetEnumerator() | Sort-Object Key | ForEach-Object { "Prefix $($_.Key) -> $($_.Value)" } | Join-Pipeline -Separator "`n"

$hubRoutes = @{}
foreach ($ln in $routerLines) {
    if ($ln -match '^\s*//') { continue }
    foreach ($m in [regex]::Matches($ln, "(\w+)\.(GET|POST|PUT|DELETE|PATCH)\(\"([^\"]*)\"")) {
        $var = $m.Groups[1].Value; $path = $m.Groups[3].Value
        $base = if ($var -eq "r") { "" } elseif ($prefix.ContainsKey($var)) { $prefix[$var] } else { continue }
        $full = ($base + $path) -replace ":(\w+)", "{param}"
        $hubRoutes[$full] = $true
    }
}
"Routes:"
$hubRoutes.Keys | Sort-Object | ForEach-Object { "$_" } | Join-Pipeline -Separator "`n"
'
' || true

printf '\nPOSIX-aware Go comment extraction probe:\n'
python3 - <<'PY'
import re, pathlib
src = (pathlib.Path('hub-server/internal/router/router.go').read_text() if pathlib.Path('hub-server/internal/router/router.go').exists() else '')
without_comments = re.sub(r'/\*.*?\*/', '', src, flags=re.S)
without_comments = re.sub(r'//.*$', '', without_comments, flags=re.M)
line_skipped = '\n'.join(line for line in src.splitlines() if not line.lstrip().startswith('//'))
print('contains_block_group_GET=', bool(re.search(r'/\*[\s\S]*?group\.GET', src)))
print('contains_block_assignment=', bool(re.search(r'/\*[\s\S]*?\w+\s*:=\s*\w+\.Group', src)))
print('no_block_group_GET_after_strip=', not bool(re.search(r'group\.GET', without_comments)))
print('line_skipped_group_GET=', bool(re.search(r'group\.GET', line_skipped)))
PY

Repository: TokenDanceLab/AgentHub

Length of output: 13425


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Router.go exact comment lines surrounding group declarations (if any):\n'
python3 - <<'PY'
import re
from pathlib import Path
p=Path('hub-server/internal/router/router.go')
if p.exists():
    lines=p.read_text().splitlines()
    for i,l in enumerate(lines,1):
        if re.search(r'^\s*(public|client|auth|comments|sessions|edge|cloud|web)\s*:=', l):
            print(f'{i}: {l!r}')
    for i,l in enumerate(lines,1):
        if 'r.Group' in l or '.Group(' in l:
            print(f'{i}: {l!r}')
PY

printf '\nPowerShell regex behavior probe from stdin instead:\n'
cat > /tmp/parser_probe.ps1 <<'PS'
$tests = @"
// line-commented group := r.Group("/admin")
/* block-commented group.GET("/admin-users") */
commented line with group.GET("/admin-users") in it
group := r.Group("/admin")
group.GET("/users")
"@
$lines = $tests -split "`r?`n"
$prefix = @{}
$changed = $true
while ($changed) {
    $changed = $false
    foreach ($ln in $lines) {
        if ($ln -match '^\s*//') { continue }
        if ($ln -match '(\w+)\s*:=\s*(\w+)\.Group\("([^"]*)') {
            $var = $Matches[1]; $parent = $Matches[2]; $seg = $Matches[3]
            if ($prefix.ContainsKey($var)) { continue }
            if ($parent -eq 'r') { $prefix[$var] = $seg; $changed = $true }
            elseif ($prefix.ContainsKey($parent)) { $prefix[$var] = $prefix[$parent] + $seg; $changed = $true }
        }
    }
}
"Parsed prefixes:"
$prefix.GetEnumerator() | Sort-Object Key | ForEach-Object { "  $($_.Key) -> $($_.Value)" } | Join-Pipeline -Separator "`n"

$routes = @{}
foreach ($ln in $lines) {
    if ($ln -match '^\s*//') { continue }
    foreach ($m in [regex]::Matches($ln, '(\w+)\.(GET|POST|PUT|DELETE|PATCH)\("([^"]*)"')) {
        $var = $m.Groups[1].Value; $path = $m.Groups[3].Value
        $base = if ($var -eq 'r') { '' } elseif ($prefix.ContainsKey($var)) { $prefix[$var] } else { continue }
        $full = ($base + $path) -replace ':(\w+)', '{param}'
        $routes[$full] = $true
    }
}
"Parsed routes:"
$routes.Keys | Sort-Object | ForEach-Object { "  $_" } | Join-Pipeline -Separator "`n"
PS
pwsh -NoProfile -NonInteractive /tmp/parser_probe.ps1

Repository: TokenDanceLab/AgentHub

Length of output: 2063


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Router current comment forms around active group/route declarations (lines 50-100):\n'
sed -n '50,100p' hub-server/internal/router/router.go | cat -n

printf '\nPowerShell regex behavior probe for line-skipping/regex matching on Go comment forms:\n'
pwsh -NoProfile -NonInteractive -Command '
$routerSrc = @"
// line-commented group := r.Group("/admin")
/* block-commented group.GET("/admin-users") */
commented line with group.GET("/admin-users") in it
group := r.Group("/admin")
group.GET("/users")
"@
$routerLines = $routerSrc -split "`r?`n"
$prefix = @{}
$changed = $true
while ($changed) {
    $changed = $false
    foreach ($ln in $routerLines) {
        if ($ln -match "^\s*//") { continue }
        if ($ln -match "(\w+)\s*:=\s*(\w+)\.Group\(\"([^\"]*\")") {
            $var = $Matches[1]; $parent = $Matches[2]; $seg = $Matches[3]
            if ($prefix.ContainsKey($var)) { continue }
            if ($parent -eq "r") { $prefix[$var] = $seg; $changed = $true }
            elseif ($prefix.ContainsKey($parent)) { $prefix[$var] = $prefix[$parent] + $seg; $changed = $true }
        }
    }
}
Write-Host "Parsed prefixes:"
$prefix.GetEnumerator() | Sort-Object Key | ForEach-Object { Write-Host "  $($_.Name) -> $($_.Value)" }
$hubRoutes = @{}
foreach ($ln in $routerLines) {
    if ($ln -match "^\s*//") { continue }
    foreach ($m in [regex]::Matches($ln, "(\w+)\.(GET|POST|PUT|DELETE|PATCH)\(\"([^\"]*\")")) {
        $var = $m.Groups[1].Value; $path = $m.Groups[3].Value
        $base = if ($var -eq "r") { "" } elseif ($prefix.ContainsKey($var)) { $prefix[$var] } elseif ($path -like "*admin-users*") { $true }
        $full = ($base + $path) -replace ":(\w+)", "{param}"
        $hubRoutes[$full] = $true
    }
}
Write-Host "Parsed routes:"
$hubRoutes.Keys | Sort-Object | ForEach-Object { Write-Host "  $_" }
'

Repository: TokenDanceLab/AgentHub

Length of output: 2982


Use Go-aware comment stripping before route extraction.

The parser only skips //-only lines, so group.GET("/admin-users") inside a /* ... */ block is still added to the declared route set. Strip /* ... */ comments for both group prefix resolution and route extraction.

🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'verify-shared-rest-contract.ps1'

(PSUseBOMForUnicodeEncodedFile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/verify/verify-shared-rest-contract.ps1` around lines 59 - 85, Add
Go-aware block-comment stripping to the router source before processing it,
removing /* ... */ content while preserving line boundaries so both the
iterative prefix resolution and route extraction loops operate only on active
code. Replace the current //-only filtering in the $prefix and $hubRoutes
parsing paths with the cleaned router lines, while retaining support for line
comments.

Comment on lines +134 to +153
$drift = 0
foreach ($k in ($normPaths.Keys | Sort-Object)) {
if ($hubRoutes.ContainsKey($k)) { continue }
$allowed = $false
foreach ($d in $KnownDefects) { if ($d -eq $k) { $allowed = $true; break } }
if ($allowed) {
Write-Host " KNOWN DEFECT client path has no hub route: $k (allowlisted)" -ForegroundColor Yellow
} else {
$sample = ($rawPaths | Where-Object { (Normalize-ClientPath $_) -eq $k } | Select-Object -First 1) -join ""
Fail "client path has no matching hub route: $k (from '$sample')"
$drift++
}
}

if ($Failed -eq 0) {
if ($drift -eq 0) {
Pass "all Hub-client call paths resolve to a registered hub route ($($normPaths.Count) unique path(s))"
} else {
Pass "no new client<->hub contract drift ($drift known-defect note(s) in allowlist)"
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Fix allowlisted-drift reporting.

$drift increments only in the branch that also calls Fail, so the $drift -ne 0 success branch is unreachable. Once $KnownDefects is populated, the script incorrectly reports that every path resolves. Track allowlisted entries separately and use that count in the success message.

🧰 Tools
🪛 PSScriptAnalyzer (1.25.0)

[warning] Missing BOM encoding for non-ASCII encoded file 'verify-shared-rest-contract.ps1'

(PSUseBOMForUnicodeEncodedFile)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/verify/verify-shared-rest-contract.ps1` around lines 134 - 153, The
drift counter in the route comparison loop only tracks failures, making the
allowlisted success message unreachable and misreporting results. Update the
loop around $KnownDefects to maintain a separate count for allowlisted entries,
increment it in the $allowed branch, and use that count in the final success
reporting while preserving $drift for actual unmatched paths.

DeliciousBuding added a commit that referenced this pull request Jul 29, 2026
Add A#3 Hub<->router REST contract gate row (#1467) and a 2026-07-29
session-log entry covering P2 closure + P3 adjudication (D-V1 DEFER via #867;
5 PLAN issues #1469-#1473; A-V2 covered by #1463; A-V5 adopted).
DeliciousBuding added a commit that referenced this pull request Jul 29, 2026
Add A#3 Hub<->router REST contract gate row (#1467) and a 2026-07-29
session-log entry covering P2 closure + P3 adjudication (D-V1 DEFER via #867;
5 PLAN issues #1469-#1473; A-V2 covered by #1463; A-V5 adopted).
DeliciousBuding added a commit that referenced this pull request Jul 29, 2026
* docs: record P2/P3 audit closure in MASTER.md (#1467, #1469-#1473)

Add A#3 Hub<->router REST contract gate row (#1467) and a 2026-07-29
session-log entry covering P2 closure + P3 adjudication (D-V1 DEFER via #867;
5 PLAN issues #1469-#1473; A-V2 covered by #1463; A-V5 adopted).

* docs(progress): A-V2 澄清 + #1468 门禁入 14 层索引

修正 P3 裁决记录:A-V2 非「由 #1463 覆盖」。p2 关门前补 A-V2 专属门禁 PR #1468
(shared-UI 禁运行时 import hubClient,类型导入放行),与 #1463「平台↛共享Edge」
是依赖图相反两侧的互补面,master 上 0 违规。#1468 rebase 至最新 master 解
checks.yml/verify-ci-gates.ps1 加法冲突,anti-cheat 通过,MERGEABLE 待 CI。
14 层门禁索引补 shared-rest-contract · shared-ui-hubclient。

* docs(progress): 同步 MASTER tip 至 #1468 (14 层门禁全合入)

#1468 A-V2 shared-UI hubClient 门禁已合入 master 54c2186。
- live tip 更新至 54c2186
- 14 层 validate 门禁全部合入(shared-rest-contract + shared-ui-hubclient)
- A7 候选余量「shared UI hubClient」标为 A-V2 已闭环
- 综合审计 top10 落地 6/10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants