Skip to content

chain (multi-root): 5 issues — idd-issue multi-finding spec hardening family from #48 verify#113

Merged
kiki830621 merged 6 commits into
mainfrom
idd/chain-multi-ab027c23-security-idd-issue-multi-finding-dual-tr
May 20, 2026
Merged

chain (multi-root): 5 issues — idd-issue multi-finding spec hardening family from #48 verify#113
kiki830621 merged 6 commits into
mainfrom
idd/chain-multi-ab027c23-security-idd-issue-multi-finding-dual-tr

Conversation

@kiki830621
Copy link
Copy Markdown
Contributor

@kiki830621 kiki830621 commented May 20, 2026

Refs #75 #76 #77 #79 #80

Summary

Multi-root chain (N=5 roots: #75 #76 #77 #79 #80) solved as one cluster via /idd-all-chain (v2.60+, traversal=dfs). All 5 sister issues from #48's 6-AI verify, all same-file (skills/idd-issue/SKILL.md), shipped as one chain per cluster-PR eligibility heuristic (same-file ✓).

Cluster overview

# root_id Spawn source Phase PR commit
#75 75 root implemented 7c63c4a
#76 76 root implemented 8c9ce5e
#77 77 root implemented 2e45fd6
#79 79 root implemented 8c9ce5e
#80 80 root implemented 2e45fd6

Per-issue details

#75 (root_id=75) — [security] idd-issue multi-finding: dual-track sanitization design (IC_R007 verbatim vs GitHub body sanitization)

NEW ### Content sanitization contract (v2.67.0+, #75) subsection between JSONL example and Merge mechanism (commit 7c63c4a).

  • F1 — Dual-track contract: jsonl finding_quote verbatim (IC_R007 line 1007) + GitHub body finding_quote_display sanitized (strip C0/C1 + warn-and-strip bidi-override U+202A-U+202E + U+2066-U+2069 + normalize CRLF + preserve other Unicode). Sanitization is composition-time projection, not retroactive on stored record.
  • F2sanitize_source_label() bash helper: strips control chars + escapes backticks + refuses @[A-Za-z0-9_-]+ mention tokens. Refuse-not-strip preserves audit trail integrity while forcing callers through rules/tagging-collaborators.md 5-step protocol.
  • F8 — Mandate jq --arg / --argjson parameter binding for JSONL write + body composition (refuses string-interpolation anti-pattern vulnerable to JSON injection).

Partial #75 (CAUTION banner above schema) shipped in commit 8c9ce5e alongside #76 / #79 schema additions.

#76 (root_id=76) — [bug] idd-issue multi-finding: run_id second-precision collision + symlink overwrite hardening

Stage 4.5 jsonl write block hardened (commit 8c9ce5e):

  • run_id format: ISO-8601 second precision → millisecond precision + UTC Z suffix (2026-05-10T17:00:00.123Z)
  • TOCTOU symlink check before write: [ -L "$JSONL_PATH" ] && abort (fail-closed)
  • Noclobber retry helper JSONL_WRITE_GUARD with RANDOM-nonce suffix on hostile concurrency
  • Footer template updated to use new run_id format consistently
  • Cites v2.64.0 IDD human-in-the-loop should conform to the NSQL confirmation protocol (v4.1.0) #103 F4 Layer P irreversible-side-effect vocabulary
#77 (root_id=77) — [enhancement] idd-issue multi-finding spec gap audit: 7 corner-case contract gaps (v2.55.0+)

7 spec contract gap closures (commit 2e45fd6):

Gap Location Closure
1 / P1.1 Override flags section Flag-conflict refusal layering table (explicit flag pairs at Step 0 arg-parse vs auto-trigger conflicts post-Stage 1)
2 / P2.1 Merge mechanism Formal partner_eligible_set = {f | f.id > current_id AND f.id NOT IN merged_into_set AND f.id NOT IN already_routed_set}
3 / P2.2 Stage 3 [Edit row N] soft cap at >5 cumulative edits with warn-not-block confirmation
4 / P2.3 Stage 2 Other second-level [Back to top-3] added as 5th option
5 / F4 Stage 1 entry Source path canonicalization MUST refuse paths outside repo work tree
6 / REQ-5 Stage 4 / 4.5 Agent-crash recovery documented as known gap with trade-off rationale; incremental persist deferred
7 Stage 4.5 Unattended-mode fallback (no TTY + IDD_ALL_UNATTENDED / CI → auto-default to skip-commit)
#79 (root_id=79) — [enhancement] idd-issue multi-finding: audit trail completeness (stale footer links, action type in footer, source_type enum)

3 audit trail completeness gaps (commit 8c9ce5e for Gaps 2-3 + commit 2e45fd6 for Gap 1):

  • Gap 1 / P3.2 — Abort-path now writes minimal aborted: true jsonl with actions[] already dispatched + partial timestamps (disposition (a)). Footer link no longer 404s after abort.
  • Gap 2 / REQ-6 — Footer template adds > **Action**: {create|comment|edit|update} line. Reader can identify dispatch shape from GitHub body without cross-referencing jsonl.
  • Gap 3 / REQ-6 — Schema source_type enum adds "srt" as first-class adapter. Stage 1 source-type description updated to enumerate srt.
#80 (root_id=80) — [enhancement] idd-issue multi-finding: Stage 1 reproducibility heuristics + score formula + N<3 picker

3 LLM-determinism gaps (commit 2e45fd6):

  • Gap 1 / P3.1 — Stage 1 anchor heuristics for the "AI MAY merge / MAY split" clauses: default = preserve original granularity; split only if ≥3 distinct independent topics; merge only if same-topic AND combined length <200 chars.
  • Gap 2 / REQ-3max_possible_score denominator explicitly defined as title_token_count × 2 + min(body_token_count, 300) × 1 from finding's own keyword set. Yields consistent [0,1] scores across invocations.
  • Gap 3 / REQ-3 — Degenerate-case picker shape table (N=0 → skip to Other; N=1 → 1+Other; N=2 → 2+Other; N≥3 → unchanged).

Review status

  • Diagnose ✓ for all 5 issues (per-issue Diagnosis comment)
  • Implement ✓ (5 commits — 1 per atomic concern + CHANGELOG + plugin.json)
  • Verify ⏳ (per-issue 6-AI ensemble — running)
  • Verify-gated: per-issue verify PASS — cluster ready to merge → /idd-close #N (per issue, sequentially) per issue after merge

🤖 Generated by /idd-all-chain. Do NOT add GitHub close trailers (Closes/Fixes/Resolves) — IDD discipline requires manual /idd-close per issue after merge to enforce checklist gate + per-issue closing summary.

…tion type

Addresses #76 + #79 + partial #75 in multi-finding mode spec:

- run_id format: ISO-8601 second precision → millisecond precision + UTC Z
  suffix + nonce-retry on collision. Pre-v2.67.0 second-precision `run_id`
  collided under parallel `/loop` / CI batch / concurrent terminals →
  silent audit-trail overwrite, the irreversible-side-effect failure mode
  added to Layer P vocabulary in v2.64.0 #103 F4.
- TOCTOU symlink check before jsonl write: `[ -L "$JSONL_PATH" ] && abort`.
  Closes the predictable-path + truncate-write hardening gap (#76 MEDIUM —
  attacker with local FS write access could pre-create the audit path as a
  symlink at e.g. ~/.ssh/authorized_keys).
- Noclobber retry helper (`JSONL_WRITE_GUARD`) on hostile concurrency (bash
  for-loops within same millisecond) — combines with ms-precision run_id and
  symlink check to close the collision channel even under adversarial use.
- Audit footer adds `> **Action**: {create|comment|edit|update}` line
  (#79 Gap 2 REQ-6) — reader can identify dispatch shape from GitHub body
  without cross-referencing jsonl.
- Audit footer adds "may be invalid on abort/skip-commit" caveat next to
  Run log line (#79 Gap 1 P3.2 disposition: lightweight documentation
  pending separate decision on write-on-abort vs compensating comment).
- Schema enum adds `"srt"` as first-class source_type (#79 Gap 3 REQ-6)
  resolving the example/enum mismatch where srt sources serialized as
  "pasted-text".
- Schema adds optional `aborted?: boolean` field (#79 Gap 1 enables
  write-minimal-aborted-jsonl path in future, currently documented).
- `finding_quote` CAUTION banner above schema (#75 F1 partial — dual-track
  contract; sanitize_source_label() + content sanitization contract subsection
  follows in next commit).

All changes additive — existing single-issue mode + bundle mode unaffected.
Multi-finding mode users see ms-precision run_id + action-typed footer
on next dispatch.

Refs #76 #79 #75
Addresses #75 F1 + F2 + F8 in multi-finding mode spec:

NEW `### Content sanitization contract (v2.67.0+, #75)` subsection between
JSONL example and Merge mechanism. Three deliverables:

- F1 (HIGH) — Dual-track contract: jsonl `finding_quote` verbatim per
  IC_R007 (line 1007) + GitHub body `finding_quote_display` sanitized.
  Strip C0/C1 control chars, warn-and-strip bidi-override (U+202A-U+202E
  + U+2066-U+2069), normalize CRLF, preserve other Unicode. Sanitization
  is composition-time projection, not retroactive on stored record —
  preserves IC_R007 fidelity. Rationale documented (downstream forensic
  / locale analysis needs verbatim source-of-truth; sanitization at
  rendering boundary, not storage boundary).
- F2 (HIGH) — `sanitize_source_label()` bash helper that strips control
  chars, escapes backticks, and *refuses* (not silently strips) embedded
  `@[A-Za-z0-9_-]+` mention tokens. Refuse-not-strip preserves audit
  trail integrity for legitimate `@scope/package.docx` names while
  forcing the caller through rules/tagging-collaborators.md 5-step
  protocol. All Stage 4 footer composition paths MUST run `<source>`
  through this helper.
- F8 (MEDIUM) — Mandate `jq --arg` / `--argjson` parameter binding
  for JSONL write + body composition with user content. String
  interpolation into jq filter is vulnerable to JSON injection when
  values contain " / \ / control chars. Spec shows the required
  pattern + refused anti-pattern explicitly.

All changes additive — pre-v2.67.0 multi-finding dispatches retain
their verbatim semantics. New dispatches get sanitized GitHub body
content with verbatim jsonl preserved.

Refs #75
…ty heuristics

Addresses #80 (3 LLM-determinism gaps) + #77 (7 spec contract gaps) + #79
Gap 1 (write-on-abort jsonl disposition).

#80 (Stage 1 + Stage 2 LLM determinism):

- Gap 1 / P3.1 — Stage 1 anchor heuristics for the "AI MAY merge / MAY
  split" clauses: default = preserve original granularity; split only if
  paragraph contains ≥3 distinct independent topics; merge only if 2
  consecutive paragraphs same-topic AND combined length <200 chars.
  Reduces variance for typical sources while preserving MAY semantics
  for genuinely ambiguous cases (so mode-switch via ≥2 threshold is more
  reproducible).
- Gap 2 / REQ-3 — `max_possible_score` denominator explicitly defined
  as `title_token_count × 2 + min(body_token_count, 300) × 1` from the
  finding's own keyword set. Yields scores in [0,1] consistently — same
  candidate now displays same absolute score across invocations.
- Gap 3 / REQ-3 — Degenerate-case picker shape table: N=0 → skip to
  [Other] second-level; N=1 → 1+Other (2-option); N=2 → 2+Other (3-option);
  N≥3 → existing top-3+Other (unchanged).

#77 (spec contract gaps):

- Gap 1 / P1.1 — Flag-conflict refusal layering table: explicit flag
  pairs refuse at Step 0 arg-parse (no Stage 1 cost); --bundle-mode +
  auto-trigger refuses post-Stage 1 (detection requires extraction).
- Gap 2 / P2.1 — `partner_eligible_set` formal definition consolidating
  rules previously 18 lines apart in Merge mechanism: `{f | f.id >
  current_id AND f.id NOT IN merged_into_set AND f.id NOT IN
  already_routed_set}`. Pickers SHALL only surface candidates from this
  set; three-way+ merge stays refused (MVP scope).
- Gap 3 / P2.2 — Stage 3 `[Edit row N]` soft cap at >5 cumulative edits
  with warn-not-block confirmation prompt. Heuristic: "if reviewer keeps
  revisiting, signal is more likely wrong source extraction than still
  picking".
- Gap 4 / P2.3 — `[Back to top-3]` added as 5th option in Stage 2 Other
  second-level picker. Escape hatch for users who entered Other by
  mistake or changed mind after seeing second-level options.
- Gap 5 / Security F4 — Stage 1 entry MUST canonicalize source paths +
  refuse paths outside repo work tree. Re-uses Step 1 adapter discipline
  (multi-finding mode does NOT bypass) — prevents `../../etc/passwd`
  leaking into body/jsonl.
- Gap 6 / REQ-5 — Agent-crash recovery semantics documented as known gap
  (not auto-recovered). Trade-off documented: incremental persist (extra
  I/O) vs accepting partial-trail (rare failure mode, observable footer
  link 404). Recovery workflow: user reconciles GitHub state manually +
  re-runs. Incremental persist deferred to future enhancement.
- Gap 7 — Stage 4.5 unattended-mode fallback: `[ ! -t 0 ] AND
  IDD_ALL_UNATTENDED/CI` set → auto-default to `skip-commit` (safest
  for unattended: jsonl locally, no commit). Explicit
  `IDD_JSONL_GITIGNORE_GATE=false` bypass remains as the audit-cited
  escape hatch.

#79 Gap 1 / P3.2 disposition (a) — abort path writes minimal
`aborted: true` jsonl with `actions[]` already dispatched + partial
timestamps. File exists → footer link valid → collaborators viewing
already-dispatched bodies don't get 404. In-memory entries beyond
abort point still discarded.

All changes additive — pre-v2.67.0 invocations retain their semantics;
new invocations benefit from tighter spec contracts.

Refs #80 #77 #79
#77 #79 #80)

- CHANGELOG.md: NEW [2.67.0] entry covering 5 sister bugs from #48 verify
  (security, audit trail, LLM determinism, spec gaps). Each addressed
  in skills/idd-issue/SKILL.md multi-finding mode section.
- plugin.json: version 2.66.0 → 2.67.0 (minor — 5 additive spec hardening
  changes; pre-v2.67.0 invocations retain semantics, new invocations
  benefit from tighter contracts)
- Marketplace.json sync deferred to /idd-close Step 6.5 chain per repo
  precedent (#103 / #102 / #110)

Refs #75 #76 #77 #79 #80
CHANGELOG entry for the 5-issue multi-finding spec hardening family.
Edit was authored alongside the plugin.json bump (58cd66f) but didn't
apply — re-committing now.

Refs #75 #76 #77 #79 #80
4 HIGH blocking findings from 6-AI verify ensemble (PR #113 round 1):

F75-1 (logic, HIGH) — `sanitize_source_label()` corrupts CJK/emoji/UTF-8
  multibyte sequences. `LC_ALL=C tr -d '\200-\237'` operates at byte
  level — empirically reproduced: `中` byte 0x96 in `中文` (e4 b8 ad
  e6 96 87) gets stripped. Replaced with Python's Unicode-aware code-point
  filter that preserves all non-control non-bidi characters while
  stripping C0/DEL/C1/bidi-override (U+202A-U+202E + U+2066-U+2069
  Trojan-Source CVE-2021-42574 family). CRLF normalization included.

F76-1 (logic, HIGH) — BSD date (macOS native /bin/date) does NOT error
  on `%3N` format specifier. It silently emits literal `3N` with exit 0,
  so `2>/dev/null || .000Z` fallback never triggers. RUN_ID becomes
  malformed `2026-05-20T03:09:08.3NZ`. Replaced with dispatch chain:
  (1) try GNU date %3N + regex-validate output shape; (2) fall through
  to Python datetime stdlib (works on every platform); (3) absolute
  fallback to .000Z with warn message. Collision-resistance preserved
  on macOS.

F76-2 (logic, HIGH; DA + regression converged) — `JSONL_WRITE_GUARD()`
  defined but never invoked. Added call site at run-start (right after
  RUN_ID is set), explicitly NOT at materialize phase. Comment explains
  the cross-issue interaction: mutating path at materialize would
  invalidate footer URLs already written by Stage 4 dispatch (#79 Gap 1
  depends on footer URL stability after abort). Also corrected
  `RANDOM % 65536` → `RANDOM % 32768` since bash $RANDOM is 15-bit
  (range 0-32767) — birthday-paradox collision threshold ~181 concurrent
  invocations, not ~256.

#79 abort spec/impl contradiction (regression, HIGH) — new spec at
  Stage 4.5 abort branch said "write minimal aborted: true jsonl" but
  unchanged impl said `unset RUN_LOG_ENTRIES` + "JSONL NOT written".
  Same PR shipped contradiction. Updated abort branch to actually write
  the aborted jsonl with `jq -n --arg/--argjson` parameter binding
  (per #75 F8 mandate), `aborted: true` field (per new schema), and
  partial timestamps. Footer URLs in already-dispatched GitHub bodies
  now resolve to a valid file with abort marker.

Refs #75 #76 #77 #79 #80
@kiki830621
Copy link
Copy Markdown
Contributor Author

/idd-verify --pr 113 — cluster verify report

Phase: verified (verify-gated PASS post-fix)
Mode: PR mode, cluster (5 issues: #75 #76 #77 #79 #80)
Diff: 375 lines initial + 132 lines verify-fix (0fe06ed), 3 files (skills/idd-issue/SKILL.md + CHANGELOG.md + plugin.json)
Reviewers: 5 general-purpose Agents (requirements / logic / security / regression / devil's advocate) + Codex gpt-5.5 xhigh (background)

Aggregate verdict: PASS (post-fix)

Reviewer Initial verdict Post-fix
Requirements PASS (2 cosmetic non-blocking) PASS
Logic CONDITIONAL PASS — 3 HIGH blocking (F75-1 CJK corruption / F76-1 BSD date / F76-2 dead WRITE_GUARD) + 4 LOW PASS (all 3 fixed in 0fe06ed)
Security PASS — 0 blocking + 4 non-blocking PASS
Regression CONDITIONAL PASS — 1 HIGH blocking (#79 spec/impl contradiction) + 2 MEDIUM + 1 HIGH non-blocking (spec-first precedent acceptable) PASS (abort branch implementation written in 0fe06ed)
Devil's Advocate CONDITIONAL PASS — partial polling (2/4 siblings on time) + 3 MEDIUM new findings + LOW nits PASS (WRITE_GUARD ordering + RANDOM 15-bit + abort interaction all addressed)
Codex (gpt-5.5 xhigh) Process gap — codex exec running >12 min with no findings output (file 0 bytes); 6th reviewer non-response recorded Not counted in PASS — 5/6 engine state

Findings dispatched in-PR (commit 0fe06ed)

# Severity Source What Fix
F75-1 HIGH logic sanitize_source_label() byte-level tr -d '\200-\237' corrupts UTF-8 multibyte sequences (CJK / emoji / accented Latin); empirically byte 0x96 stripped Replaced with Python Unicode-aware code-point filter
F76-1 HIGH logic BSD date (/bin/date on macOS) doesn't error on %3N — emits literal 3N; fallback chain never fires; RUN_ID malformed Dispatch chain: GNU %3N + regex-validate → Python datetime stdlib fallback → .000Z last resort
F76-2 HIGH logic + DA + regression JSONL_WRITE_GUARD() defined but never invoked; comment-code drift on RANDOM (bash 15-bit not 16-bit) Added explicit call site at run-start; corrected % 32768 and DOCUMENT in comment block
#79 spec/impl HIGH regression Abort branch spec said "write minimal aborted: true jsonl" but unchanged impl said unset RUN_LOG_ENTRIES + "JSONL NOT written" — same PR shipped contradiction Updated abort branch to actually write the aborted jsonl with parameter binding, aborted: true, partial timestamps
DA-3 MEDIUM DA WRITE_GUARD called at materialize phase would mutate path AFTER Stage 4 footer composed → footer 404 Called WRITE_GUARD at run-start; comment explains ordering rationale + cites #79 Gap 1 dependency

Per-issue verdict

#75 — Content sanitization contract: PASS

  • F1 dual-track (jsonl verbatim + GitHub display sanitized) — Python implementation covers C0/DEL/C1/bidi-override (BOTH U+202A-U+202E AND U+2066-U+2069 Trojan-Source CVE-2021-42574 family) + CRLF normalization + preserves CJK/emoji/accented
  • F2 sanitize_source_label() UTF-8-safe, refuse-on-@token cross-references rules/tagging-collaborators.md 5-step protocol
  • F8 jq --arg / --argjson mandate with REQUIRED + REFUSED examples
  • CAUTION banner above schema readable from jsonl file itself

#76 — run_id collision + symlink: PASS

  • ms-precision dispatch chain (GNU %3N → Python fallback → .000Z) — macOS BSD date hazard closed
  • TOCTOU symlink check fail-closed before any write
  • JSONL_WRITE_GUARD() invoked at run-start; nonce 15-bit space documented; second-collision abort
  • Footer template + schema TypeScript updated

#77 — 7 corner-case gaps: PASS

  • Gap 1 flag-conflict layering table (Step 0 vs post-Stage 1)
  • Gap 2 partner_eligible_set formal definition
  • Gap 3 Edit-row soft cap >5 (warn-not-block)
  • Gap 4 [Back to top-3] 5th option
  • Gap 5 Stage 1 path canonicalization cross-references Step 1 (security reviewer noted cross-reference gap — Step 1 doesn't currently enforce; documented as known descriptive-only for v2.67.0 + flagged for follow-up issue)
  • Gap 6 agent-crash recovery known gap with trade-off rationale
  • Gap 7 unattended fallback (! -t 0 + IDD_ALL_UNATTENDED/CI)

#79 — Audit trail completeness: PASS

  • Gap 1 abort-path now writes aborted: true jsonl with partial timestamps — footer URLs in already-dispatched bodies remain valid (fix commit 0fe06ed)
  • Gap 2 footer > **Action** line
  • Gap 3 "srt" enum + Stage 1 source-type list

#80 — Stage 1 + Stage 2 LLM determinism: PASS

  • Gap 1 anchor heuristics (preserve / split if ≥3 distinct topics / merge if <200 chars same-topic)
  • Gap 2 max_possible_score formula explicit
  • Gap 3 N<3 picker shape table (N=0/1/2/≥3)

Non-blocking observations (filed as follow-up candidates)

These are filed as candidates for /idd-issue follow-up, not as blocking for v2.67.0.

Process gap

Codex gpt-5.5 xhigh background task hung at 12+ minutes with no findings output (file 0 bytes when this report compiled). 5-AI Claude ensemble carried successfully; cross-model independent verification not achieved this round. Recorded transparently per v2.59.0+ Process Gap convention (precedent: chain 1 DA stale-file finding) — not hidden in aggregate verdict.

Next: merge ready

Cluster ready to merge per verify-gated doctrine:

  1. Merge chain (multi-root): 5 issues — idd-issue multi-finding spec hardening family from #48 verify #113 (squash recommended — single review surface)
  2. /idd-close #75 #76 #77 #79 #80 per issue (per-issue closing summary required, no shortcut)
  3. Distribution sync v2.66.0 → v2.67.0 via /plugin-tools:plugin-update issue-driven-dev

Verify: 5/6-AI ensemble PASS (post-fix). Findings dispatched in same PR per feedback_verify_fix_same_pr.

@kiki830621 kiki830621 merged commit 14bc930 into main May 20, 2026
kiki830621 added a commit that referenced this pull request May 20, 2026
…n-dev v2.67.0

Distribution sync after PR #113 merge (14bc930) closing #75 #76 #77 #79 #80
(multi-finding spec hardening family from #48 verify):
- .claude-plugin/marketplace.json: 2.66.0 → 2.67.0 + full description
  (NSQL doctrine follow-up family rationale + per-issue scope + verify
  fix highlights + follow-up candidates)
- plugins/issue-driven-dev/README.md: Version History row v2.56.0–v2.66.0
  → v2.56.0–v2.67.0, appended v2.67.0 paragraph with per-issue scope
  + 5/6-AI verify outcome + fix commit reference

Refs #75 #76 #77 #79 #80
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant