Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
ad07bcd
test: specify deterministic policy JSON export
seonghobae Sep 8, 2026
d82485a
test: require browser JSON download
seonghobae Sep 8, 2026
e834aa7
test: prevent credential URL export
seonghobae Sep 8, 2026
553c1a6
test: keep export cases independently runnable
seonghobae Sep 8, 2026
856958a
feat: project deterministic policy draft export
seonghobae Sep 8, 2026
d883d16
feat: download policy draft JSON locally
seonghobae Sep 8, 2026
384c337
test: type the exported Blob mock argument
seonghobae Sep 8, 2026
2a4249c
docs: define local draft export boundary
seonghobae Sep 8, 2026
e386140
test: expose export portability edge cases
seonghobae Sep 8, 2026
eaf18cb
fix: preserve portable export semantics
seonghobae Sep 8, 2026
b72ce67
docs: record export review repairs
seonghobae Sep 8, 2026
0f1a608
test: expose export data and mock leaks
seonghobae Sep 8, 2026
5c803b1
fix(export): strip query credentials from service URL
seonghobae Sep 8, 2026
90a3d68
test(export): restore browser URL spies
seonghobae Sep 8, 2026
2833391
docs: record export disclosure controls
seonghobae Sep 8, 2026
0e9a349
test(export): reject UI status sentinels in schema types
seonghobae Sep 8, 2026
f3f0f9e
fix(export): narrow portable status schema types
seonghobae Sep 8, 2026
7be6d36
test: verify policy draft browser download
seonghobae Sep 8, 2026
35b9fbd
docs: record browser download evidence
seonghobae Sep 8, 2026
b731c01
docs: preserve non-recursive evidence wording
seonghobae Sep 8, 2026
798f2c4
test: cover JSON export interactions
seonghobae Sep 8, 2026
b9507d9
docs: record JSON export interaction evidence
seonghobae Sep 8, 2026
4d57f60
test: bind revoked URLs to created downloads
seonghobae Sep 8, 2026
bc4c7f1
test: specify download activation failure recovery
seonghobae Sep 8, 2026
d8117e2
fix: report local export activation failures
seonghobae Sep 8, 2026
f7c3ee2
docs: record export activation failure recovery
seonghobae Sep 8, 2026
01ab876
test: fail closed on lossy service URL export
seonghobae Sep 8, 2026
3f16e06
fix: reject lossy service URL export
seonghobae Sep 8, 2026
c2f4887
test: preserve service URL destination semantics
seonghobae Sep 8, 2026
71ef187
docs: align service URL authority contract
seonghobae Sep 8, 2026
170a658
test: preserve complete service URL rejection matrix
seonghobae Sep 8, 2026
bc539d7
docs: preserve service URL rejection lineage
seonghobae Sep 8, 2026
0580f46
test: reject empty service URL delimiters
seonghobae Sep 8, 2026
9d92a9e
fix: reject empty service URL delimiters
seonghobae Sep 8, 2026
6032102
docs: record empty URL delimiter repair evidence
seonghobae Sep 8, 2026
a6cf635
test: preserve encoded service URL path data
seonghobae Sep 8, 2026
a7cac94
docs: bind encoded URL path regression
seonghobae Sep 8, 2026
83f189e
test: expose pre-activation export failure
seonghobae Sep 8, 2026
8d637f9
fix: contain export preparation failures
seonghobae Sep 8, 2026
751e3d5
docs: record export preparation recovery
seonghobae Sep 8, 2026
6529617
docs: define export preparation failure boundary
seonghobae Sep 8, 2026
fd44c84
docs: align export error contract
seonghobae Sep 8, 2026
9edb24f
docs: bind export preparation RED and GREEN
seonghobae Sep 8, 2026
becfd95
docs: document authoring helper invariants
seonghobae Sep 8, 2026
4c74e5d
docs: document policy helper contracts
seonghobae Sep 8, 2026
cef8c0b
merge: restack export stack on full writer-stack tip
seonghobae Sep 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Core invariants:
11. External legal-source updates produce explicit re-evaluation, not silent rewriting.

## Current implementation
The active MVP is a React/Vite browser workspace. State is in memory and there is no production persistence or publication backend. The seven PRD steps are routed to distinct editing surfaces. The collection taxonomy is metadata only. `src/policy.ts` owns deterministic collection-selection/no-collection/mode/purpose/path and non-collection authoring-completeness findings; `src/App.tsx` owns browser orchestration, explicit no-collection and transfer-status capture, warning-to-source navigation, stale dependent-fact invalidation, and deterministic preview rendering. `src/AuthoringFocusController.tsx` is a browser interaction adapter: after explicit rail, previous/next, or review-warning navigation changes the active editing surface, it moves programmatic focus to that surface's heading without changing domain state, intercepting ordinary field interaction, or overriding the separate preview-focus shortcut.
The active MVP is a React/Vite browser workspace. State is in memory and there is no production persistence or publication backend. The browser can download a deterministic versioned JSON draft containing normalized operator-authored facts and readiness finding codes; this local portability projection is not publication, persistence, or legal approval. The seven PRD steps are routed to distinct editing surfaces. The collection taxonomy is metadata only. `src/policy.ts` owns deterministic collection-selection/no-collection/mode/purpose/path and non-collection authoring-completeness findings; `src/App.tsx` owns browser orchestration, explicit no-collection and transfer-status capture, warning-to-source navigation, stale dependent-fact invalidation, and deterministic preview rendering. `src/AuthoringFocusController.tsx` is a browser interaction adapter: after explicit rail, previous/next, or review-warning navigation changes the active editing surface, it moves programmatic focus to that surface's heading without changing domain state, intercepting ordinary field interaction, or overriding the separate preview-focus shortcut.

Authoring completeness is deliberately separate from legal sufficiency. Current readiness rules prove that product-defined fact responsibilities were explicitly addressed; they do not assert that a policy complies with law. Source/effective-date-bound legal validation belongs to the Legal Source Registry -> Review & Publication boundary.

Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ All notable product changes are recorded here. PolicyWeave is pre-release; entri
## Unreleased

### Added
- Deterministic local JSON draft export with a versioned `snake_case` contract, normalized operator-authored facts, explicit incomplete/review-ready state, readiness finding codes, and fail-closed omission of credential-bearing service URLs plus query/fragment data. Unresolved collection mode is serialized as `null`, not the UI empty-string sentinel, and object-URL cleanup is deferred until after download navigation starts. The browser download performs no network transfer and does not claim publication.
- PostgreSQL restart and custom-format dump/restore evidence that preserves NULL-safe complete service/collection-item values, a collecting-without-retention cross-state fixture, and independent no-collection and applies-retention facts, then re-executes no-collection plus both retention-status/rule contradictions against the restored database.
- PostgreSQL two-session concurrency evidence that observes real lock waits, rejects a collection-item writer racing with a no-collection update, and proves competing same-item UPSERTs converge to one row carrying the second writer's label, mode, and path with NULL-safe complete-value assertions and without timing-based transaction sleeps.
- PostgreSQL 18 runtime contract coverage for migration apply/down/apply cycles, item-key UPSERT idempotency, and deferred rejection of no-collection, missing-retention-rule, and revision-owner contradictions. The database remains CI-only and is not a hosted product backend.
Expand Down Expand Up @@ -45,7 +46,7 @@ All notable product changes are recorded here. PolicyWeave is pre-release; entri
- Step-rail, previous/next, and review-warning navigation now transfers programmatic focus to the newly active step heading; ordinary form controls and the dedicated preview shortcut are excluded from that transfer.
- Review-warning navigation now lets the browser scroll the focused owner heading into view; the previous `preventScroll` option could leave that heading hundreds of pixels above the desktop or mobile viewport.
- The publication-area CTA describes a readiness check and directs the operator to responsible review rather than exposing internal implementation boundaries.
- Unshipped JSON export is visibly disabled as `준비 중`, the redundant no-op `검토본 생성` control was removed, and the document title is non-interactive status text.
- JSON export now downloads the current structured draft locally; the redundant no-op `검토본 생성` control remains removed, and the document title remains non-interactive status text.
- Authored generic and custom-checkbox keyboard focus outlines now use the high-contrast `--green` token; a CSS regression test computes and enforces at least 3:1 contrast against white instead of relying on a low-contrast focus color.
- Responsive review behavior and mobile publication feedback were repaired during PR review.
- Responsive CSS contract tests use literal media-query regular expressions, removing the Semgrep dynamic-RegExp finding without suppressing or weakening the scanner gate.
Expand Down
3 changes: 2 additions & 1 deletion docs/PRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ PolicyWeave는 법률 문장을 임의로 창작하는 도구가 아니다. 운
- 제3자 제공과 국외 이전의 명시적 `있음`/`없음` 확인; `있음`일 때만 종속 상세 사실 요구
- 공개 전 검토 요약과 버전 정보
- 정적 공개 URL 발행 계약(후속 백엔드에서 구현)
- JSON 내보내기 가능한 정책 데이터 모델
- 버전이 명시된 JSON으로 현재 정책 사실과 검토 상태를 로컬 내보내기

## 비목표

Expand All @@ -45,5 +45,6 @@ PolicyWeave는 법률 문장을 임의로 창작하는 도구가 아니다. 운
- 제3자 제공 또는 국외 이전을 `있음`으로 확인한 경우 해당 수령자/목적 또는 국가/수령자 사실까지 확인되어야 한다.
- `없음` 확인은 명시적 운영자 사실이며, 이전에 입력한 종속 제공·이전 상세 사실은 상태 변경 시 폐기된다.
- 모든 경고는 해당 입력 단계로 이동할 수 있다.
- JSON 내보내기는 확인되지 않은 값을 임의 사실로 채우지 않고, 자격정보가 포함된 잘못된 서비스 URL을 파일에 기록하지 않으며, 네트워크 전송 없이 현재 작성 사실과 검토 상태를 재현한다.
- 모바일에서도 작성과 미리보기를 전환할 수 있다.
- 키보드만으로 모든 입력과 주요 동작을 수행할 수 있다.
4 changes: 4 additions & 0 deletions docs/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ Protected assets include policy facts, contact details, processing descriptions,

## Verification
Security posture is head-specific. A successful predecessor scan, unresolved finding dismissal, or queued security workflow is not passing evidence. Merge/release decisions must reacquire the exact current head's organization-required security/SAST/review checks.


## Local JSON export
The export path serializes only the current in-memory PolicyWeave draft and deterministic readiness codes into a browser Blob. It makes no network request, uses a fixed filename rather than customer-controlled path text, and defers object-URL revocation until the next task after initiating the download so browsers with deferred navigation can consume the Blob. A service URL containing username or password components is omitted from the file and remains represented by the `service_url_format` finding. Query and fragment components are removed from accepted URLs so token-like values are not copied into the export. The file is still customer-controlled sensitive data; operators remain responsible for its storage and transfer. This control is not encryption, persistence, publication, backup, or authorization evidence.
4 changes: 4 additions & 0 deletions docs/TRD.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This TRD covers the pre-release PolicyWeave browser workspace and the contracts
## Current runtime
- React + TypeScript + Vite browser application.
- Structured authoring state is in browser memory; no production database or backend exists.
- Local JSON export projects the current draft through `createPolicyExport` into deterministic `schema_version = 1` data with normalized facts, explicit `incomplete`/`review_ready` state, and readiness finding codes. Invalid credential-bearing service URLs export as `null`, accepted service URLs are reduced to origin/path without query or fragment data, and unresolved collection modes export as `null`; download uses a browser Blob/object URL, defers URL revocation until the next task so deferred browser navigation can consume it, and performs no network request.
- `src/policy.ts` owns deterministic review logic for collection selection/no-collection attestation/mode/purpose/path and the non-collection authoring-completeness findings for service identity, explicit retention status/period, transfer statuses/details, and privacy contact.
- `src/App.tsx` provides the seven-step authoring flow, review navigation, explicit collection/retention/transfer-status capture, stale dependent-fact invalidation, and deterministic preview projection.
- `src/AuthoringFocusController.tsx` keeps explicit step navigation and review-warning jumps aligned with the newly active step by moving programmatic focus to its heading after the React update and allowing the browser to reveal that target; ordinary form controls and the dedicated preview shortcut are outside this behavior.
Expand Down Expand Up @@ -37,6 +38,9 @@ The separation between collection and retention follows the PIPC Standard Person
- Hosted web endpoints, when introduced, use non-blocking/asynchronous handling and require realistic k6 tests before a p95 <=20 ms page/API claim is recorded.
- Production does not depend on synthetic demo data.

## Local draft portability
The JSON file is a draft portability artifact, not a publication receipt, immutable revision, legal approval, or persistence backup. It may be exported while incomplete so operators can inspect and transfer their authored work without converting blanks into `none`. Contract changes require a new schema version and compatibility evidence; the current fixed filename avoids using customer-controlled text as a filesystem name.

## Hosted persistence/publication entry criteria
Before network persistence lands, define a versioned policy-data schema, migration policy, 3NF relational model, per-item UPSERT/idempotency rules, immutable publication receipt, supersession/rollback semantics, tenant/purpose authorization, audit evidence, encryption/key management, retention/deletion behavior, and backup/restore testing. Use two-or-more-word semantic persistence object names in `snake_case` by default. The revision model must preserve explicit no-collection and explicit retention status independently; `none` must not be materialized from collection absence, and an inapplicable/non-retained state must not carry a live `retention_rule` value.

Expand Down
3 changes: 3 additions & 0 deletions docs/product-technical-gap-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,14 @@ Queue RCA then observed distinct stale heads `cbfbdae4a4bb72433d7bdcc51afdbd8a29

The restart/restore slice remains bounded CI evidence and does not enable a hosted adapter. After the initial atomic-seed repair, review found that paired collection/applies and no-collection/none fixtures could not prove collection and retention are independent, the restored database re-exercised only the no-collection trigger, and a nullable restored `service_name` could evade `<>` through SQL three-valued logic. Test-only head `5e54834873e125b3e3ce4f599e4037e017330638` added the missing cross-state and NULL-safe assertions; exact-head CI `34204279846` was RED only in the restore step with `restart did not preserve independent collection and retention facts`. The next commit seeds a valid collecting revision with `retention_status = none`, keeps authored service and collection-item assertions NULL-safe, and executes status-side missing-rule plus rule-side unexpected-rule transactions against the restored database. Pre-documentation head `202e69d95c94e4432365d6599016a371c0f2cbc3` CI `34204464388` then passed the complete suite. A later exact-head review found that the nullable authored service URL was not selected or asserted even though the evidence claim covered complete service values. Mutation-probe head `aaef3b5489493669cdb53c08a72b6a109fc0b687` deliberately nulled that URL after restart; CI `34205653966` passed every preceding step and failed only the new NULL-safe restore assertion. Commit `57732c6dbec872ad29e97a7f22096dbba9613e9a` removes the probe while retaining literal name/URL checks. These immutable runs establish the TDD transitions but are not substitutes for the final current-head verdict. This is CI durability evidence, not operational backup, tenant authorization, or a released datastore.

The local-draft portability slice converts the previously disabled JSON affordance into a deterministic versioned export. Test-only head `553c1a62514286b0a2e57621072a192556594314` produced exact RED CI `34210028140`: the four new contracts failed while 62 predecessor tests passed. The minimal projection normalizes authored values, keeps unresolved states explicit, exports readiness finding codes, omits credential-bearing service URLs, and downloads through a fixed-name browser Blob without network transfer. This is a mutable draft artifact, not `publication_revision`, durable persistence, backup, authorization, or legal approval. Exact-head review then found two portability defects: immediate object-URL revocation could race deferred WebKit navigation, and an unresolved collection mode leaked the UI sentinel `""` into schema v1. Test-only head `e3861409e3d5f786a1a6756178db71912d1edec0` produced RED CI `34212535756` with precisely 65 passed/2 failed. The minimal repair defers revocation to the next task and narrows the exported mode to the selected enum or `null`; implementation head `eaf18cb4115c1419263b132b5693325e2be42a8d` passed lint, 67/67 tests, build, and all PostgreSQL evidence before final documentation sealing. A later CodeRabbit review found that direct `Object.defineProperty` URL mocks survived `restoreAllMocks()` and that accepted service URLs could copy query/fragment secrets into JSON. Test-first head `0f1a608428681aebfc9187f3d867c59f9905171b` added both regression contracts, but CI `34213620870` was cancelled before execution when the concurrent writer advanced the same branch, so it is not claimed as RED evidence. Concurrent child `5c803b1d2369d4149566b1bf30b83248a46c74c1` preserves that test head and canonicalizes accepted URLs to credential-free origin/path; `90a3d6810ab213af7fb332df0e88f3475109488e` replaces the URL overrides with restorable Vitest spies.

## Current baseline

| Area | Evidence | Status | Commercialization gap | Owner/action | Next verification |
| --- | --- | --- | --- | --- | --- |
| Guided authoring | PRD, ADR-0002, seven routed editors, first-responsibility startup, `getReview`, explicit retention `getDraftReview`, no-collection/transfer states | Repaired foundation | Fresh state no longer skips or falsely completes step 1; collection, retention, transfer and other responsibilities fail closed independently; collection-path evidence remains unstructured free text and legal sufficiency is deliberately separate | Policy Fact Authoring: preserve deterministic completeness and independent authority; add structured path-evidence types only when a real integration/use case proves the need | Exact-head unit/UI edge tests, then browser E2E |
| Draft portability | Versioned `createPolicyExport`, fixed-name browser Blob download, explicit readiness state/finding codes, credential-URL omission | Implemented foundation; exact-head verification required | No import/migration contract or immutable publication semantics; exported files remain operator-controlled sensitive data | Policy Fact Authoring: preserve deterministic schema-versioned projection without network transfer or inferred facts | Exact-head unit/UI/build/browser evidence and future version compatibility tests |
| Customer-fact authority | Zero-inferred startup facts; truthful initial rail state; explicit no-collection; independent explicit retention status; explicit transfer states; stale dependent-fact invalidation | Repaired | No known buyer-facing authority dead end remains in the in-memory seven-step fact model; hosted persistence must preserve these independent states without deriving one from another | Policy Fact Authoring: encode collection and retention as separate revision-owned facts; no automatic no-collection→no-retention rule | Persistence/schema invariant tests and exact-head UI tests |
| Review workspace | Live preview, total blocker count, warning-to-owner navigation, buyer-facing readiness guidance, deterministic step-heading focus transfer, Playwright viewport/screenshot harness | Implemented foundation; offscreen focus repaired and exact-head browser verified | Automated desktop/tablet/mobile focus-scroll evidence is GREEN; broader interaction coverage remains bounded | UX: retain exact-head artifacts, then extend interaction coverage | Exact-head screenshots, keyboard/focus and accessibility checks |
| Accessibility | Semantic controls, visible focus behavior, focus-token >=3:1 regression, muted-text >=4.5:1 authored-surface regression, jsdom focus transition, axe/browser, focused-heading viewport checks, responsive retention transitions, and effective 200% browser-zoom reflow | Partial; bounded exact-head browser GREEN | Native browser UI zoom automation, screen-reader, and manual WCAG evidence remain absent | UX/Test Engineering: add a manual interaction record and remaining cases without claiming conformance from automation alone | Exact-head WCAG/browser matrix plus screen-reader and manual evidence |
Expand Down
Loading
Loading