-
Notifications
You must be signed in to change notification settings - Fork 0
Codex/chat topic scope trust tier #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
062a1ea
1c63a1f
4143c4e
933aa9a
1c908f8
15624de
afeb93c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -84,6 +84,36 @@ export async function GET(request: Request) { | |
| snapshot = null; | ||
| } | ||
| } | ||
| let intakeDraft: unknown = null; | ||
| let publicationEligible = true; | ||
| let intakeTitle = ""; | ||
| let intakeAnswer = ""; | ||
| for (const event of eventsByCandidate.get(id) ?? []) { | ||
| if (typeof event.metadata_json !== "string") continue; | ||
| try { | ||
| const metadata = JSON.parse(event.metadata_json) as Record< | ||
| string, | ||
| unknown | ||
| >; | ||
| if ( | ||
| metadata.intakeDraft && | ||
| typeof metadata.intakeDraft === "object" && | ||
| !Array.isArray(metadata.intakeDraft) | ||
| ) { | ||
| intakeDraft = metadata.intakeDraft; | ||
| const draft = metadata.intakeDraft as Record<string, unknown>; | ||
| intakeTitle = | ||
| typeof draft.title === "string" ? draft.title : intakeTitle; | ||
| intakeAnswer = | ||
| typeof draft.answer === "string" ? draft.answer : intakeAnswer; | ||
| } | ||
| if (metadata.publicationEligible === false) { | ||
| publicationEligible = false; | ||
| } | ||
| } catch { | ||
| // Invalid historical metadata is ignored by this read-only projection. | ||
| } | ||
| } | ||
|
Comment on lines
+87
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg '(^|/)app/admin/(api/web-search-candidates/route\.ts|AdminDashboard\.tsx)$|CandidateSnapshot|draftSnapshot|intakeDraft|normalizeReviewedCandidateSnapshot' || true
echo
echo "== route ts outline =="
ast-grep outline app/admin/api/web-search-candidates/route.ts --view expanded || true
echo
echo "== AdminDashboard outline relevant =="
ast-grep outline app/admin/AdminDashboard.tsx --view expanded || true
echo
echo "== relevant snippets =="
sed -n '1,180p' app/admin/api/web-search-candidates/route.ts
echo
sed -n '280,330p' app/admin/AdminDashboard.tsxRepository: tomnyson/tuyen_truyen_an_toan_giao_thong Length of output: 8637 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== occurrences =="
rg -n "draftSnapshot|intakeDraft|normalizeReviewedCandidateSnapshot|CandidateSnapshot|tags\.join|citations\.map|startEdit" app/admin -S || true
echo
echo "== focused read with line numbers =="
echo "-- web-search-candidates route 1-180 --"
cat -n -v app/admin/api/web-search-candidates/route.ts | sed -n '1,180p'
echo
echo "-- AdminDashboard 1,280-340 --"
cat -n -v app/admin/AdminDashboard.tsx | sed -n '1,40p;260,350p'Repository: tomnyson/tuyen_truyen_an_toan_giao_thong Length of output: 16682 Validate
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| return { | ||
| id, | ||
| initialAnswer: candidate.initial_answer_text, | ||
|
|
@@ -99,6 +129,10 @@ export async function GET(request: Request) { | |
| createdAt: candidate.created_at, | ||
| updatedAt: candidate.updated_at, | ||
| snapshot, | ||
| draftSnapshot: publicationEligible ? intakeDraft : null, | ||
| intakeTitle, | ||
| intakeAnswer, | ||
| publicationEligible, | ||
| sources: (sourcesByCandidate.get(id) ?? []).map((source) => ({ | ||
| title: source.title, | ||
| url: source.official_url, | ||
|
|
@@ -151,6 +185,33 @@ export async function PATCH(request: Request) { | |
| return noStoreJson({ error: "Candidate hoặc version không hợp lệ." }, 400); | ||
| } | ||
| try { | ||
| const workflow = await listWebSearchCandidates(); | ||
| const publicationBlocked = workflow.events.some((event) => { | ||
| if ( | ||
| event.candidate_id !== candidateId || | ||
| typeof event.metadata_json !== "string" | ||
| ) { | ||
| return false; | ||
| } | ||
| try { | ||
| const metadata = JSON.parse(event.metadata_json) as Record< | ||
| string, | ||
| unknown | ||
| >; | ||
| return metadata.publicationEligible === false; | ||
| } catch { | ||
| return false; | ||
| } | ||
| }); | ||
| if (publicationBlocked) { | ||
| return noStoreJson( | ||
| { | ||
| error: | ||
| "Đây là hướng dẫn an toàn cho MVP, không được đưa vào kho căn cứ pháp lý.", | ||
| }, | ||
| 409, | ||
| ); | ||
| } | ||
|
Comment on lines
+188
to
+214
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== files =="
git ls-files | rg 'app/admin/api/web-search-candidates/route\.ts|PRODUCT_REQUIREMENTS\.md|web_search_candidate_events|web_search_candidates' || true
echo "== route outline/size =="
wc -l app/admin/api/web-search-candidates/route.ts 2>/dev/null || true
ast-grep outline app/admin/api/web-search-candidates/route.ts 2>/dev/null | head -120 || true
echo "== relevant route lines =="
sed -n '1,260p' app/admin/api/web-search-candidates/route.ts
echo "== search listWebSearchCandidates == "
rg -n "function listWebSearchCandidates|const listWebSearchCandidates|listWebSearchCandidates|web_search_candidate_events|publicationEligible" -S .Repository: tomnyson/tuyen_truyen_an_toan_giao_thong Length of output: 17170 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== lib/web-search-candidates.ts relevant section =="
sed -n '630,720p' lib/web-search-candidates.ts
echo "== tests/listWebSearchCandidates section =="
sed -n '310,350p' tests/web-search-candidates.test.mjs
echo "== docs/product requirements relevant section =="
sed -n '770,810p' docs/PRODUCT_REQUIREMENTS.md
sed -n '2355,2390p' docs/TECHNICAL_SPEC.md
echo "== targeted query evidence from implementation =="
rg -n "publicationEligible|web_search_candidate_events|LIST|listWebSearchCandidates|migrations|schema" db lib tests docs -SRepository: tomnyson/tuyen_truyen_an_toan_giao_thong Length of output: 26106 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== behavioral probe: bounded query misses candidate outside top-200 =="
python3 - <<'PY'
from dataclasses import dataclass
from typing import Any
`@dataclass`
class Candidate:
id: str
updated_at: int
lifecycle_status: str = "draft"
`@dataclass`
class Event:
candidate_id: str
metadata_json: str
def list_web_search_candidates(candidates: list[Candidate], events: list[Event]):
# Mirrors lib/web-search-candidates.ts: top-200 candidates and candidate_id IN that set.
top = sorted(candidates, key=lambda c: (-c.updated_at, c.id))[:200]
top_ids = {c.id for c in top}
return [
{"candidates": top},
{"sources": []},
{"revisions": []},
{
"events": sorted(
[e for e in events if e.candidate_id in top_ids],
key=lambda e: (
e.candidate_id,
-len(e.metadata_json), # placeholder for occurred_at if metadata was JSON
e.candidate_id
)
)
},
]
def publication_blocked(listed, candidate_id: str):
# Mirrors app/admin/api/web-search-candidates/route.ts PATCH check.
for event in listed[3]["events"]:
if event.candidate_id != candidate_id:
continue
try:
metadata = __import__("json").loads(event.metadata_json)
if metadata.get("publicationEligible") is False:
return True
except Exception:
return False
return False
candidates: list[Candidate] = [
Candidate("older", 1),
Candidate("target", 0),
]
events: list[Event] = [
Event("target", '{"publicationEligible": false}''),
]
listed = list_web_search_candidates(candidates, events)
print("listed_candidate_count=" + str(len(listed[0]["candidates"])))
print("listed_events_count=" + str(len(listed[3]["events"])))
print("publication_blocked=" + str(publication_blocked(listed, "target")))
PY
echo "== schema table for web_search_candidates =="
sed -n '760,810p' db/schema.tsRepository: tomnyson/tuyen_truyen_an_toan_giao_thong Length of output: 435 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== behavioral probe: bounded query misses target candidate outside top-200 =="
python3 - <<'PY'
import json
candidates = [
["older", "2026-08-01T00:00:00Z"],
["target", "2025-01-01T00:00:00Z"],
]
events = [
["target", json.dumps({"publicationEligible": False})],
]
top = sorted(candidates, key=lambda c: (- (c[1] > "1970-01-01"), c[0]))[:200]
top_ids = {c[0] for c in top}
listed_events = [e for e in events if e[0] in top_ids]
publication_blocked = any(
e[0] == "target" and json.loads(e[1])["publicationEligible"] is False
for e in listed_events
)
print(f"listed_candidates={len(top)}")
print(f"listed_events_for_target={1 if 'target' in top_ids else 0}")
print(f"metadata_is_visibility_flag_on_target_event={'publicationEligible' in json.loads(next(e[1] for e in events if e[0] == 'target'))}")
print(f"publication_blocked_from_list_when_target_outside_top_200={publication_blocked}")
PY
echo "== schema table for web_search_candidates =="
sed -n '760,810p' db/schema.tsRepository: tomnyson/tuyen_truyen_an_toan_giao_thong Length of output: 2208 Guard every target candidate before allowing admin mutations.
🤖 Prompt for AI Agents |
||
| const result = | ||
| action === "save_revision" | ||
| ? await saveWebSearchCandidateRevision( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Static analysis flags the buttons at Lines 436-440 for missing an explicit
typeattribute. These cards render as siblings of the editor<form>, not inside it, so there is no live risk of accidental form submission here. Settingtype="button"explicitly is still good defensive practice in case the markup is restructured later.🧰 Tools
🪛 React Doctor (0.9.1)
[warning] 436-436: Your users can submit the form by accident because a
<button>with notypedefaults to submit.Set an explicit button
typeso plain buttons do not submit forms by accident:type="button","submit", or"reset".(button-has-type)
[warning] 437-437: Your users can submit the form by accident because a
<button>with notypedefaults to submit.Set an explicit button
typeso plain buttons do not submit forms by accident:type="button","submit", or"reset".(button-has-type)
[warning] 438-438: Your users can submit the form by accident because a
<button>with notypedefaults to submit.Set an explicit button
typeso plain buttons do not submit forms by accident:type="button","submit", or"reset".(button-has-type)
[warning] 439-439: Your users can submit the form by accident because a
<button>with notypedefaults to submit.Set an explicit button
typeso plain buttons do not submit forms by accident:type="button","submit", or"reset".(button-has-type)
[warning] 440-440: Your users can submit the form by accident because a
<button>with notypedefaults to submit.Set an explicit button
typeso plain buttons do not submit forms by accident:type="button","submit", or"reset".(button-has-type)
🤖 Prompt for AI Agents
Source: Linters/SAST tools