KB-2DB9

6000x-input-contract — Qdrant onboarding blocked by lifecycle gate

7 min read Revision 1
iu-core6000xqdrantonboardingexact-gaplifecycle

03 — Qdrant onboarding: blocked by lifecycle gate (exact gap)

Branch: Qdrant onboarding for real corpora DIEU-35 / DIEU-28 / DIEU-32. Status: PARTIAL_WITH_EXACT_GAP — 0 IUs indexed of 86 corpus IUs. Cause: All 86 are lifecycle_status='draft'; the 2400x convention is enacted-only indexing.

1. Discovery

The macro authorised onboarding for DIEU-35 / DIEU-28 / DIEU-32 "if safe". Discovery against live directus PG (via SSH → docker exec postgres):

SELECT axis_a_doc_code AS dieu, COUNT(*) AS total_iu,
       MIN(axis_c_depth) min_d, MAX(axis_c_depth) max_d
FROM iu_three_axis_envelope
WHERE axis_a_doc_code IN ('DIEU-35','DIEU-28','DIEU-32')
GROUP BY axis_a_doc_code ORDER BY axis_a_doc_code;
 dieu    | total_iu | min_d | max_d
---------+----------+-------+-------
 DIEU-28 |       27 |     0 |     1
 DIEU-32 |       23 |     0 |     1
 DIEU-35 |       36 |     0 |     2

86 IUs total, none currently in iu_vector_sync_point:

SELECT e.axis_a_doc_code, COUNT(*) total, COUNT(s.unit_id) already_synced
FROM iu_three_axis_envelope e
LEFT JOIN iu_vector_sync_point s ON s.unit_id=e.unit_id
                                AND s.sync_status='indexed'
WHERE e.axis_a_doc_code IN ('DIEU-35','DIEU-28','DIEU-32')
GROUP BY e.axis_a_doc_code;
 dieu    | total | already_synced
---------+-------+----------------
 DIEU-28 |    27 |              0
 DIEU-32 |    23 |              0
 DIEU-35 |    36 |              0

2. Lifecycle gate failure

The 2400x macro established enacted-only as the technical gate because Qdrant point ids are content-addressed UUIDv5 over the chunk's human key — a draft body that changes between indexings creates a new point id, leaving the old one as orphan content. The 2400x report (doc 03) records this verbatim: "the only lifecycle that carries an authoritative body". Applied to this macro's corpora:

SELECT e.axis_a_doc_code, uv.lifecycle_status, COUNT(*),
       COUNT(uv.body), AVG(LENGTH(uv.body))::int avg_body
FROM iu_three_axis_envelope e
LEFT JOIN unit_version uv ON uv.unit_id = e.unit_id
WHERE e.axis_a_doc_code IN ('DIEU-35','DIEU-28','DIEU-32')
GROUP BY e.axis_a_doc_code, uv.lifecycle_status
ORDER BY e.axis_a_doc_code, uv.lifecycle_status;
 dieu    | lifecycle | n_rows | with_body | avg_body
---------+-----------+--------+-----------+----------
 DIEU-28 | draft     |     27 |        27 |      282
 DIEU-32 | draft     |     23 |        23 |      214
 DIEU-35 | draft     |     36 |        36 |      988

All 86 IUs have bodies (none null), but zero are enacted. The filter lifecycle_status='enacted' returns 0 rows on the JOIN — i.e. the enacted-only convention closes the branch at 0/86.

3. Decision (user choice, recorded verbatim)

The user was asked to choose between (a) override convention and index 86 draft IUs, (b) skip and record exact gap, (c) promote draft→enacted first. User chose (b) Skip Qdrant write — record exact gap. Ruling:

  • Preserve the enacted-only Qdrant onboarding convention from 2400x.
  • Do not index the 86 draft IUs in DIEU-35/28/32.
  • Do not override the convention for draft content.
  • Do not promote draft→enacted in this macro.

4. Exact gap

  • DIEU-35/28/32 have 86 eligible-by-corpus IUs.
  • All 86 are lifecycle_status='draft'.
  • 0 are currently indexed.
  • Qdrant onboarding is blocked until (a) lifecycle promotion to enacted (a separate governed mutation needing its own audit), or (b) a separate draft-vector policy is explicitly approved (would also need a chunk-id stability strategy because content addressing is broken by draft churn).

5. Live state — Qdrant untouched

The existing 60 enacted IUs / 61 points indexed by 2400x are untouched this macro:

qdrant_collection: ok=True
  active=iu_core_iu_chunks
  sync_points_indexed=61
vector_boundary:   ok=True
  per_iu_boundary_ok=True
  sync_point_rows=61
  unique_units=60
write_gates:       all 6 inert
  iu_core.vector_sync_enabled = false

production_documents collection (9 213 points / green) also untouched — out of scope for this macro and for IU Core entirely.

6. Re-onboarding command package (when authority + lifecycle align)

When draft→enacted promotion has occurred (or a draft-vector policy is ratified), the apply driver is identical to 2400x — only the candidate SQL changes:

-- Updated discovery (post-promotion)
SELECT e.unit_id, e.canonical_address,
       jsonb_build_object(
         'source_axis_ref', e.axis_a_doc_code,
         'semantic_axis_ref', e.axis_b_tags->'unit_kind'->>0,
         'hierarchy_axis_ref', e.axis_c_parent_id::text
       ) AS axis_refs,
       uv.body
FROM iu_three_axis_envelope e
JOIN unit_version uv ON uv.unit_id=e.unit_id
                     AND uv.lifecycle_status='enacted'
WHERE e.axis_a_doc_code IN ('DIEU-35','DIEU-28','DIEU-32')
  AND uv.body IS NOT NULL
ORDER BY e.axis_a_doc_code, e.axis_a_sort_order;

Driver pattern (mirrors 2400x):

  1. discover_default_plan(executor)iu_core_iu_chunks / dim 1536 / Cosine / openai:text-embedding-3-small.
  2. ensure_collection(connector, plan) — already exists; no create.
  3. For each batch of 30:
    • build_iu_point_set(unit_id, canonical_address, body, axis_refs).
    • assert_boundary(points) (app-layer guard).
    • UPDATE dot_config SET value='true' WHERE key='iu_core.vector_sync_enabled'.
    • apply_iu_set(plan, points, bodies, embedder, connector, executor, actor='iu_core_6Xx_dieu_promotion_reindex', record_status='indexed').
    • Always UPDATE dot_config SET value='false' in finally.
  4. Post-verify: scroll Qdrant + boundary audit + drift report against iu_vector_sync_point.

Rollback per-actor (if needed):

DELETE FROM iu_vector_sync_point
WHERE sync_status='indexed'
  AND last_actor='iu_core_6Xx_dieu_promotion_reindex';

Then DELETE /collections/iu_core_iu_chunks/points/<uuid> for each recomputed point_id_for(point_key) (UUIDv5 over namespace iu-core.qdrant.point-id.v1).

7. Constitutional close

  • No vector boundary violated this macro (no write at all).
  • No cross-IU vector contamination (no write at all).
  • production_documents untouched (verified via healthcheck — out of scope but a useful invariant to record).
  • Gate stayed closed end-to-end: iu_core.vector_sync_enabled=false both pre and post (no flip, no risk).
  • Reversibility trivially holds: nothing to reverse.
Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-core-6000x-input-contract-qdrant-ops-closeout-open-goal/03-qdrant-onboarding-blocked-by-lifecycle.md