Context
The containerized plane's pull-mode tenant lane is live. ai/deploy/kb-config.yaml registers three repos under neo-shared — create-app, devindex-opt-in, devindex-opt-out — and the file is mounted read-only into both kb-server and the orchestrator by docker-compose.local-agent-os.yml (:41, :76), where the orchestrator mount is documented as load-bearing.
Both DevIndex intake repositories are already tenants. The application repository is not. Now that neomjs/devindex exists as a standalone product repo with its own guides, unit suite and Data Factory, it should be ingested by our own local deployment.
The Problem
The existing entries are deliberately tiny — the file's own comment explains why: "first-ingest cost scales with tracked-file count on a blobless mirror, so a tiny repo is the cheap end-to-end PROOF that pull-mode ingestion works." create-app, plus 4 and 3 tracked files.
That proof is done. What the lane has never had is a tenant with real content: neomjs/devindex tracks 112 files — an application, a Node.js service layer, 26 guide documents and a Playwright suite. It exercises retrieval quality, chunking across heterogeneous file types, and per-repo scheduling against something a query can actually be wrong about, which three README-sized repos cannot.
It is also the ideal candidate for a second reason: we own it, so a bad ingestion costs us nothing external, and its blast radius is a repository we can re-clone at will.
The Architectural Reality
Why neo itself is excluded, and why that reasoning does not transfer. kb-config.yaml documents that the neo repo is deliberately unregistered: a pull-mode entry declares no parser, falls through to RawRepoSource, and yields untyped raw-file chunks — "a second, weaker corpus under the SAME {tenantId, repoSlug} stamp that kbSync resolves to by default. Each lane then classifies the other's rows as stale and deletes them."
That collision requires two lanes writing one stamp. kbSync ingests the neo checkout through 10 typed source extractors; it does not touch neomjs/devindex. With no competing writer there is no stamp contention and no mutual deletion, so devindex is safe to register where neo is not. This is the one inherited constraint that had to be checked rather than assumed, and it does not apply.
⚠️ The blocker, verified at source. RawRepoSource performs whole-tree ingestion with includeExtensions: [] — no allow-list — and DEFAULT_EXCLUDE_EXTENSIONS is binary and media only:
.7z .avif .bin .bmp .bz2 .class .dmg .eot .exe .gif .gz .ico .jar .jpeg .jpg
.lockb .mov .mp3 .mp4 .otf .pdf .png .sqlite .tar .tgz .ttf .wasm .webm .webp
.woff .woff2 .zip
Neither .json nor .jsonl is excluded, and there is no byte-size guard anywhere in the walker. neomjs/devindex currently tracks:
| path |
size |
apps/devindex/resources/data/users.jsonl |
24 MB / 49,999 lines |
apps/devindex/resources/data/tracker.json |
2.1 MB |
apps/devindex/resources/data/visited.json |
1.6 MB |
Registering it today would embed 49,999 contributor records — login, normalized country, lifetime contribution counts, hireable flag, sponsor status — as raw-text chunks in the shared store. That is a cost problem and, more importantly, a personal-data problem: public-profile-derived records about 50,000 identifiable people, ingested under neo-shared visibility, for no retrieval benefit whatsoever. Nobody asks the Knowledge Base who ranks 31,402nd.
#17375 removes exactly those three files from git. After it merges, whole-tree ingestion of this repository is safe by construction — which is why this ticket is blocked by it rather than carrying its own filter.
The Fix
One entry in ai/deploy/kb-config.yaml, after #17375:
- tenantId: neo-shared
repoSlug : devindex
cloneUrl : https://github.com/neomjs/devindex.git
credentialRef: none
branchRef : maincredentialRef: none because the repository is public — the contract requires the field and none means "public clone, no credential material". branchRef: main verified against the remote; the file's comment warns that branchRef is per-repo and never inheritable, so this was checked rather than copied from a sibling.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
tenants.neo-shared.tenantRepos[] |
ai/deploy/kb-config.yaml (tier 2 of tenant-config resolution) |
gains a devindex entry, whole-tree ingested |
tier-3 aiConfig defaults if the file is unmounted — which silently drops every entry, not just this one |
the file's own header comment |
lastIngestedRev for neo-shared/devindex goes non-null after a sync cycle |
| ingestion scope for this repo |
RawRepoSource defaults |
whole-tree, per the graduated zero-code contract |
none — an include-manifest is a separate contract lane on the multi-tenant work |
as above |
a query returning a devindex guide chunk, and returning no chunk sourced from resources/data/** |
Decision Record impact
none — this adds a data entry to an existing, graduated contract. It introduces no new source class, parser, or config surface. Structure-map gate executed; N/A for new Agent OS placement — the only file touched is ai/deploy/kb-config.yaml.
Acceptance Criteria
Out of Scope
- Registering the neo repository itself. Blocked by the documented two-lane stamp collision; it returns as a tenant only once sources and parsers are declarable per tenant.
- A per-tenant include-manifest. Named in
kb-config.yaml as a separate contract lane. This ticket avoids needing one by depending on #17375 instead.
- A byte-size guard in
RawRepoSource. Its absence is real and is what makes the hazard above possible — but a generic walker limit is a change to shared ingestion behaviour affecting every tenant, and it deserves its own ticket rather than riding a config entry. Filing it is not this ticket's job to defer indefinitely; see Related.
- Non-default
branchRef coverage. devindex is main, so this adds no witness for the branch-resolution path that the file notes still has none.
- Remote/cloud deployment. Local docker only.
Avoided Traps
- Registering it now because the entry is one line. The entry is one line; the consequence is 50,000 people's records in a vector store. The cheapness of the change is unrelated to the cost of the outcome.
- Adding an
excludeExtensions override to work around the data files. That treats the symptom in the tenant entry and leaves the next tenant to rediscover it, while #17375 removes the files at the source within the same epic.
- Assuming neo's exclusion rationale applies. It reads like a general warning about pull-mode entries; it is specifically about two lanes writing one stamp. Inheriting it unchecked would have blocked this ticket for a reason that is not true of it.
- Treating
lastIngestedRev going non-null as success. It proves ingestion ran, not that it ingested the right things or excluded the wrong ones. AC-2 and AC-3 exist because AC-1 alone would pass on a store full of contributor records.
Related
Blocked by #17375 (removes the derived data files from git). Sibling context: #17238 (the epic that produced #17375), #16546 / #16557 (tenant-mirror clone cost — devindex is small, which is part of why it is a good tenant).
Follow-up worth filing separately once this lands: RawRepoSource has no byte-size guard and no .json/.jsonl exclusion, so any tenant registering a repository with a large generated data file hits this. devindex is simply the instance that surfaced it.
Retrieval Hint: query_raw_memories("kb-config tenant pull-mode devindex RawRepoSource whole tree no size guard") · "neo-shared tenantRepos credentialRef none"
Live latest-open sweep: checked the latest 20 open issues at 2026-08-19T08:47:36Z plus a keyword sweep on tenant/ingestion/devindex and a 30-message A2A claim scan across all read-states; no equivalent ticket and no in-flight claim on this scope.
Origin Session ID: a105d215-c261-4b34-82a9-546596f665ef
Context
The containerized plane's pull-mode tenant lane is live.
ai/deploy/kb-config.yamlregisters three repos underneo-shared—create-app,devindex-opt-in,devindex-opt-out— and the file is mounted read-only into bothkb-serverand the orchestrator bydocker-compose.local-agent-os.yml(:41,:76), where the orchestrator mount is documented as load-bearing.Both DevIndex intake repositories are already tenants. The application repository is not. Now that
neomjs/devindexexists as a standalone product repo with its own guides, unit suite and Data Factory, it should be ingested by our own local deployment.The Problem
The existing entries are deliberately tiny — the file's own comment explains why: "first-ingest cost scales with tracked-file count on a blobless mirror, so a tiny repo is the cheap end-to-end PROOF that pull-mode ingestion works."
create-app, plus 4 and 3 tracked files.That proof is done. What the lane has never had is a tenant with real content:
neomjs/devindextracks 112 files — an application, a Node.js service layer, 26 guide documents and a Playwright suite. It exercises retrieval quality, chunking across heterogeneous file types, and per-repo scheduling against something a query can actually be wrong about, which three README-sized repos cannot.It is also the ideal candidate for a second reason: we own it, so a bad ingestion costs us nothing external, and its blast radius is a repository we can re-clone at will.
The Architectural Reality
Why neo itself is excluded, and why that reasoning does not transfer.
kb-config.yamldocuments that the neo repo is deliberately unregistered: a pull-mode entry declares no parser, falls through toRawRepoSource, and yields untyped raw-file chunks — "a second, weaker corpus under the SAME{tenantId, repoSlug}stamp thatkbSyncresolves to by default. Each lane then classifies the other's rows as stale and deletes them."That collision requires two lanes writing one stamp.
kbSyncingests the neo checkout through 10 typed source extractors; it does not touchneomjs/devindex. With no competing writer there is no stamp contention and no mutual deletion, so devindex is safe to register where neo is not. This is the one inherited constraint that had to be checked rather than assumed, and it does not apply.⚠️ The blocker, verified at source.
RawRepoSourceperforms whole-tree ingestion withincludeExtensions: []— no allow-list — andDEFAULT_EXCLUDE_EXTENSIONSis binary and media only:Neither
.jsonnor.jsonlis excluded, and there is no byte-size guard anywhere in the walker.neomjs/devindexcurrently tracks:apps/devindex/resources/data/users.jsonlapps/devindex/resources/data/tracker.jsonapps/devindex/resources/data/visited.jsonRegistering it today would embed 49,999 contributor records — login, normalized country, lifetime contribution counts, hireable flag, sponsor status — as raw-text chunks in the shared store. That is a cost problem and, more importantly, a personal-data problem: public-profile-derived records about 50,000 identifiable people, ingested under
neo-sharedvisibility, for no retrieval benefit whatsoever. Nobody asks the Knowledge Base who ranks 31,402nd.#17375removes exactly those three files from git. After it merges, whole-tree ingestion of this repository is safe by construction — which is why this ticket is blocked by it rather than carrying its own filter.The Fix
One entry in
ai/deploy/kb-config.yaml, after #17375:- tenantId: neo-shared repoSlug : devindex cloneUrl : https://github.com/neomjs/devindex.git credentialRef: none branchRef : maincredentialRef: nonebecause the repository is public — the contract requires the field andnonemeans "public clone, no credential material".branchRef: mainverified against the remote; the file's comment warns thatbranchRefis per-repo and never inheritable, so this was checked rather than copied from a sibling.Contract Ledger Matrix
tenants.neo-shared.tenantRepos[]ai/deploy/kb-config.yaml(tier 2 of tenant-config resolution)devindexentry, whole-tree ingestedaiConfigdefaults if the file is unmounted — which silently drops every entry, not just this onelastIngestedRevforneo-shared/devindexgoes non-null after a sync cycleRawRepoSourcedefaultsresources/data/**Decision Record impact
none— this adds a data entry to an existing, graduated contract. It introduces no new source class, parser, or config surface. Structure-map gate executed; N/A for new Agent OS placement — the only file touched isai/deploy/kb-config.yaml.Acceptance Criteria
neo-shared/devindexappears inkb-config.yamland a sync cycle drives itslastIngestedRevnon-null on the local deployment. That field going non-null is the artifact no unit test provides, per the lane's own precedent.apps/devindex/resources/data/**exists in the store after ingestion. Asserted by querying for a value that only appears in the contributor corpus — a specific indexed login — and getting nothing. A pass here is only meaningful once #17375 has landed; running it before is a control that cannot fail for the right reason.learn/**of this tenant, not from the neo corpus. This is the first entry with content a query can be wrong about, so retrieval correctness is the point rather than ingestion liveness.create-app,devindex-opt-inanddevindex-opt-outkeep their checkpoints and independent backoff. Per-repo scheduling has only ever been exercised against three near-empty repos; this is the first entry large enough to make a shared-state defect visible.{tenantId: neo-shared, repoSlug: devindex}, so a later per-tenant split can address this corpus without re-ingesting.Out of Scope
kb-config.yamlas a separate contract lane. This ticket avoids needing one by depending on #17375 instead.RawRepoSource. Its absence is real and is what makes the hazard above possible — but a generic walker limit is a change to shared ingestion behaviour affecting every tenant, and it deserves its own ticket rather than riding a config entry. Filing it is not this ticket's job to defer indefinitely; see Related.branchRefcoverage.devindexismain, so this adds no witness for the branch-resolution path that the file notes still has none.Avoided Traps
excludeExtensionsoverride to work around the data files. That treats the symptom in the tenant entry and leaves the next tenant to rediscover it, while #17375 removes the files at the source within the same epic.lastIngestedRevgoing non-null as success. It proves ingestion ran, not that it ingested the right things or excluded the wrong ones. AC-2 and AC-3 exist because AC-1 alone would pass on a store full of contributor records.Related
Blocked by #17375 (removes the derived data files from git). Sibling context: #17238 (the epic that produced #17375), #16546 / #16557 (tenant-mirror clone cost — devindex is small, which is part of why it is a good tenant).
Follow-up worth filing separately once this lands:
RawRepoSourcehas no byte-size guard and no.json/.jsonlexclusion, so any tenant registering a repository with a large generated data file hits this. devindex is simply the instance that surfaced it.Retrieval Hint:
query_raw_memories("kb-config tenant pull-mode devindex RawRepoSource whole tree no size guard")·"neo-shared tenantRepos credentialRef none"Live latest-open sweep: checked the latest 20 open issues at 2026-08-19T08:47:36Z plus a keyword sweep on tenant/ingestion/devindex and a 30-message A2A claim scan across all read-states; no equivalent ticket and no in-flight claim on this scope.
Origin Session ID: a105d215-c261-4b34-82a9-546596f665ef