chore(int9): שער-סטטי ל-INV-INT9 — issues.update({status}) רק במסלול sync-case-status (legal-ai #618) #14

Merged
chaim merged 1 commits from chore/618-inv-int9-ci-gate into main 2026-09-02 01:41:23 +00:00
Owner

מה זה עושה

אוכף את INV-INT9 — בעלות-כתיבה יחידה על issue.status.

ל-issue.status כותב לגיטימי אחד בפלאגין: הג'וב sync-case-status
(ctx.jobs.register("sync-case-status", …), src/worker.ts:898), והקריאה היחידה בכל הריפו
יושבת בתוכו (src/worker.ts:991). עד היום האכיפה הייתה code-review בלבד — וזה בדיוק מה
שאפשר את legal-ai #446 (flip-flop done → in_progress): כותב שני שנכנס בלי שאיש שם לב.

איך

קובץ תפקיד
scripts/int9-guard.mjs השער. Node ESM, אפס תלויות
scripts/fixtures/int9/*.ts (5) פיקסצ'רים ל---self-test, מחוץ ל-src/ בכוונה
package.json npm run int9:guard · npm run int9:guard:self-test

טוקנייזר תו-תו (מחרוזות · template עם ${} מקונן · הערות · רגקס-ליטרלים) בונה מסכת
"קוד רגיל", ומעליה: כל jobs.register("sync-case-status", …) מגדיר טווח-תווים מותר
(איזון-סוגריים), וכל issues.update(…) שרשימת-הארגומנטים שלו מכילה את המזהה status
חייב לשבת בתוכו.

למה טווח-קוד ולא whitelist-לפי-קובץ (הדפוס של leak_guard.py ב-legal-ai): הקריאה
המותרת היחידה וכל קריאה עתידית אסורה יושבות באותו worker.ts בן 1,795 השורות.
רשימת-קבצים הייתה מתירה את כולן. הטווח נגזר מהמבנה האמיתי, ולכן שורד מעבר של הג'וב לקובץ
אחר — ושינוי שם-הג'וב מפיל את השער בקול (אומת, M3 למטה).

חריגה מכוונת: // noqa: INT9 — <נימוק>. noqa: INT9 בלי נימוק הוא הפרה בעצמו.
כשל-פענוח (סוגר שלא נסגר) מפיל את השער ולא נבלע כ"נקי".

מגבלה מוצהרת: תיל-מעידה סטטי, לא הוכחהissues.update(id, patch) שבו אובייקט-העדכון
נבנה במקום אחר אינו נתפס. כתוב בכותרת-הסקריפט וגם בספ (X7, ‏legal-ai#705).

⚠️ ה-PR הזה מוזג בלי קובץ ה-workflow — בכוונה

ה-Gitea Actions runner היחיד על nautilus רשום בהיקף-repo ל-legal-ai בלבד:

select id,owner_id,repo_id from action_runner;   -- id=2 → owner_id=0, repo_id=6  (=legal-ai)

/repos/…/plugin-legal-ai/actions/runnerstotal_count: 0;
/orgs/ezer-mishpati/actions/runnerstotal_count: 0.

לכן job של הריפו הזה לא נאסף לעולם — נמדד: ריצה 3200 / job 3250 ישבה ב-queued עם
started_at: 1970-01-01, בזמן שה-runner היה busy: false והריץ 7/7 שערים של legal-ai.

פאנל בן 5 מומחים הכריע 4/5 שאין לשנות תשתית-ייצור אוטונומית בשביל משימת p3-low.
מיזוג .gitea/workflows/int9-guard.yaml ל-main היה הופך את הריפו מאפס checks
לcheck תקוע-לנצח בכל PR עתידי (13 PRs ממוזגים עד היום), וגורם ל-AC2 להיראות מסופק
בזמן שהוא חסום. לכן ה-workflow יושב ב-PR נפרד וחסום — שהוא גם המדידה: ביום שיירשם
runner לריפו הזה, ה-PR ההוא יהפוך לירוק מעצמו.

עד אז השער רץ ידנית: npm run int9:guard · npm run int9:guard:self-test.

אימות שהורץ (לא "הסקריפט קיים")

npm run int9:guard            → ✓ (13 קבצים)   exit 0
npm run int9:guard:self-test  → ✓ 5/5          exit 0
npx tsc --noEmit                               exit 0
npm run biome / format:check  → Checked 13 files, 0 fixes

מוטציה חיה על הקוד האמיתי (הוזרקה, נתפסה, שוחזרה — src/ לא נגעה ב-PR):

מוטציה תוצאה
M1 — issues.update({status}) בתוך stale-case-reminder src/worker.ts:1018 · exit 1
M2 — shorthand { status } בקובץ אחר (sync-target.ts) src/sync-target.ts:153 · exit 1
M3 — שינוי שם הג'וב ל-sync-case-status-v2 src/worker.ts:991 · exit 1 (הטווח אינו no-op)
שחזור ✓ exit 0

אפס false positives על 4,255 שורות src/ הקיימות. src/ לא שונתה — אין שינוי
התנהגות-ריצה של הפלאגין.

Invariants

  • INV-INT9 (legal-ai docs/spec/X7-paperclip-client-params.md §4) — מנוהל לשער ניתן-להרצה.
  • G2 — מסלול-כתיבה יחיד ל-issue.status.
  • G9 — חריגה מותירה עקבה כתובה בקוד.
  • G12 — הקוד חי בריפו-המעטפת המוצהר.

מסלול-חזרה

git revert של קומיט-המיזוג. אפס תלות בתשתית — לא נגענו ב-runner, ב-Coolify או ב-DB.

Refs ezer-mishpati/legal-ai#618

## מה זה עושה אוכף את **INV-INT9** — בעלות-כתיבה יחידה על `issue.status`. ל-`issue.status` כותב לגיטימי אחד בפלאגין: הג'וב `sync-case-status` (`ctx.jobs.register("sync-case-status", …)`, `src/worker.ts:898`), והקריאה היחידה בכל הריפו יושבת בתוכו (`src/worker.ts:991`). עד היום האכיפה הייתה code-review בלבד — וזה בדיוק מה שאפשר את **legal-ai #446** (flip-flop `done → in_progress`): כותב שני שנכנס בלי שאיש שם לב. ## איך | קובץ | תפקיד | |---|---| | `scripts/int9-guard.mjs` | השער. Node ESM, **אפס תלויות** | | `scripts/fixtures/int9/*.ts` (5) | פיקסצ'רים ל-`--self-test`, **מחוץ ל-`src/`** בכוונה | | `package.json` | `npm run int9:guard` · `npm run int9:guard:self-test` | טוקנייזר תו-תו (מחרוזות · template עם `${}` מקונן · הערות · רגקס-ליטרלים) בונה מסכת "קוד רגיל", ומעליה: כל `jobs.register("sync-case-status", …)` מגדיר **טווח-תווים מותר** (איזון-סוגריים), וכל `issues.update(…)` שרשימת-הארגומנטים שלו מכילה את המזהה `status` חייב לשבת בתוכו. **למה טווח-קוד ולא whitelist-לפי-קובץ** (הדפוס של `leak_guard.py` ב-legal-ai): הקריאה המותרת היחידה **וכל** קריאה עתידית אסורה יושבות באותו `worker.ts` בן 1,795 השורות. רשימת-קבצים הייתה מתירה את כולן. הטווח נגזר מהמבנה האמיתי, ולכן שורד מעבר של הג'וב לקובץ אחר — ושינוי שם-הג'וב **מפיל את השער בקול** (אומת, M3 למטה). **חריגה מכוונת:** `// noqa: INT9 — <נימוק>`. `noqa: INT9` **בלי** נימוק הוא הפרה בעצמו. **כשל-פענוח** (סוגר שלא נסגר) מפיל את השער ולא נבלע כ"נקי". **מגבלה מוצהרת:** תיל-מעידה סטטי, **לא הוכחה** — `issues.update(id, patch)` שבו אובייקט-העדכון נבנה במקום אחר אינו נתפס. כתוב בכותרת-הסקריפט וגם בספ (`X7`, ‏legal-ai#705). ## ⚠️ ה-PR הזה מוזג **בלי** קובץ ה-workflow — בכוונה ה-Gitea Actions runner היחיד על nautilus רשום **בהיקף-repo ל-`legal-ai` בלבד**: ```sql select id,owner_id,repo_id from action_runner; -- id=2 → owner_id=0, repo_id=6 (=legal-ai) ``` `/repos/…/plugin-legal-ai/actions/runners` → `total_count: 0`; `/orgs/ezer-mishpati/actions/runners` → `total_count: 0`. לכן job של הריפו הזה **לא נאסף לעולם** — נמדד: ריצה `3200` / job `3250` ישבה ב-`queued` עם `started_at: 1970-01-01`, בזמן שה-runner היה `busy: false` והריץ 7/7 שערים של legal-ai. פאנל בן 5 מומחים הכריע **4/5** שאין לשנות תשתית-ייצור אוטונומית בשביל משימת `p3-low`. מיזוג `.gitea/workflows/int9-guard.yaml` ל-`main` היה הופך את הריפו מ**אפס checks** ל**check תקוע-לנצח בכל PR עתידי** (13 PRs ממוזגים עד היום), וגורם ל-AC2 להיראות מסופק בזמן שהוא חסום. לכן ה-workflow יושב ב-**PR נפרד וחסום** — שהוא גם המדידה: ביום שיירשם runner לריפו הזה, ה-PR ההוא יהפוך לירוק מעצמו. **עד אז השער רץ ידנית:** `npm run int9:guard` · `npm run int9:guard:self-test`. ## אימות שהורץ (לא "הסקריפט קיים") ``` npm run int9:guard → ✓ (13 קבצים) exit 0 npm run int9:guard:self-test → ✓ 5/5 exit 0 npx tsc --noEmit exit 0 npm run biome / format:check → Checked 13 files, 0 fixes ``` **מוטציה חיה על הקוד האמיתי** (הוזרקה, נתפסה, שוחזרה — `src/` לא נגעה ב-PR): | מוטציה | תוצאה | |---|---| | M1 — `issues.update({status})` בתוך `stale-case-reminder` | ✗ `src/worker.ts:1018` · exit 1 | | M2 — shorthand `{ status }` בקובץ אחר (`sync-target.ts`) | ✗ `src/sync-target.ts:153` · exit 1 | | M3 — שינוי שם הג'וב ל-`sync-case-status-v2` | ✗ `src/worker.ts:991` · exit 1 (הטווח אינו no-op) | | שחזור | ✓ exit 0 | **אפס false positives** על 4,255 שורות `src/` הקיימות. **`src/` לא שונתה** — אין שינוי התנהגות-ריצה של הפלאגין. ## Invariants - **INV-INT9** (`legal-ai docs/spec/X7-paperclip-client-params.md` §4) — מנוהל לשער ניתן-להרצה. - **G2** — מסלול-כתיבה יחיד ל-`issue.status`. - **G9** — חריגה מותירה עקבה כתובה בקוד. - **G12** — הקוד חי בריפו-המעטפת המוצהר. ## מסלול-חזרה `git revert` של קומיט-המיזוג. אפס תלות בתשתית — לא נגענו ב-runner, ב-Coolify או ב-DB. Refs ezer-mishpati/legal-ai#618
chaim added 1 commit 2026-09-02 01:40:21 +00:00
ל-`issue.status` כותב לגיטימי אחד בפלאגין: הג'וב `sync-case-status`
(`ctx.jobs.register("sync-case-status", …)` ב-`src/worker.ts`). עד היום
האכיפה הייתה code-review בלבד — וזה בדיוק מה שאפשר את הבאג של legal-ai
‏#446 (flip-flop `done → in_progress`), כותב שני שנכנס בלי שאיש שם לב.

`scripts/int9-guard.mjs` — Node ESM חסר-תלויות. טוקנייזר תו-תו
(מחרוזות/תבניות עם `${}` מקונן/הערות/רגקס-ליטרלים) בונה מסכת "קוד רגיל",
ומעליה: כל `jobs.register("sync-case-status", …)` מגדיר **טווח-תווים מותר**
(איזון-סוגריים), וכל `issues.update(…)` שרשימת-הארגומנטים שלו מכילה את
המזהה `status` חייב לשבת בתוכו.

הגרעיניות היא טווח-קוד ולא רשימת-קבצים (הדפוס של `leak_guard.py` ב-legal-ai)
במכוון: הקריאה המותרת היחידה וכל קריאה עתידית אסורה יושבות באותו
`worker.ts`. הטווח נגזר מהמבנה האמיתי, ולכן שורד מעבר של הג'וב לקובץ אחר —
ושינוי שם-הג'וב מפיל את השער בקול במקום לפתוח חור שקט.

חריגה מכוונת: `// noqa: INT9 — <נימוק>`. `noqa: INT9` בלי נימוק הוא הפרה
בעצמו — אין השתקה שקטה. כשל-פענוח (סוגר שלא נסגר) מפיל את השער ולא נבלע.

`--self-test` מריץ 5 פיקסצ'רים (`scripts/fixtures/int9/`, מחוץ ל-`src/`
בכוונה) ומאמת ספירת-הפרות מדויקת — הוכחה שהשער נושך.

**ללא חיווט-CI.** ה-runner היחיד על nautilus רשום בהיקף-repo ל-`legal-ai`
בלבד (`action_runner.repo_id = 6`), ולכן job של הריפו הזה לא נאסף לעולם
(נמדד: ריצה 3200 / job 3250 תקוע ב-`queued`, `started_at: 1970-01-01`).
קובץ ה-workflow יושב ב-PR נפרד וחסום, כדי שלא ייכנס ל-`main` שער שמייצר
check תקוע-לנצח בכל PR עתידי. עד אז: `npm run int9:guard` /
`npm run int9:guard:self-test`.

מגבלה מוצהרת: תיל-מעידה סטטי, לא הוכחה — `issues.update(id, patch)` שבו
אובייקט-העדכון נבנה במקום אחר אינו נתפס.

Refs ezer-mishpati/legal-ai#618

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
chaim force-pushed chore/618-inv-int9-ci-gate from fa1bda524d to b24d816cc0 2026-09-02 01:40:21 +00:00 Compare
chaim changed title from chore(ci): שער-CI ל-INV-INT9 — issues.update({status}) רק במסלול sync-case-status (legal-ai #618) to chore(int9): שער-סטטי ל-INV-INT9 — issues.update({status}) רק במסלול sync-case-status (legal-ai #618) 2026-09-02 01:41:14 +00:00
chaim merged commit c6c0b76914 into main 2026-09-02 01:41:23 +00:00
chaim deleted branch chore/618-inv-int9-ci-gate 2026-09-02 01:41:23 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ezer-mishpati/plugin-legal-ai#14