Skip to content

Commit e082644

Browse files
Merge pull request #1403 from corbitsdev/cl-10528-scripts-dedupe
refactor(scripts): dedupe percentile and release helpers
2 parents e88fda1 + 50f55f6 commit e082644

6 files changed

Lines changed: 48 additions & 62 deletions

File tree

‎scripts/approval-forensics.ts‎

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,12 @@ import { join } from "node:path";
1616
import { homedir } from "node:os";
1717

1818
import { findAll } from "./find-all.js";
19+
import { percentile } from "./percentile.js";
1920
import {
2021
APPROVAL_LOG_FILE,
2122
type ApprovalRecord,
2223
} from "../src/permission/approval-log.js";
2324

24-
function percentile(sorted: readonly number[], p: number): number {
25-
if (sorted.length === 0) return 0;
26-
const index = Math.min(
27-
sorted.length - 1,
28-
Math.floor((p / 100) * sorted.length),
29-
);
30-
const value = sorted[index];
31-
if (value === undefined) return 0;
32-
return value;
33-
}
34-
3525
interface Bucket {
3626
count: number;
3727
byOutcome: Map<string, number>;

‎scripts/intervention-forensics.ts‎

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,12 @@ import { join } from "node:path";
2525
import { homedir } from "node:os";
2626

2727
import { findAll } from "./find-all.js";
28+
import { percentile } from "./percentile.js";
2829
import {
2930
INTERVENTION_FILE,
3031
type InterventionRecord,
3132
} from "../src/subagent/intervention-log.js";
3233

33-
function percentile(sorted: readonly number[], p: number): number {
34-
if (sorted.length === 0) return 0;
35-
const index = Math.min(
36-
sorted.length - 1,
37-
Math.floor((p / 100) * sorted.length),
38-
);
39-
const value = sorted[index];
40-
if (value === undefined) return 0;
41-
return value;
42-
}
43-
4434
interface Bucket {
4535
count: number;
4636
byFamily: Map<string, number>;

‎scripts/percentile.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Shared percentile for the forensics scripts. `sorted` must be sorted in
2+
// ascending order; an empty array yields 0.
3+
export function percentile(sorted: readonly number[], p: number): number {
4+
if (sorted.length === 0) return 0;
5+
const index = Math.min(
6+
sorted.length - 1,
7+
Math.floor((p / 100) * sorted.length),
8+
);
9+
const value = sorted[index];
10+
if (value === undefined) return 0;
11+
return value;
12+
}

‎scripts/release.sh‎

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ TAP_REPO="corbitsdev/homebrew-tap" # tap repo (formula)
4747
TAP_SLUG="corbitsdev/tap" # `brew tap' name of TAP_REPO
4848
BINARY="corbits" # CLI binary + tarball/deb stem
4949
BREW_FORMULA="corbits-code" # `brew install` name (file Formula/$BREW_FORMULA.rb)
50-
FORMULA="$BINARY" # legacy alias used in package paths
5150
DESC="Single-process coding agent CLI built on the Interchange runtime"
5251
DOC_FILES=(LICENSE.md README.md CHANGELOG.md GPLv2-AI-Exception.md GPL-2.0.txt) # shipped with the binary
5352

@@ -94,6 +93,9 @@ step() { printf '\n\033[1;34m==>\033[0m \033[1m%s\033[0m\n' "$*"; }
9493
info() { printf ' %s\n' "$*"; }
9594
skip() { printf ' \033[2m(skip) %s\033[0m\n' "$*"; }
9695
die() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
96+
is_blank() { # is_blank STRING — true when STRING is empty or only whitespace
97+
[ -z "$(printf '%s' "$1" | sed '/^[[:space:]]*$/d')" ]
98+
}
9799

98100
# Push a repo's HEAD (and optionally a ref) to origin. HTTPS remotes authenticate
99101
# via `gh auth git-credential` (token never lands in argv/URLs/process lists).
@@ -196,21 +198,27 @@ tar_root() { # tar_root OUTPUT.tgz DIR PATH...
196198
fi
197199
}
198200

201+
# First-party plugins ship next to the binary (discoverRepoPlugins execPath);
202+
# copy them into a package payload dir or a tarball staging dir.
203+
copy_plugins() { # copy_plugins DEST_DIR
204+
if [ -d "$ROOT/plugins" ]; then
205+
cp -R "$ROOT/plugins" "$1/plugins"
206+
fi
207+
}
208+
199209
# Build a Debian package by hand: a .deb is an `ar` archive of debian-binary,
200210
# control.tar.gz, and data.tar.gz (in that order). `ar rcS` keeps that order
201211
# and skips the symbol table macOS `ar` would otherwise prepend.
202212
build_deb() { # build_deb BINARY DEB-ARCH OUTPUT.deb
203213
local bin=$1 arch=$2 out=$3
204214
local wd; wd=$(mktemp -d)
205-
mkdir -p "$wd/data/usr/bin" "$wd/data/usr/share/doc/$FORMULA" "$wd/ctrl"
206-
install -m 0755 "$bin" "$wd/data/usr/bin/$FORMULA"
207-
for f in "${DOC_FILES[@]}"; do cp "$ROOT/$f" "$wd/data/usr/share/doc/$FORMULA/"; done
208-
if [ -d "$ROOT/plugins" ]; then
209-
cp -R "$ROOT/plugins" "$wd/data/usr/bin/plugins"
210-
fi
215+
mkdir -p "$wd/data/usr/bin" "$wd/data/usr/share/doc/$BINARY" "$wd/ctrl"
216+
install -m 0755 "$bin" "$wd/data/usr/bin/$BINARY"
217+
for f in "${DOC_FILES[@]}"; do cp "$ROOT/$f" "$wd/data/usr/share/doc/$BINARY/"; done
218+
copy_plugins "$wd/data/usr/bin"
211219
local kb; kb=$(( ( $(wc -c < "$bin") + 1023 ) / 1024 ))
212220
cat > "$wd/ctrl/control" <<EOF
213-
Package: $FORMULA
221+
Package: $BINARY
214222
Version: $VERSION
215223
Architecture: $arch
216224
Maintainer: $MAINTAINER
@@ -377,13 +385,14 @@ if [ -n "$NOTES_FILE" ]; then
377385
else
378386
[ -f CHANGELOG.md ] || die "CHANGELOG.md missing at repo root"
379387
SECTION=$(changelog_section "$VERSION")
380-
if [ -n "$(printf '%s' "$SECTION" | sed '/^[[:space:]]*$/d')" ]; then
388+
if ! is_blank "$SECTION"; then
381389
NOTES_SOURCE="CHANGELOG.md ## [$VERSION] (hand-written)"
382390
else
383391
info "no ## [$VERSION] section — generating notes from merged pull requests"
384392
SECTION=$(generate_notes) || die "gh could not generate release notes for $TAG"
385-
[ -n "$(printf '%s' "$SECTION" | sed '/^[[:space:]]*$/d')" ] || \
393+
if is_blank "$SECTION"; then
386394
die "GitHub returned empty release notes for $TAG"
395+
fi
387396
# Deferred, not written here: the release commit step below refuses to run
388397
# on a dirty tree, so touching a tracked file during preflight would abort
389398
# every generated-notes release before it started.
@@ -432,7 +441,7 @@ else
432441
git add CHANGELOG.md
433442
info "wrote generated ## [$VERSION] section into CHANGELOG.md"
434443
fi
435-
git commit -q -m "chore(release): $FORMULA $VERSION"
444+
git commit -q -m "chore(release): $BINARY $VERSION"
436445
info "committed release $VERSION (PR and tag deferred until after build)"
437446
fi
438447

@@ -441,23 +450,20 @@ step "Build standalone binaries and packages"
441450
mkdir -p "$STAGE"
442451
for entry in "${TARGETS[@]}"; do
443452
IFS='|' read -r label target kind debarch <<< "$entry"
444-
pkg="$FORMULA-$VERSION-$label"
453+
pkg="$BINARY-$VERSION-$label"
445454
tarball="$STAGE/$pkg.tar.gz"
446455
if [ -f "$tarball" ] && [ -f "$tarball.sha256" ]; then
447456
skip "$pkg.tar.gz already built"
448457
else
449458
info "compiling $label ($target)"
450-
bin="$STAGE/$FORMULA-$label.bin"
459+
bin="$STAGE/$BINARY-$label.bin"
451460
compile_bin "$target" "$bin"
452461
[ -s "$bin" ] || die "compile produced no binary for $label"
453462
smoke_bin "$label" "$bin"
454463
rm -rf "$STAGE/$pkg"; mkdir -p "$STAGE/$pkg"
455-
cp "$bin" "$STAGE/$pkg/$FORMULA"; chmod 755 "$STAGE/$pkg/$FORMULA"
464+
cp "$bin" "$STAGE/$pkg/$BINARY"; chmod 755 "$STAGE/$pkg/$BINARY"
456465
for f in "${DOC_FILES[@]}"; do cp "$ROOT/$f" "$STAGE/$pkg/"; done
457-
# First-party plugins sit next to the binary (discoverRepoPlugins execPath).
458-
if [ -d "$ROOT/plugins" ]; then
459-
cp -R "$ROOT/plugins" "$STAGE/$pkg/plugins"
460-
fi
466+
copy_plugins "$STAGE/$pkg"
461467
tar -C "$STAGE" -czf "$tarball" "$pkg"
462468
( cd "$STAGE" && shasum -a 256 "$pkg.tar.gz" > "$pkg.tar.gz.sha256" )
463469
rm -rf "$STAGE/$pkg"
@@ -466,11 +472,11 @@ for entry in "${TARGETS[@]}"; do
466472

467473
# Debian package for the Linux targets.
468474
if [ "$kind" = "linux" ] && [ "$SKIP_DEB" != 1 ]; then
469-
deb="$STAGE/${FORMULA}_${VERSION}_${debarch}.deb"
475+
deb="$STAGE/${BINARY}_${VERSION}_${debarch}.deb"
470476
if [ -f "$deb" ] && [ -f "$deb.sha256" ]; then
471477
skip "$(basename "$deb") already built"
472478
else
473-
bin="$STAGE/$FORMULA-$label.bin"
479+
bin="$STAGE/$BINARY-$label.bin"
474480
[ -s "$bin" ] || { # tarball was cached: recompile the raw binary for the deb
475481
info "recompiling $label for .deb"
476482
compile_bin "$target" "$bin"
@@ -482,7 +488,7 @@ for entry in "${TARGETS[@]}"; do
482488
info "packaged $(basename "$deb") ($(du -h "$deb" | cut -f1))"
483489
fi
484490
fi
485-
rm -f "$STAGE/$FORMULA-$label.bin"
491+
rm -f "$STAGE/$BINARY-$label.bin"
486492
done
487493

488494
# ---- 4. land the release commit on main via PR, then tag ------------------
@@ -505,7 +511,7 @@ else
505511
--json number --jq '.[0].number // empty')
506512
if [ -z "$PR_NUM" ]; then
507513
gh pr create --repo "$MAIN_REPO" --head "$RELEASE_BRANCH" --base main \
508-
--title "chore(release): $FORMULA $VERSION" \
514+
--title "chore(release): $BINARY $VERSION" \
509515
--body "Version bump to $VERSION. Release notes are generated from the pull requests merged since the previous release." >/dev/null
510516
PR_NUM=$(gh pr list --repo "$MAIN_REPO" --head "$RELEASE_BRANCH" --state open \
511517
--json number --jq '.[0].number // empty')
@@ -541,10 +547,10 @@ if git ls-remote --exit-code --tags origin "refs/tags/$TAG" >/dev/null 2>&1; the
541547
else
542548
if ! git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
543549
if [ "$DO_PUSH" = 1 ]; then
544-
git tag -a "$TAG" -m "$FORMULA $VERSION" "origin/main"
550+
git tag -a "$TAG" -m "$BINARY $VERSION" "origin/main"
545551
info "tagged $TAG at origin/main"
546552
else
547-
git tag -a "$TAG" -m "$FORMULA $VERSION"
553+
git tag -a "$TAG" -m "$BINARY $VERSION"
548554
info "tagged $TAG at HEAD (--no-push)"
549555
fi
550556
fi

‎scripts/tool-fingerprint-forensics.ts‎

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { join } from "node:path";
1919
import { homedir } from "node:os";
2020

2121
import { findAll } from "./find-all.js";
22+
import { percentile } from "./percentile.js";
2223

2324
function stableJson(value: unknown): string {
2425
if (value === null || typeof value !== "object") return JSON.stringify(value);
@@ -132,24 +133,16 @@ for (const file of files) {
132133
}
133134

134135
runLengths.sort((a, b) => a - b);
135-
function percentile(p: number): number {
136-
if (runLengths.length === 0) return 0;
137-
const idx = Math.min(
138-
runLengths.length - 1,
139-
Math.floor((p / 100) * runLengths.length),
140-
);
141-
return runLengths[idx] as number;
142-
}
143136

144137
console.log(
145138
JSON.stringify(
146139
{
147140
sessionFilesScanned: files.length,
148141
sessionsWithToolOnlyRun,
149142
totalToolOnlyRuns: runLengths.length,
150-
runLengthP50: percentile(50),
151-
runLengthP90: percentile(90),
152-
runLengthP99: percentile(99),
143+
runLengthP50: percentile(runLengths, 50),
144+
runLengthP90: percentile(runLengths, 90),
145+
runLengthP99: percentile(runLengths, 99),
153146
runLengthMax: runLengths[runLengths.length - 1] ?? 0,
154147
// Largest exact-repeat count observed per period; 1 means no repeat
155148
// beyond the base occurrence.

‎scripts/verify-corbits-only-scope.sh‎

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@ changed="$(
1313
} | sed '/^$/d' | LC_ALL=C sort -u
1414
)"
1515

16-
if [ -z "$changed" ]; then
17-
echo "OK: no forbidden path changes detected."
18-
exit 0
19-
fi
20-
2116
violations="$(printf '%s\n' "$changed" | grep -E "$forbidden" || true)"
2217
if [ -n "$violations" ]; then
2318
echo "SCOPE VIOLATION: changes under vendor/ are not allowed for this wave."

0 commit comments

Comments
 (0)