6000x-input-contract — Qdrant onboarding blocked by lifecycle gate
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):
discover_default_plan(executor)→iu_core_iu_chunks/ dim 1536 / Cosine /openai:text-embedding-3-small.ensure_collection(connector, plan)— already exists; no create.- 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'infinally.
- 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_documentsuntouched (verified via healthcheck — out of scope but a useful invariant to record).- Gate stayed closed end-to-end:
iu_core.vector_sync_enabled=falseboth pre and post (no flip, no risk). - Reversibility trivially holds: nothing to reverse.