auto-improve: Hold the bodies the screens send against the fields the server reads - #1
Merged
Conversation
The user and admin guides described screens nobody had pictured since the first documentation commit, and the capture script that could have fixed that carried a password literal and a hard-coded target. The guides are rewritten to the shared guide standard around thirty-four captures taken from a seeded local install: first five minutes, screen by screen, the common jobs, and the messages people actually hit. Environment variables and settings are tabulated from the code rather than from memory. The capture script now refuses to run without its own URL and credential variables, stops unless the target is loopback, and puts back the two settings it changes for the run. The old guides point at the new ones so there is one canonical copy; README and the docs hub follow.
The 결재 요청 회수 and 보완 재개 dialogs posted their text as "comment", the field the approval and rejection dialogs use, while both handlers decode a "reason" and refuse unknown fields. Every press ended in INVALID_JSON, so neither button ever worked from the screen; only the API did. The dialog now sends the field each action reads, and stops calling itself 심의 반려 with a 반려 button when it is withdrawing or reopening.
The approval step was the one part of the user guide told in words alone: the capture run never turned approval on, so there was no 승인 대기 review, no 결재 전 확인 card, no signature dialog and no rejection to photograph. The capture script now turns approval_enabled on for the run alongside allow_self_review (both read first and put back), names the capture account as approver so submissions pass the gate, and seeds a review parked for signature and one the approver sent back. The approved review now travels through the signature too, so the list shows every state. Section 4-4 walks the approver through the four new screens, and the messages a reviewer meets when withdrawing or reopening are in the table. The rest of the captures were retaken in the same run; both PDFs are regenerated with the shared tool.
features.md was re-pointed at the new captures two rounds ago, but the PDF next to it (and the complete manual that embeds it) kept showing the screens that were deleted in the same change. Nothing tied the two together: the PDFs came from scripts/generate_pdf.js, a converter this repository carried on its own, while the user and admin guides were already built with the shared md2pdf tool. scripts/build_docs_pdf.sh now drives md2pdf for all six PDFs, by name or all at once, and the complete manual is the three technical documents concatenated inside docs/ so their image paths still resolve. The repository-only converter is gone along with the marked devDependency that existed only for it, and the docs/pdf/ copies it kept -- the same files twice, referenced by nothing. The complete manual's description in both READMEs now says what it actually contains.
Two rounds of guide work renamed every capture. The guides followed, but nothing said whether they all had: a reference to a picture that is not on disk only surfaces when the PDF is built, and a capture no document uses just sits there after a rename. precheck.sh now compares the file names the docs reference with what docs/screenshots holds, in both directions, and fails on either.
The PDFs are committed next to the Markdown they come from, and nothing rebuilds them on its own: features.md was re-pointed at the new captures while its PDF kept showing the old ones until someone opened it. md2pdf lives outside this repository, so precheck.sh cannot rebuild anything -- instead it asks git whether a document, or a picture it embeds, changed after its PDF was last baked, in the working tree or in history, and says which build_docs_pdf.sh name to run. The document-to-PDF mapping now lives in one describe() function in build_docs_pdf.sh, which precheck reads through a --list mode that needs no converter, so the two scripts cannot disagree about what feeds what.
The only release asset is the image archive, and the admin guide's install section told the operator to "use the repository's compose.yaml" -- which a closed network has no way to reach. The section now carries the file in full, with a note on the one line worth changing, and the upgrade step no longer names a next version that a blanket bump would rewrite into a no-op sed. A copy of a file drifts, so a test holds the guide's yaml block to the real compose.yaml byte for byte, and the guide joins the files whose release version must agree with VERSION. The PDF is re-baked.
Every capture in the guides comes out of a 1440x900 headless window, and a picture pasted in from somewhere else is the one that looks wrong on the page. precheck now reads the width from each PNG's IHDR chunk with od -- nothing to install -- and names any file that is not 1440 wide. Height is left alone, since a full-page capture is legitimately taller.
The admin guide's environment-variable table claims to be everything the runtime reads, and its service-settings tables give the value a fresh installation starts with. Both were written by hand and neither was checked: the deleted-evidence retention was filed under the general tab while the screen keeps it under file security, service_name was presented as a value an operator can change although the screen shows it fixed, and the OIDC tab had no key table at all -- an operator scripting PUT /admin/settings/oidc had to read the source to learn the names. Two tests now hold the guide to the code. The environment-variable table must list exactly the names that os.Getenv reads in internal/ and cmd/ (the test-only database helper aside). Each settings table must list exactly the keys the migration seeds and the settings screen put on that tab, with the default the seeds wrote first -- or, for keys the seeds never wrote, the fallback the screen shows. The OIDC tab gets its table, the misfiled row moves, and the PDF is re-baked.
The user guide's 막혔을 때 table and the admin guide's 장애 대응 table exist so that someone can search the guide for the text in front of them -- an error on the screen, a line in the log. Both tables were written by hand and nothing checked them against the code. The admin guide quoted "audit event could not be recorded" as a 서버 로그 row under component audit, but that string only ever reaches the container's standard output; the row the screen actually gets is "감사 이벤트를 기록하지 못했습니다." The list of log components in 5-3 named seven where the code writes twelve, so an operator filtering by component could not know about evidence, export, review, admin or bootstrap. Two tests now hold the tables to the code. Every phrase the user guide quotes in backticks must appear letter for letter in a server literal or in the web sources, with `N` before a counter and `<…>` standing for what the message fills in at runtime. Every "로그에 `…`" in the admin guide must be a string the Go code prints, and every "서버 로그 `component` 의 `…`" must match a Store.Log call with that exact component and message; the component list in 5-3 must equal the set the code uses. The two rows are corrected, the format-string example becomes a placeholder, and both PDFs are re-baked.
docs/README.md and docs/operations.md tell people to run scripts/precheck.sh and scripts/build_docs_pdf.sh as written, and the usage comments at the top of each script say the same. All three shell scripts carry a shebang but were committed as 100644, so following the docs on a fresh clone ends in "Permission denied" and one learns to put bash in front. The mode is now 100755 in the index, which holds on checkouts where core.filemode is off as well.
…shows The user guide names buttons, menus, filters, statuses and badges in backticks so a reader can find them on the screen, and the standard asks for those names to match the UI to the letter. Nothing held them to it: a button renamed on the screen left the guide pointing at something that was not there. A test now takes every backticked phrase in the walkthrough, screen and task sections and the glossary and requires it to exist in the web sources or, for text the server composes, in the Go sources, sharing the placeholder-aware matcher with the 막혔을 때 table test. Running it found three quotes the screen never shows: `+ 신규 심의 요청` (the plus is an icon, the text is `신규 심의 요청`), `2 / 210` and `SC-2026-000001` (runtime values written as if they were labels). The guide now names the button as the screen does and gives the two values as plain examples; the admin guide's `+ 로컬 사용자` is fixed the same way. Both PDFs are re-baked with the shared converter.
The admin guide's 5-1 table is what an operator wires a load balancer and a monitor to, and its 5-4 table is what they read when a job is stuck. Neither was held to the code. A path listed with the wrong method sends whoever follows it into a 405; a job type the code stopped enqueuing, or started enqueuing without a row, leaves the operator nothing to act on. A test now checks each 5-1 row against the s.handle registration in server.go -- method, path, and whether it is public or needs the named role -- and compares the 5-4 job types both ways with the INSERT INTO jobs statements in the Go code. Both tables already matched.
… user guide The guide described Ctrl+K search, the participants card and what a re-review copy leaves behind in words only. All three are now shot from a running install and placed in 3-1, 3-4 and 4-3, and the search paragraph says what the palette lists before and after typing. capture_all.js takes SECCHECK_CAPTURE_ONLY so one screen can be added without re-shooting the forty already in the guides; the run still seeds and walks every screen, it just does not write the others. The copy scene waits on the toast itself, since it lives four seconds and a wait on a hidden <option> had let it lapse.
Every sidebar label must appear in a section heading or a picture caption of the user or admin guide, so a screen cannot be added without one of the guides naming it. The simulator's caption carried the page title (Rule Engine 시뮬레이터) rather than the menu name; the caption now uses the menu name and the text gives both.
The user guide quoted the button as 심의 요청자 인계, which is the title of the dialog it opens; the button itself reads 요청자 인계. The guide now names the button and says what the dialog is called.
…le on the bell The 장애 대응 table quoted 작업 재시도 소진 and 감사로그 무결성 실패, which are labels from the notification preference screen; the bell shows 작업이 재시도를 모두 소진했습니다 and 감사로그 체인 검증 실패, so the administrator who searched the guide for what they had just read found nothing. The stalled-queue and unwritable-volume alerts had no row at all. Every alert the code sends to administrators now has a row keyed by its title, with where the notification's own button leads and what to do, and a test holds the guide to the send sites both ways.
The alert told the reader to search the server log for the notify component; the mail worker has only ever logged as notification.
The quotation check only asks that each name in 3-2 exist somewhere in the screens, so a chip added to the list left the guide short without a test noticing. The chips and the sort table of Reviews.tsx are now held to the two lines both ways.
web/package.json has run vitest since the start, but no test file was ever written, and `vitest run` with nothing to run exits 1. Nothing in the pipeline called it, so nobody saw; the improvement runner now does, and every round would have stopped at that step. The one test reads each post/put/patch call whose body is written out at the call site and checks its keys against internal/web/payloads.go -- the mistake of 008c9ec, where the withdraw and reopen dialogs sent `comment` to endpoints that read `reason`, in a form a test can catch. The run is added to precheck.sh and to the CI frontend gate.
Owner
Author
|
🔒 보호 파일을 건드려 자동 머지하지 않습니다. 사람이 검토해 주세요.
(run 2026-09-12-063115-SecCheck-improve) |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
hkjang
added a commit
to hkjang/aidev
that referenced
this pull request
Sep 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
자율 개선 에이전트가 생성한 PR입니다. (run 2026-09-12-063115-SecCheck-improve, base c32eec7)
2026-09-12
cd web && npm test가 테스트 파일 0개로 exit 1)를 미리 고치기: 프런트엔드 post/put/patch 호출의 리터럴 본문 키를 payloads.go 와 대조하는 vitest 추가 + precheck.sh·ci.yml 프런트엔드 단계에 그 실행을 포함 (가치 3 / 위험 1 / 작업량 M)5963517)이 아직 main 에 없어 auto/2026-09-12-0611 위로 fast-forward 한 뒤 작업했다. 수정 과제 확인: GitHub 의 release.yml 실행은 API 로 조회한 최근 5건(v1.0.140v1.0.144)이 모두 success 라 릴리즈 워크플로 자체는 실패한 적이 없고, 12회 연속 실패는 러너의[ -d node_modules ] || npm ci가 lock 없는 루트에서 돈 것(러너 쪽에서 수정 완료)이었다. 그런데 고쳐진 러너(bin/run.sh)는 이제 web/ 에서npm ci→npm test --silent→npm run build --silent를 돌리는데, web/package.json 의test는vitest run이고 테스트 파일이 하나도 없어 "No test files found, exiting with code 1" 로 exit 1 이 된다 — 로컬에서 재현해 확인. 다음 회차도 같은 자리에서 실패할 것이므로 보류 목록의 vitest 아이디어를 앞당겨 web/src/lib/payloads.test.ts 를 만들었다: web/src 의post/put/patch(path, { … })와api(path, { method, body: JSON.stringify({ … }) })중 본문을 호출 자리에 리터럴로 적은 28곳을 괄호·문자열 균형 파서로 읽어 (1) 메서드+경로(${…}↔{id}정규화)가 payloads.go 의 요청 본문 표에 있는지, (2) 적은 키가 모두 서버가 읽는 필드인지(008c9ec 의 comment/reason 어긋남 류) 확인하고, 스캐너가 망가져 0건이 되는 것을 막는 하한(15건)을 둔다. 스프레드(...form)·변수 본문은 검사 대상에서 제외한다. 검증: 정상 트리 3/3 통과, 고의로 어긋낸 세 트리(comments 의{body}→{text}/ totp/enable 경로를 totp/verify 로 / payloads.go 의 to_user_id 를 target_user_id 로)에서 각각 실패 확인, 러너와 동일한 세 명령(cd web && [ -d node_modules ] || npm ci,npm test --silent,npm run build --silent)을 bash -o pipefail 로 실행해 모두 exit 0, precheck.sh 전체 통과(gofmt·go vet·go test·프런트엔드 tsc+vitest+vite build·가이드 그림·가이드 PDF 신선도·gitleaks "no leaks found"). precheck.sh 프런트엔드 단계와 ci.yml 의 "Frontend build and dependency gate" 에npm --prefix web test --silent를 넣어 CI 가 이 대조를 돌리게 했다(워크플로를 느슨하게 한 것이 아니라 한 단계 더한 것). 가이드는 표준 구성·캡처 41장·PDF 2종이 이미 갖춰져 precheck 의 그림·PDF 검사를 통과하므로 이번 회차는 원고를 바꾸지 않았다. 참고: DecisionModal 이action('reopen', data)로 경로와 본문을 모두 변수로 넘기는 바로 그 자리(008c9ec 의 현장)는 정적으로 읽을 수 없어 아직 검사 밖이다.{ reopen: ['reason'], withdraw: ['reason'], … })로 빼고 vitest 가 그 표까지 읽게 해 008c9ec 의 현장을 검사 범위에 넣기 (가치 2 / 위험 1 / S); vitest 를 get/del/upload 경로까지 넓혀 화면이 부르는 모든 메서드+경로가 server.go 의 s.handle 등록에 있는지 대조 — 라우트 이름이 바뀌면 화면이 404 를 받는 것을 잡는다 (가치 3 / 위험 1 / M); USER_GUIDE 3-8 의 알림수신 설정창을 캡처해 3-7 표 옆에 싣기 (가치 2 / 위험 1 / S, 서버 필요); Notifications.tsx 의 adminDestination 에 API_KEY_REVOKED·ACCOUNT_LOCKED·USER 대상 인계 알림의 이동 버튼 추가 — 캠페인 밖, docs_test 표 갱신 동반 (가치 2 / 위험 2 / S)🤖 auto-improve 2026-09-12 · https://hkjang.github.io/aidev/projects/SecCheck/