๐งฌ repoctx Absorption Study¶
Date: 2026-05-29 Author: Claude (with Oluwasegun Olumbe) Status: Phase 1 implementation paused mid-flight to capture this record.
This document captures the field test, design decisions, and refined plan for
absorbing impact-map (Python) and pullpass (Go) into repoctx
(TypeScript/Node) so there is one local-first tool, one binary, one MCP server,
and one risk vocabulary.
๐ Table of contents¶
- The three tools, briefly
- Decisions taken
- Field test โ bashbop-api
- Performance
- Five lessons from running them
- Refined absorption plan
- Phase 1 status
- Open follow-ups
๐งฐ The three tools, briefly¶
| Tool | Stack | Core LOC | What it does |
|---|---|---|---|
| repoctx | TS/Node | ~7,000 | Repo indexing, AST code map, context packs, PR review, MCP server |
| impact-map | Python | ~1,200 | "English change request โ likely files + tests + risks" |
| pullpass | Go | ~2,500 | "Can this PR merge?" โ PASS/WARN/FAIL with policy profiles |
Key observation discovered while reading the source: pullpass's local
evaluator already shells out to repoctx context and repoctx pr as its
"Context Evidence" (pullpass/internal/local/evaluate.go:65-70).
The dependency arrow already points toward repoctx. Absorption is reversing a
relationship that is halfway there.
โ Decisions taken¶
| Question | Choice | Rationale |
|---|---|---|
| Absorption mode | Port logic to TypeScript | One install, one binary, one MCP server. Kills cross-runtime friction. |
| Standalone repos | Archive after migration | Avoid dual maintenance. Existing installs keep working; READMEs point to repoctx. |
Output style¶
User requested "very fancy and nicely formatted with emojis etc." โ the new
renderer (src/lib/render/fancy.js) becomes the default; plain mode via
--no-emoji (or NO_EMOJI=1) for CI logs.
๐ฌ Field test โ bashbop-api¶
Repo: /Users/segzy/projects/bashbop-api (NestJS, Prisma, Stripe SDK,
3,140 source-like files, payment-heavy).
๐ฏ Query 1 โ "add Stripe refunds to bookings"¶
| Rank | ๐ impact-map | ๐ฆ repoctx context |
|---|---|---|
| 1 | โ documentation/styles/stripe.css (score 128) |
โ
scripts/sync-database-with-stripe.ts (50) |
| 2 | โ scripts/check-orphaned-bookings-in-stripe.js (119) |
โ
src/guests/dto/guest.update.dto.ts (34) |
| 3 | โ
src/booking/booking.controller.ts (112) |
๐ฏ src/payment/processors/stripe.processor.ts (34) |
| 4 | โ documentation/controllers/StripeController.html (102) |
โ
src/booking/booking.controller.ts (31) |
| 5 | โ src/scripts/stripe-db-sync-matcher.js (100) |
โ |
๐จ impact-map ranked a generated CSS file as the #1 owner of "add Stripe refunds." The real owner
stripe.processor.tsappears in repoctx's top 3 but not in impact-map's top 8.
Why it happened โ scoring.py:170 gives
path matches: stripe +9 per token, plus a +4 "configuration/domain hint"
because stripe is in CONFIG_HINTS, plus an +88 import-graph boost from
sibling files. No domain or kind awareness penalises documentation/ or .css.
๐ฏ Query 2 โ "fix Apple sign-in token validation"¶
| Rank | ๐ impact-map | ๐ฆ repoctx context |
|---|---|---|
| 1 | โ src/shared/validation/currency-validation.service.ts (49) |
โ src/shared/validation/currency-validation.service.ts (59) |
| 2 | โ documentation/injectables/CurrencyValidationService.html (35) |
๐ฏ src/authentication/auth.controller.ts (50) |
| 3 | โ documentation/interfaces/WebhookValidationResult.html (35) |
โ
src/guests/guest.controller.ts (28) |
| 4 | โ documentation/interfaces/CurrencyValidationResult.html (34) |
โ
src/utils/pipes/sanitizing-validation.pipe.ts (25) |
Both tools overweight "validation". repoctx recovers with the auth controller at #2; impact-map's top 4 is dominated by generated HTML docs.
๐ก๏ธ pullpass โ auth + Prisma migration diff (HEAD~5)¶
Verdict: WARN
โ
Changed files 9 files
โ
Secret safety no secrets touched
โ ๏ธ Risk review 5 sensitive paths (prisma/*, auth.controller.ts, config schema)
โ
Release discipline no version bump expected
โ
Validation commands yarn test ยท yarn lint ยท yarn check:type
โ
Dependency audit commands available
โ ๏ธ Review state local mode can't verify approvals
โ
Policy profile standard
๐งช repoctx pr โ same diff¶
Risk: high (11)
Changed Domains: prisma (3, +2997/-1), authentication (1, +287/-0), user, config, utils, test
Risk Flags: auth/security ยท data model ยท large PR ยท large file diff ยท request surface
Review Targets: 20 auth routes mapped to auth.controller.ts
Verification: yarn lint ยท yarn check:type ยท yarn test ยท yarn test:e2e ยท yarn build
๐ค Verdict: complementary, not competing. pullpass gives a clean verdict;
repoctx pr gives rich context. After absorption these merge into
repoctx review.
โก Performance¶
Same repo, same query, cold-ish runs:
impact-map . "fix Apple sign-in token validation" --top 5 --json โ 8.99s
repoctx context "fix Apple sign-in token validation" --json โ 1.72s
5.2ร faster
- impact-map re-scans every call.
- repoctx caches
.dev-context/index.jsonand refreshes incrementally.
For an MCP server called repeatedly by an agent, this is a material UX win.
๐ง Five lessons from running them¶
1. ๐ชฆ impact-map's scanner is the actual problem¶
The scoring formula isn't bad โ what kills it is the regex scanner treating
.css, documentation/*.html, and one-off scripts/*.js as first-class
source files. repoctx's AST-backed code map already knows
kind: source/scripts vs kind: controller/authentication.
Absorbing impact-map = throwing away scanner.py + extractors.py and
reusing code-map.js. Only ~250 LOC of scoring.py + text.py +
validation.py is actually worth porting.
2. ๐ฏ Add kind/domain-aware penalties to scoring¶
After absorption, scoring should consume repoctx's kind field directly:
// pseudo
penalty if kind starts with 'source/docs' or path starts with 'documentation/'
penalty if extension in {.html, .css} unless query mentions ui/style/css
boost if kind matches one of: controller, service, processor, dto
This single change would have moved stripe.processor.ts to #1 in Query 1.
3. ๐ช The "Apple โ auth" gap is real¶
Neither tool knows that "Apple sign-in" is an auth concept. A small synonym/concept map would help both:
apple | google | oauth | sso โ boost auth domain
refund | chargeback | invoice โ boost payment domain
otp | 2fa | verify | mfa โ boost auth domain
This belongs in src/lib/risk-paths.js next to the shared keyword list โ same
vocabulary, two uses.
4. ๐ค pullpass + repoctx pr should merge, not compete¶
pullpass's Context Evidence block literally tells the user to run
repoctx pr. After absorption that becomes:
pullpass is essentially the verdict layer wrapping repoctx pr. Putting
them in one binary removes the "install Go just to get PASS/WARN/FAIL"
friction.
5. โก Speed gap is mostly Python startup + re-scan¶
The 5.2ร gap isn't even Python vs Node โ it's repoctx having a cached
.dev-context/index.json it can refresh incrementally vs impact-map
cold-walking the FS every call. After absorption, repoctx impact inherits
the cache for free.
๐ช Refined absorption plan¶
Phase ordering¶
| Phase | Work | ~LOC | ~Days |
|---|---|---|---|
| 1. Shared risk + fancy renderer ๐จ | src/lib/risk-paths.js, src/lib/render/fancy.js, retrofit doctor as demo |
~400 | 1โ2 |
| 2. Absorb impact-map ๐ โ ๐ฆ | src/lib/impact.js (port scoring.py + text.py + validation.py), repoctx impact command, MCP tool, --diff-base validation, kind/domain penalties |
~500 | 2โ3 |
| 3. Absorb pullpass local ๐น โ ๐ฆ | src/lib/pass-local.js, policy.js, codeowners.js, release-check.js, repoctx pass command |
~800 | 3โ4 |
| 4. Absorb pullpass PR + composite ๐ | src/lib/pass-pr.js (gh integration, 856 LOC ported), repoctx review composite, MCP review_pr tool |
~900 | 3โ5 |
What to keep vs. drop from impact-map¶
| Keep โ | Drop โ |
|---|---|
Weighted-token scoring formula (scoring.py:176-241) |
scanner.py โ repoctx's code-map.js is AST-backed |
Dependency boost via import graph (scoring.py:272-305) |
extractors.py โ repoctx already extracts imports/routes/symbols |
text.py stop-words, singularize, domain keywords |
report.py โ repoctx has its own renderer |
Validation against git diff (validation.py, ~68 LOC) |
Standalone scanner, CLI, MCP entrypoint |
What to keep from pullpass¶
| Module | New file | Why keep |
|---|---|---|
internal/local/evaluate.go |
src/lib/pass-local.js |
Local merge gate (secret/risk/validation/audit) |
internal/githubpr/evaluate.go (856 LOC!) |
src/lib/pass-pr.js |
gh-driven review decision, mergeability, status checks, conversations |
internal/codeowners/codeowners.go |
src/lib/codeowners.js |
Pattern matching + team membership |
internal/policy/policy.go |
src/lib/policy.js |
standard / company / high-risk profiles |
internal/rules/rules.go |
merge into src/lib/risk-paths.js |
Unified risk vocabulary |
internal/release/check.go |
src/lib/release-check.js |
Release-discipline rules |
CLI shape after absorption¶
repoctx impact . "add Stripe refunds to bookings" # โ was: impact-map
repoctx impact . "fix auth redirect" --diff-base HEAD --json
repoctx pass . --base origin/main # โ was: pullpass local
repoctx pass . --policy high-risk --governance team
repoctx pass-pr 123 --comment # โ was: pullpass pr
repoctx pass-pr --governance solo
repoctx review . --base origin/main --request "add refunds" # ๐ NEW: composite
# runs: code-map โ impact โ pr โ pass, returns one verdict
Fancy output preview¶
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ ๐ repoctx pass ยท merge readiness โ
โ ๐ ~/projects/bashbop-api โ
โ ๐ HEAD vs origin/main ยท 14 changed files ยท policy: high-risk
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
โ
Changed files 14 files scoped to api/, tests/
โ
Secret safety no .env or credential paths touched
โ ๏ธ Risk review 5 sensitive paths changed
โโ ๐ณ src/payments/refund.ts
โโ ๐ src/auth/session.ts
โโ ๐๏ธ prisma/schema.prisma
โ
Release check CHANGELOG.md updated for 1.4.0
โ
Validation commands npm run ci ยท npm test ยท npm run lint
โ
Dependency audit package-lock.json present
โ Review state CODEOWNERS approval missing for prisma/
โ ๏ธ Policy profile high-risk: record specialist sign-off
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ ๐ฆ VERDICT โ FAIL โ
โ โ blocked by Review state โ
โ ๐ next step request @data-team review โ
โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
๐ง Phase 1 status¶
Phase 1 work was started and paused for this writeup. Task list:
| # | Task | Status |
|---|---|---|
| 1 | Read existing risk-flag logic in pr-review.js |
โ done |
| 2 | Create src/lib/risk-paths.js shared policy module |
โณ pending |
| 3 | Create src/lib/render/fancy.js renderer |
โณ pending |
| 4 | Retrofit doctor command as fancy-renderer demo |
โณ pending |
| 5 | Add tests for risk-paths and fancy renderer | โณ pending |
| 6 | Run full repoctx CI gate (npm run ci) |
โณ pending |
Existing repoctx risk vocabulary (preserve in risk-paths.js)¶
Discovered in src/lib/pr-review.js:742-767:
request surface โ route | apiRoute | controller kinds
frontend/backend contract โ apiClient kind
data model โ schema kind, prisma | migration paths
auth/security โ auth | session | jwt | permission | role | password | token
money flow โ payment | billing | checkout | webhook | stripe | refund
configuration โ config kind, package.json | lock | docker | configs | env
large file diff โ additions + deletions โฅ 300
This is more semantic than impact-map's RISK_KEYWORDS ("payments",
"database") or pullpass's riskPathParts ("stripe", "prisma"). The shared
module should keep repoctx's vocabulary and map the other tools' lists to
these canonical flags.
Glyph mapping for the renderer¶
| Flag | Glyph |
|---|---|
| auth/security | ๐ |
| money flow | ๐ณ |
| data model | ๐๏ธ |
| request surface | ๐ก |
| frontend/backend contract | ๐ |
| configuration | โ๏ธ |
| large file diff | ๐ฆ |
| secret risk | ๐จ |
Status glyphs: โ pass ยท โ ๏ธ warn ยท โ fail ยท ๐ฆ verdict ยท ๐ฏ primary ยท ๐ฅ๐ฅ๐ฅ ranks.
๐ญ Open follow-ups¶
- Verify findings on a Next.js repo (
snapabird-web) to make sure the documentation/* overweighting is a NestJS-specific artifact or a general problem. - Confirm CODEOWNERS team-membership scope โ needs a GitHub token with
read:org. Document in the newrepoctx pass-prhelp. - Decide
repoctx contextdeprecation path โimpactandcontextanswer the same question with different vocabularies. Likelycontextbecomes an alias forimpactin the next major. - Confirm tool name
pass-prvs alternatives (pass --pr 123,pass remote,review). Current docs assumepass-prbut it's not shipped yet.
๐ Source files referenced¶
| File | Purpose |
|---|---|
/Users/segzy/projects/impact-map/src/change_impact_analyzer/scoring.py |
Scoring formula to port |
/Users/segzy/projects/impact-map/src/change_impact_analyzer/text.py |
Tokenizer + domain keywords |
/Users/segzy/projects/impact-map/src/change_impact_analyzer/validation.py |
Diff-validation feature |
/Users/segzy/projects/pullpass/internal/local/evaluate.go |
Local merge gate |
/Users/segzy/projects/pullpass/internal/githubpr/evaluate.go |
gh-driven PR mode |
/Users/segzy/projects/pullpass/internal/policy/policy.go |
Policy profiles |
/Users/segzy/projects/pullpass/internal/rules/rules.go |
Risk path list |
/Users/segzy/projects/pullpass/internal/codeowners/codeowners.go |
CODEOWNERS parser |
/Users/segzy/projects/repoctx/src/lib/pr-review.js |
Existing risk-flag inference (preserve vocabulary) |
/Users/segzy/projects/repoctx/src/lib/context-engine.js |
Existing task-context engine that impact will merge with |
/Users/segzy/projects/repoctx/src/lib/code-map.js |
AST code map that replaces impact-map's scanner |