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.