Context
Eighth friction signal from the first-real-world cloud-deployment exercise (siblings #12014, #12015, #12016, #12017, #12019, #12022, #12025; #12023 is the test-side dual). After R2 OIDC ingress was validated end-to-end on a external-deployment mirror, the natural next step was seeding the KB with Neo-shared default content. That immediately hit a wall: the deploy image deliberately strips the very content paths the KB needs to index.
This is distinct from #12023: that ticket is about restoring test/ so the integration-unified test harness can find its fixture files. This ticket is about restoring learn/, resources/, examples/, and source-side docs/ so the runtime ai:sync-kb flow has Neo-shared content to ingest.
The Problem
ai/deploy/Dockerfile.dockerignore (added by PR #12018) excludes:
<h1 class="neo-h1" data-record-id="4">Frontend, docs, examples, tests, and local KB corpus are not deploy runtime.</h1>
apps
docs
examples
learn
resources
test
The "not deploy runtime" comment is empirically wrong for at least four of those paths. Neo's curated Source classes (useDefaultSources: true in aiConfig) read from:
learn/** — guides, ADRs, the cloud-deployment substrate itself (>60% of Neo-shared KB volume)
examples/** — code examples that index as source chunks
resources/content/issues/, resources/content/discussions/, resources/content/release-notes/ — KB-cached GitHub artifacts ingested as ticket / discussion / release chunks
docs/** (source content, excluding the built docs viewer at docs/app/)
Plus test/** per #12023 for the integration harness.
Empirical V-B-A on a freshly built kb-server image (dev HEAD with current Dockerfile.dockerignore):
$ docker exec <deployment->kb sh -c 'for d in learn examples resources docs apps test; do
if [ -d "/app/$d" ]; then echo " /app/$d present"; else echo " /app/$d MISSING"; fi
done'
/app/learn MISSING
/app/examples MISSING
/app/resources MISSING
/app/docs MISSING
/app/apps MISSING
/app/test MISSINGResult: docker exec <deployment->kb npm run ai:sync-kb cannot ingest any Neo-shared content because the content tree isn't there. Day-0 Tutorial Milestone 2's "Run the deployed KB sync/import step for Neo-shared content" is not actionable against an image built with this dockerignore.
The Architectural Reality
Two distinct categories of "non-runtime" paths got bundled into the same dockerignore block, but they have different consumers:
| Path |
Excluded? |
Justification |
node_modules, dist, coverage, playwright-report, test-results |
yes |
Rebuilt-in-container or test-only output |
.git, .github, .husky, sent-to-cull.jsonl |
yes |
Repository automation substrate |
.neo-ai-data, .claude, .codex, .gemini, .idea, .vscode, .env*, tmp |
yes |
Host-only operator state |
learn, resources, examples, source-side docs |
NO (should be kept) |
KB-ingested source content; Source classes need them at runtime |
test |
NO (should be kept) |
Per #12023 — integration harness needs fixtures |
apps |
maybe excluded |
Demo workspace apps; possibly not Neo-shared substrate. Operator's call. |
docs/app |
yes |
Built docs viewer (frontend bundle), separate from source docs/** content |
The Fix
Revised Dockerfile.dockerignore:
<h1 class="neo-h1" data-record-id="7">Runtime / agent state: host-only, never deployed.</h1>
.neo-ai-data
.agents
.claude
.codex
.env*
.gemini
.idea
.vscode
tmp
<h1 class="neo-h1" data-record-id="8">Built docs viewer (frontend bundle) — source docs/ tree stays for KB ingestion.</h1>
docs/app
<h1 class="neo-h1" data-record-id="9">Workspace demo apps — not Neo-shared KB content.</h1>
apps
<h1 class="neo-h1" data-record-id="10">Build / test artifacts: rebuilt in-container.</h1>
node_modules
dist
coverage
playwright-report
test-results
<h1 class="neo-h1" data-record-id="11">Git and repository automation substrate: not needed inside deploy images.</h1>
.git
.github
.husky
sent-to-cull.jsonl
<h1 class="neo-h1" data-record-id="12">Logs and local OS metadata.</h1>
*.log
.DS_Store
Key changes vs current state:
- Remove
learn, resources, examples, docs, test from the exclusion list — these are runtime-needed.
- Add
docs/app (narrower exclusion) — keeps source docs content, drops only the built viewer bundle.
- Keep
apps excluded (operator's call — these are demo workspaces, not Neo-shared KB substrate). Revisit if a Source class indexes them.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
ai/deploy/Dockerfile.dockerignore (path list) |
Neo's Source-class registry (useDefaultSources: true); Day-0 Tutorial Milestone 2 "deployed KB sync/import step" |
Keep learn/, resources/, examples/, docs/, test/ in the image; exclude docs/app and apps (narrower); other host-only / build-artifact exclusions unchanged |
None — without this, ai:sync-kb inside the container has no Neo-shared content to ingest |
Inline comment |
docker exec <deployment->kb ls /app/{learn,examples,resources,docs} returns MISSING; du -sh on a maintainer checkout shows learn/ is ~150 MB of canonical KB substrate |
| Day-0 Tutorial Milestone 2 wording ("Run the deployed KB sync/import step") |
Same |
Update to name the concrete command (likely docker compose exec kb-server npm run ai:sync-kb once the dockerignore fix lands) |
None — current claim is unactionable |
Day-0 Tutorial |
Empirical: tutorial-following operator hits dead end at this step |
Decision Record impact
aligned-with ADR 0014 — completes the cloud-deployment substrate by enabling the documented Neo-shared-content seeding step. No ADR successor risk.
Acceptance Criteria
Out of Scope
- The architectural choice between "in-container sync (image carries content)" vs "operator-side sync (content shipped via MCP push)" vs "external chroma/ollama exposure". That's a Discussion-shaped question worth peer review across the swarm before code lands. This ticket only addresses the image-content-availability prerequisite for option (a).
apps/ reclassification — if a future Source class indexes them, file a follow-up.
- Per-path size optimization (e.g., excluding
learn/agentos/sessions/ if it's huge but not KB-indexed) — defer to a follow-up if image size becomes a problem.
Avoided Traps
- Filing as
documentation only — rejected; the dockerignore is functionally broken for KB seeding, not just mis-documented.
- Wholesale revert of #12017's dockerignore — rejected; the host-state + build-artifact exclusions are correct and load-bearing (without them, build context returns to 20+ GB). Narrow restoration is the right shape.
- Including
apps/ in the keep-list speculatively — rejected; no Source class currently indexes apps/ so excluding it is correct. Re-evaluate if that changes.
Related
- #12017 — predecessor (merged via #12018); introduced the over-exclusive dockerignore.
- #12023 — sibling (in-flight via #12024); restoring
test/ for the integration test harness. Both concerns share the same root cause (the "not deploy runtime" framing was too broad). Resolving both in the same PR is the natural shape if #12024 hasn't merged yet.
- ADR 0014 — cloud-deployment topology authority.
- Day-0 Tutorial Milestone 2 — the operator-facing path this fix unblocks.
Handoff Retrieval Hints
- Empirical:
docker exec <kb-server> ls /app/{learn,examples,resources,docs} on a freshly built image. Pre-fix: all MISSING. Post-fix: all present.
- Semantic: "ai:sync-kb in-container Neo-shared content missing", "dockerignore excludes learn directory KB ingestion", "Day-0 Tutorial Milestone 2 deployed KB sync step undocumented".
- File anchor:
ai/deploy/Dockerfile.dockerignore.
Context
Eighth friction signal from the first-real-world cloud-deployment exercise (siblings #12014, #12015, #12016, #12017, #12019, #12022, #12025; #12023 is the test-side dual). After R2 OIDC ingress was validated end-to-end on a external-deployment mirror, the natural next step was seeding the KB with Neo-shared default content. That immediately hit a wall: the deploy image deliberately strips the very content paths the KB needs to index.
This is distinct from #12023: that ticket is about restoring
test/so the integration-unified test harness can find its fixture files. This ticket is about restoringlearn/,resources/,examples/, and source-sidedocs/so the runtimeai:sync-kbflow has Neo-shared content to ingest.The Problem
ai/deploy/Dockerfile.dockerignore (added by PR #12018) excludes:
The "not deploy runtime" comment is empirically wrong for at least four of those paths. Neo's curated Source classes (
useDefaultSources: trueinaiConfig) read from:learn/**— guides, ADRs, the cloud-deployment substrate itself (>60% of Neo-shared KB volume)examples/**— code examples that index assourcechunksresources/content/issues/,resources/content/discussions/,resources/content/release-notes/— KB-cached GitHub artifacts ingested asticket/discussion/releasechunksdocs/**(source content, excluding the built docs viewer atdocs/app/)Plus
test/**per #12023 for the integration harness.Empirical V-B-A on a freshly built kb-server image (dev HEAD with current
Dockerfile.dockerignore):$ docker exec <deployment->kb sh -c 'for d in learn examples resources docs apps test; do if [ -d "/app/$d" ]; then echo " /app/$d present"; else echo " /app/$d MISSING"; fi done' /app/learn MISSING /app/examples MISSING /app/resources MISSING /app/docs MISSING /app/apps MISSING /app/test MISSINGResult:
docker exec <deployment->kb npm run ai:sync-kbcannot ingest any Neo-shared content because the content tree isn't there. Day-0 Tutorial Milestone 2's "Run the deployed KB sync/import step for Neo-shared content" is not actionable against an image built with this dockerignore.The Architectural Reality
Two distinct categories of "non-runtime" paths got bundled into the same
dockerignoreblock, but they have different consumers:node_modules,dist,coverage,playwright-report,test-results.git,.github,.husky,sent-to-cull.jsonl.neo-ai-data,.claude,.codex,.gemini,.idea,.vscode,.env*,tmplearn,resources,examples, source-sidedocstestappsdocs/appdocs/**contentThe Fix
Revised
Dockerfile.dockerignore:Key changes vs current state:
learn,resources,examples,docs,testfrom the exclusion list — these are runtime-needed.docs/app(narrower exclusion) — keeps source docs content, drops only the built viewer bundle.appsexcluded (operator's call — these are demo workspaces, not Neo-shared KB substrate). Revisit if a Source class indexes them.Contract Ledger
ai/deploy/Dockerfile.dockerignore(path list)useDefaultSources: true); Day-0 Tutorial Milestone 2 "deployed KB sync/import step"learn/,resources/,examples/,docs/,test/in the image; excludedocs/appandapps(narrower); other host-only / build-artifact exclusions unchangedai:sync-kbinside the container has no Neo-shared content to ingestdocker exec <deployment->kb ls /app/{learn,examples,resources,docs}returns MISSING;du -shon a maintainer checkout showslearn/is ~150 MB of canonical KB substratedocker compose exec kb-server npm run ai:sync-kbonce the dockerignore fix lands)Decision Record impact
aligned-with ADR 0014— completes the cloud-deployment substrate by enabling the documented Neo-shared-content seeding step. No ADR successor risk.Acceptance Criteria
ai/deploy/Dockerfile.dockerignorekeepslearn/,resources/,examples/,docs/,test/in the build context.ai/deploy/Dockerfile.dockerignoreexcludesdocs/appspecifically (built docs viewer, not source docs).kb-server,docker exec <kb> ls /app/learnlists files anddocker exec <kb> ls /app/docslists files (regression guard).docker exec <kb> npm run ai:sync-kbcompletes without "missing source content" errors against a freshly-booted compose stack (functional regression guard — note: still requires Chroma + Ollama to be reachable in the compose network, which they are).learn/+resources/size). Note as the cost of in-container sync; operator can choose external-sync model to keep image smaller (see Out of Scope).Out of Scope
apps/reclassification — if a future Source class indexes them, file a follow-up.learn/agentos/sessions/if it's huge but not KB-indexed) — defer to a follow-up if image size becomes a problem.Avoided Traps
documentationonly — rejected; the dockerignore is functionally broken for KB seeding, not just mis-documented.apps/in the keep-list speculatively — rejected; no Source class currently indexesapps/so excluding it is correct. Re-evaluate if that changes.Related
test/for the integration test harness. Both concerns share the same root cause (the "not deploy runtime" framing was too broad). Resolving both in the same PR is the natural shape if #12024 hasn't merged yet.Handoff Retrieval Hints
docker exec <kb-server> ls /app/{learn,examples,resources,docs}on a freshly built image. Pre-fix: all MISSING. Post-fix: all present.ai/deploy/Dockerfile.dockerignore.