KB-7BA5

80000x · 06 — Three-axis requirements (Axis A source order, Axis B professional, Axis C parent-child-grandchild)

8 min read Revision 1
iu-core80000xthree-axis-requirementsaxis-a-original-source-reconstructionaxis-b-professional-content-axisaxis-c-parent-child-grandchildfn_iu_reconstruct_sourcefn_iu_subtreeiu_metadata_tag_registryinvariantsmanifest-to-db-mapping

06 — Three-axis requirements (A, B, C)

The three axes are the IU-core's invariant model. Every cut piece must satisfy all three. MARK proposes them; CUT writes them; VERIFY confirms them.

1. Why three axes

A single piece needs to be reachable along three independent dimensions:

  • Axis A — Original source reconstruction. Given an article label, can we reproduce the exact original text by concatenating its pieces in source order?
  • Axis B — Professional / content axis. What kind of document is this piece part of? What section type? What discipline/topic tags? This is the filter "show me all rules about X".
  • Axis C — Structural axis. Where does this piece sit in the parent/child/grandchild hierarchy? This is the filter "show me the children of this clause".

The three axes are orthogonal in the data model: an Axis B query (section_type = 'definition') does not constrain Axis A or Axis C, and vice versa.

2. Axis A — Original source reconstruction

2.1 Invariants

A1  Every piece carries source_position (int) within its article.
A2  source_position is dense:      values are 1..N with no gaps.
A3  source_position is monotonic:  sorted ascending reproduces source order.
A4  source_position is unique within an article.
A5  Concatenating piece text by source_position ascending, with the article's
    inter-piece separator and the article's normalization rule, yields a string
    whose sha256 equals manifest.articles[].original_text_hash.
A6  fn_iu_reconstruct_source('<article_label>') returns the pieces in order,
    each resolvable to a live information_unit row.

2.2 Code path (live, 70000x)

SELECT *
FROM fn_iu_reconstruct_source('DIEU-37')
ORDER BY source_position;
-- expect: N rows; source_position dense+monotonic; every iu_id non-null

T01 of ops/iu-core-six-flow-test-readiness/ validates this at 36 pieces for DIEU-35 (PASS at run 20260525T081115Z).

2.3 Manifest fields driving Axis A

articles[].pieces[].axis_a:
  source_position:    <int 1..N>
  source_url:         <article URL>
  source_hash:        <sha256 of full source body>

2.4 VERIFY check V1

After CUT, the verifier runs fn_iu_reconstruct_source and asserts the reconstructed-normalized hash equals manifest.articles[].original_text_hash. Mismatch ⇒ rollback.

3. Axis B — Professional / content axis

3.1 Invariants

B1  Every piece is tagged in at least one Axis-B kind.
B2  Axis-B kinds = {legal_document, section_type, unit_kind}  (per iu_metadata_tag_registry).
B3  No piece's Axis-B field re-encodes information already carried elsewhere
    (no second source of truth — e.g., do NOT also tag section_type='article'
     if information_unit.section_type already says 'article').
B4  professional_tags[] (extras) come from the source, not from Agent imagination.
B5  The full Axis-B vocabulary is enforced by DB CHECK constraints — discover
    live with `pg_get_constraintdef`, do not hardcode.

3.2 Code path (live, 70000x)

SELECT DISTINCT tag_kind
FROM iu_metadata_tag_registry
WHERE active=true;
-- expect 3 Axis-B kinds: legal_document, section_type, unit_kind

SELECT *
FROM v_iu_metadata_envelope
WHERE iu_id = '<id>';
-- read all tags attached to a piece

T02 of the six-flow runner validates Axis-B (PASS).

3.3 Manifest fields driving Axis B

articles[].pieces[].axis_b:
  legal_document:     <e.g., "luat-xyz-2024" or null+flag>
  section_type:       <substrate vocab term>
  unit_kind:          design_doc_section | law_unit
  professional_tags:  [<string>, …]              # optional extras

3.4 VERIFY check V2

For each piece created by CUT, assert iu_metadata_tag rows exist covering {section_type, unit_kind} at minimum, plus legal_document if the manifest set one.

4. Axis C — Parent / child / grandchild

4.1 Invariants

C1  Exactly one piece per article has parent_local_piece_id = null (the title/root).
C2  Every non-root piece has parent_local_piece_id resolvable within the same article.
C3  depth(piece) = depth(parent(piece)) + 1; root depth = 0.
C4  Max depth ≤ 2 by default (root → clause → sub-point). Deeper ⇒ explicit operator sign-off.
C5  The Axis-C graph is acyclic.
C6  fn_iu_subtree(root_iu_id) returns root + descendants with relative_depth ∈ {0,1,…}.

4.2 Code path (live, 70000x)

SELECT *
FROM fn_iu_subtree('<root_iu_id>')
ORDER BY relative_depth, subtree_position;
-- expect: root + children + grandchildren; no orphans; no cycles

T03 of the six-flow runner validates Axis-C (PASS, root cb211ee6-…, max_depth=2, 8 descendants).

4.3 Manifest fields driving Axis C

articles[].pieces[].axis_c:
  parent_local_piece_id:  <local_piece_id of parent, or null for root>
  depth:                  <int 0|1|2>
  subtree_position:       <int 1-based among siblings>

At CUT time the operator's fn_iu_compose call materializes Axis C via iu_piece_membership rows ordered by piece_order, with iu_tree_path populated by the trigger.

4.4 VERIFY check V3

After CUT, run fn_iu_subtree(article_root_iu_id). Assert:

  • Row count = manifest.articles[].pieces[].length;
  • relative_depth distribution matches the manifest's depth distribution;
  • Parent-child pairs match the manifest's parent_local_piece_id → iu_id translation.

5. Joint invariants across axes

J1  No piece may exist in any axis but not the others — every piece appears in
    Axis A (source_position), Axis B (≥1 tag), and Axis C (parent or root).
J2  Axis A reconstruction must succeed for the article as a whole AND for every
    Axis-C subtree (i.e., a clause's children concatenate to that clause's body).
J3  Axis B tags do not duplicate Axis-C structure (don't tag a piece with
    section_type='paragraph' AND set it as a sub-point of a paragraph — pick one).
J4  Inserting a new piece via fn_iu_collection_add_piece preserves all three
    axes (T04 of the six-flow runner — PASS).

6. Mapping table — manifest field → DB column

Manifest field DB target
axis_a.source_position iu_piece_membership.source_position
axis_a.source_url iu_metadata_tag (kind=source_url, optional)
axis_a.source_hash iu_metadata_tag (kind=source_hash, optional)
axis_b.legal_document iu_metadata_tag (kind=legal_document)
axis_b.section_type information_unit.section_type
axis_b.unit_kind information_unit.unit_kind
axis_b.professional_tags[] iu_metadata_tag (kind=tag-specific)
axis_c.parent_local_piece_id (resolved) iu_piece_membership.parent_iu_id + iu_tree_path
axis_c.depth derived from iu_tree_path
axis_c.subtree_position iu_piece_membership.piece_order within parent

7. Forbidden during MARK across axes

  • Inventing a source_url (Axis A) the user did not provide.
  • Inventing a legal_document tag (Axis B) when the source does not name one.
  • Inferring deep grandchildren (Axis C beyond depth 2) without an explicit marker in the source.
  • Computing a "professional tag" the source does not justify.

When in doubt, leave the field null and add an uncertainty_flag. Operator decides.

8. Reference

  • T01, T02, T03 SQL: ops/iu-core-six-flow-test-readiness/test_01..03_*.sql
  • KB: knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-core-70000x-full-test-readiness-six-user-flows-open-goal/04-p3-six-flow-evidence.md
  • Live vocab discovery: pg_get_constraintdef on information_unit, iu_sql_link, iu_piece_membership.
Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-core-operational-cut-workflow-mark-review-cut-verify/06-three-axis-requirements.md