Context
neo-shared/neo is registered as a pull-mode tenant repo, and kbSync also ingests neo's content. Both stamp {tenantId: neo-shared, repoSlug: neo} — configBase.mjs:439/:447 default the stamp to exactly that tuple — while producing different chunk populations: kbSync emits typed/structured chunks (type: src|adr, kind: method|class-config|module-context|class-properties) from 10 source extractors, the tenant path emits untyped raw-file chunks via RawRepoSource because no parserId is declared.
Same identity, different content, so each lane classifies the other's rows as stale. Live consequence 2026-08-06: 17,550 rows deleted and not re-embedded, then a tenant repo's 50 rows swept by an unrelated lane.
This is not a config accident. D#15605's N=1 partial graduation registered neo as a pull-mode tenant deliberately, as a "whole-tree/zero-code PMV", and its point 3 required an identity-classified reconciliation receipt proving "zero same-identity double representation." That receipt was never produced — the lane never once completed — and the failure it named is what arrived. Full account at the D#15605 acquisition-vs-extraction lane.
The Problem
The neo tenant entry has served its purpose and now only costs:
- Identity collision with
kbSync, unfixable by #16584's scoping (which separates repo slugs, not lanes sharing one).
- A strictly worse duplicate. It re-ingests content
kbSync already provides typed; raw-file chunks cannot serve query_documents({type}) or get_class_hierarchy.
- ~24k blob fetches per fresh mirror. Measured at the provider: 24,834 repository clones on 2026-08-05 against a ~1k/day baseline, uniques falling to 78 because it was one cloner.
#16557 owns that cost.
Meanwhile multi-tenant ingestion has only ever been exercised with one viable tenant repo (create-app), so per-repo scheduling, jitter, independent backoff, and per-repo checkpointing have no multi-repo witness.
The Architectural Reality
kb-config.yaml's own comment states the constraint this ticket must respect: "branchRef is per-repo and NOT inheritable: neo integrates on dev, create-app has only main … Copying a sibling's branchRef is [wrong]." Verified against the remotes for both additions:
| repo |
visibility |
branches |
blobs at HEAD |
size |
neomjs/devindex-opt-in |
public |
main only |
4 |
3 KB |
neomjs/devindex-opt-out |
public |
main only |
3 |
1 KB |
So both take branchRef: main and credentialRef: none. Combined they add ~7 tracked files, which keeps the full sweep cycle in seconds and cannot reproduce the blobless clone cost.
Sequencing, not reversal. Neo returns as an ingestion tenant once sources/parsers are declarable per tenant — then it uses our own extractors rather than raw-file fallback, which is the outcome the N=1 slice was reaching for. That lane is D#15605's; this ticket only removes the entry that cannot work until then.
The Fix
ai/deploy/kb-config.yaml, tenants.neo-shared.tenantRepos:
- Remove the
neo entry.
- Add
devindex-opt-in and devindex-opt-out, each credentialRef: none, branchRef: main.
- Update the surrounding comment so the
branchRef lesson still names live entries rather than a removed one.
No code changes.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
tenantRepos[neo] |
D#15605 N=1 slice |
Removed; returns via declared sources |
kbSync already owns neo's corpus, typed |
kb-config comment |
collision measured 2026-08-06 |
tenantRepos[devindex-opt-in] |
this ticket |
branchRef: main, credentialRef: none |
— |
kb-config comment |
remote: main only, 4 blobs |
tenantRepos[devindex-opt-out] |
this ticket |
branchRef: main, credentialRef: none |
— |
kb-config comment |
remote: main only, 3 blobs |
Decision Record impact
none. A tenant-registration change. It does not amend ADR authority; the role split it anticipates is still in divergence at D#15605.
Acceptance Criteria
Out of Scope
- Declaring sources/parsers for any tenant, and re-registering neo — D#15605's lane.
- The 132.7 MB blobless
neo mirror and its tenant-repo-sync-revisions.json entry. Both become inert; the mirror is arguably a useful cache for when neo returns. Deliberately not deleted in the same change that stops using them.
#16557's acquisition cost, #16591's boundary spec, #16577's zero-chunk disposition.
Known coverage regression this accepts
neo was the only tenant repo whose branchRef differed from its default_branch. After this change every entry is main-on-main, so the per-repo branchRef resolution path loses its only live witness — precisely the path whose "not inheritable" lesson the config comment records. Stated rather than discovered later; a future non-default-branch tenant restores it.
Related
- D#15605 — the acquisition-vs-extraction lane, the N=1 slice's origin, and the overdue point-3 receipt.
- Epic #16566 · #16584 / PR #16590 (scoping, which cannot separate same-stamp lanes) · #16587 / PR #16583 · #16557 (held) · D#12034.
Origin Session ID: 6004a4aa-2089-4b14-b73f-b58c08cf53d9
Retrieval Hint: query_raw_memories("neo tenant entry removed devindex tenant repos identity collision") · ai/deploy/kb-config.yaml
Authored by @neo-opus-vega (Claude Opus 5).
Context
neo-shared/neois registered as a pull-mode tenant repo, andkbSyncalso ingests neo's content. Both stamp{tenantId: neo-shared, repoSlug: neo}—configBase.mjs:439/:447default the stamp to exactly that tuple — while producing different chunk populations:kbSyncemits typed/structured chunks (type: src|adr,kind: method|class-config|module-context|class-properties) from 10 source extractors, the tenant path emits untyped raw-file chunks viaRawRepoSourcebecause noparserIdis declared.Same identity, different content, so each lane classifies the other's rows as stale. Live consequence 2026-08-06: 17,550 rows deleted and not re-embedded, then a tenant repo's 50 rows swept by an unrelated lane.
This is not a config accident. D#15605's N=1 partial graduation registered neo as a pull-mode tenant deliberately, as a "whole-tree/zero-code PMV", and its point 3 required an identity-classified reconciliation receipt proving "zero same-identity double representation." That receipt was never produced — the lane never once completed — and the failure it named is what arrived. Full account at the D#15605 acquisition-vs-extraction lane.
The Problem
The
neotenant entry has served its purpose and now only costs:kbSync, unfixable by #16584's scoping (which separates repo slugs, not lanes sharing one).kbSyncalready provides typed; raw-file chunks cannot servequery_documents({type})orget_class_hierarchy.#16557owns that cost.Meanwhile multi-tenant ingestion has only ever been exercised with one viable tenant repo (
create-app), so per-repo scheduling, jitter, independent backoff, and per-repo checkpointing have no multi-repo witness.The Architectural Reality
kb-config.yaml's own comment states the constraint this ticket must respect: "branchRefis per-repo and NOT inheritable: neo integrates ondev, create-app has onlymain… Copying a sibling'sbranchRefis [wrong]." Verified against the remotes for both additions:neomjs/devindex-opt-inmainonlyneomjs/devindex-opt-outmainonlySo both take
branchRef: mainandcredentialRef: none. Combined they add ~7 tracked files, which keeps the full sweep cycle in seconds and cannot reproduce the blobless clone cost.Sequencing, not reversal. Neo returns as an ingestion tenant once sources/parsers are declarable per tenant — then it uses our own extractors rather than raw-file fallback, which is the outcome the N=1 slice was reaching for. That lane is D#15605's; this ticket only removes the entry that cannot work until then.
The Fix
ai/deploy/kb-config.yaml,tenants.neo-shared.tenantRepos:neoentry.devindex-opt-inanddevindex-opt-out, eachcredentialRef: none,branchRef: main.branchReflesson still names live entries rather than a removed one.No code changes.
Contract Ledger Matrix
tenantRepos[neo]kbSyncalready owns neo's corpus, typedtenantRepos[devindex-opt-in]branchRef: main,credentialRef: nonemainonly, 4 blobstenantRepos[devindex-opt-out]branchRef: main,credentialRef: nonemainonly, 3 blobsDecision Record impact
none. A tenant-registration change. It does not amend ADR authority; the role split it anticipates is still in divergence at D#15605.Acceptance Criteria
neois absent fromtenantRepos;devindex-opt-inanddevindex-opt-outare present withbranchRef: mainandcredentialRef: none.create-appmints a receipt, commits its checkpoint, and its rows survive the followingkbSync— the first time both have been true.repoSlugcounts are non-zero for all three after a full sweep plus onekbSync.Out of Scope
neomirror and itstenant-repo-sync-revisions.jsonentry. Both become inert; the mirror is arguably a useful cache for when neo returns. Deliberately not deleted in the same change that stops using them.#16557's acquisition cost,#16591's boundary spec,#16577's zero-chunk disposition.Known coverage regression this accepts
neo was the only tenant repo whose
branchRefdiffered from itsdefault_branch. After this change every entry ismain-on-main, so the per-repobranchRefresolution path loses its only live witness — precisely the path whose "not inheritable" lesson the config comment records. Stated rather than discovered later; a future non-default-branch tenant restores it.Related
Origin Session ID: 6004a4aa-2089-4b14-b73f-b58c08cf53d9
Retrieval Hint:
query_raw_memories("neo tenant entry removed devindex tenant repos identity collision")·ai/deploy/kb-config.yamlAuthored by @neo-opus-vega (Claude Opus 5).