KB-6A5D

dot-iu-cutter v0.5 — S2→CUT Capability Readiness · S2/cutplan Capability Review (doc 2)

6 min read Revision 1
dot-iu-cutterv0.5s2-to-cut-capability-readinesss2-cutplan-capability-reviewplanner-not-writerdieu442026-05-19

dot-iu-cutter v0.5 — S2→CUT Capability Readiness · S2/cutplan Capability Review

doc 2 of 5 · 2026-05-19 · source-read of cutter_agent/cutplan.py (sha 548eabc5…)

  • tests/test_cutplan_snapshot.py (sha 06e871e7…). Read-only; no code change.

1. Required capability questions — answered from source

Question Finding (cutplan.py line refs)
Receives manifest 9d908a62…? YES. --manifest file is read; recompute_manifest_digest() reproduces dryrun.build_manifest's algorithm (sort union by source_span.line_start; project [address, level, effective_status, span_sha256]; canonical-JSON; sha256) and is checked == --expect-manifest-digest AND == manifest self-declared header digest; raw file sha checked == --expect-manifest-file-sha. Mismatch ⇒ exit 3 BLOCKED.
Identifies the 60 candidates? YES. len(manifest["candidates"]) == --expect-candidate-count (60) enforced; happy-path test reproduces the accepted identity (digest 9d908a62…, file sha 7d56f3ce…, count 60) from the byte-exact fixture via dryrun.
Handles NGUYEN_TAC/KIEN_TRUC/DIEU granularity? PARTIAL — passes level through, does NOT decide IU-vs-container. Each candidate's u["level"] is carried verbatim into iu_mapping/manifest_unit_block preview. N-2 (whether NGUYEN_TAC/KIEN_TRUC_SECTION are IU rows or containers) is NOT ruled — it is unresolved and out of S2's planner scope.
Keeps Điều 44 / draft / obsolete excluded? YES. --exclude-dieu-44 mandatory (missing ⇒ exit 2); any candidate with number=="44" ⇒ exit 3; dieu_44_excluded computed from excluded[] having number 44 + effective_status=="controlled_draft"; idempotency/preview built from candidates only, never excluded.
Fixed canonical DIEU addresses? YES (N-4 satisfied). Uses u["canonical_address"] verbatim; no renumber/normalise; duplicate-address guard across candidates ∪ excluded; no canonicalization-stub invocation.
Output sufficient for a production row-set? NO — see §3. It emits a faithful per-candidate IU mapping/idempotency set, but the would-be write-set targets the v0.4 ledger families, not the real production information_unit/unit_version birth model (doc 3).
Writer or planner? PLANNER ONLY. Net-zero by construction; emits 5 fixed artifacts; every write-set row is "preview": true, "persisted": false.
Signing / credential / idempotency / rollback / verify hooks? Idempotency: YES (OD-1 sha256(canon(source_document_version_id, canonical_address, content_hash)), label-independent, distinctness asserted). Signing/credential/rollback/verify: NONE — none present, by design (a planner has nothing to roll back).

2. Planner correctness — strong (fail-closed, deterministic, isolated)

guards (exit 2 REFUSED): mode!=cut-plan-only; missing --no-db-write/--no-cut/--no-verify;
  missing --exclude-dieu-44; scope!=enacted_only; any of PG_DSN/DATABASE_URL/DIRECTUS_URL/
  PGPASSWORD/PGHOST/PGUSER set in env.
fail-closed (exit 3 BLOCKED, only dryrun_report.md written): file-sha / recomputed-digest /
  self-digest / candidate-count / source-version / docprefix / scope mismatch;
  Điều 44 among candidates; candidate span overlap; duplicate address; reconstruction
  failure; non-distinct idempotency keys; non-deterministic re-build.
determinism: build_cut_plan() run twice in-process; cut_plan_digest compared (d1==d2).
import-isolation: AST test asserts cutplan.py imports ⊆ {argparse,hashlib,json,os,sys,
  pathlib,__future__}; bans psycopg/socket/requests/sqlalchemy/cutter_agent.
test coverage (15/15 GREEN this session): happy-path-real-manifest (3: reproduces accepted
  identity; PASS+5-artifact contract; deterministic rerun), fail-closed (11: digest/
  file-sha/count/source-version/dup-addr/Điều44-candidate + 5 guard-matrix REFUSE),
  import-isolation (1).

3. The decisive limitation — preview targets the wrong schema

cutplan write_set_preview models: manifest_envelope (1), manifest_unit_block (60),
  cut_change_set (1) — these are the v0.4 `cutter_agent/ledger.py` table families.
production reality (doc 3): a Constitution IU is born into `public.information_unit`
  (UNIQUE canonical_address; birth-gate triggers) + `public.unit_version` (FK back to IU),
  with dot_config vocab prerequisites and an IU↔version anchoring transaction. The v0.4
  ledger families are NOT the production write target.
consequence: S2 is a CORRECT manifest→cut-plan PLANNER (identity/exclusion/idempotency/
  determinism all sound) but its preview row-set is NOT yet a production row-set. Re-binding
  the preview to the real information_unit/unit_version birth model + ruling N-2 is a
  bounded, separately-gated planner-revision (NOT a mechanical fix; needs a design ruling).

4. Capability verdict

S2_as_a_CUT_PLANNER:  SOUND and FULLY GREEN (15/15; deterministic; fail-closed;
  import-isolated; net-zero; reproduces the accepted pinned identity). Ready for a
  commit-gate / planner command-review on the targeted-suite precedent (doc 4/5).
S2_as_a_PRODUCTION_WRITER:  NOT APPLICABLE — S2 is intentionally not a writer; the
  production writer is a separate, unbuilt capability (doc 3, GAP-W).

doc 2 of 5. Read-only. Nothing committed/executed. Self-advance PROHIBITED.

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.5-s2-to-cut-capability-readiness/dot-iu-cutter-v0.5-s2-cutplan-capability-review-2026-05-19.md