Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions docs/项目文档/02-架构/02-NoName-代理系统.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@

### 5.1 命令层压力正在下降

`reviewed apply` 的请求构造、快照校验、显式 apply、人工 review intent 与 second guardrail 记录逻辑已经下沉到 `noname_apply.rs`。
`reviewed apply` 的 request input 构造、快照校验、显式 apply、人工复核结果写回、review intent、second guardrail 预校验与记录逻辑已经下沉到 `noname_apply.rs`。

`tauri_commands.rs` 在这条链路上主要保留:

Expand All @@ -82,7 +82,7 @@
- game engine / plot state 读取与回写
- 命令错误返回

后续仍可继续观察低风险 apply 与生成主链中是否还有适合抽离的重复编排,但不应再把新的 reviewed apply 权限逻辑直接堆回命令层。
这意味着 reviewed apply 的命令层已经进一步变成薄壳,本轮 A2/A3 可视为阶段性收口完成。后续仍可继续观察低风险 apply 与生成主链中是否还有适合抽离的重复编排,但不应再把新的 reviewed apply 权限逻辑直接堆回命令层。

### 5.2 Web mock 与后端容易漂移

Expand Down
7 changes: 4 additions & 3 deletions docs/项目文档/03-规划/01-现状评估.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@

`tauri_commands.rs` 仍然承担大量主游戏编排工作,继续向里面叠逻辑会提高维护风险和冲突概率。

当前 `NoName reviewed apply` 链路已经完成第三轮减压:
当前 `NoName reviewed apply` 链路已经完成第五轮减压:

- reviewed apply request 构造、快照校验与显式 apply 已下沉到 `noname_apply.rs`
- trace 获取 / plot state 更新 / trace 回写已复用命令层共用壳层
- human review intent 与 second guardrail 决策记录已下沉到 `noname_apply.rs`
- human review intent、人工复核结果写回与 second guardrail 预校验 / 决策记录已下沉到 `noname_apply.rs`
- `apply_noname_reviewed_output` 的 request input 组装也已回收到 `noname_apply.rs`

后续风险重点不再是 reviewed apply 本身,而是其他低风险 apply 与剧情生成主链是否继续膨胀。
因此 A2/A3 在当前主开发线可以视为阶段性完成。后续风险重点不再是 reviewed apply 本身,而是其他低风险 apply 与剧情生成主链是否继续膨胀。

### 3.2 前后端 mock 语义漂移

Expand Down
7 changes: 4 additions & 3 deletions docs/项目文档/03-规划/02-后续路线图.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@

当前状态:

- 已完成第三轮下沉
- 已完成第五轮下沉
- reviewed apply 的 request builder 与 plotText manual apply helper 已进入 `noname_apply.rs`
- `tauri_commands.rs` 在 reviewed apply 路径上已减少内联快照构造与包装逻辑
- reviewed apply 命令已开始复用共享的 trace 读取、plot state 更新和 trace 回写壳层
- human review intent 与 second guardrail 决策记录已进入 `noname_apply.rs`
- 下一步若继续减压,应优先评估低风险 apply 或剧情生成主链中仍重复的编排,而不是扩大新的 apply scope
- human review intent、人工复核结果写回与 second guardrail 预校验 / 决策记录已进入 `noname_apply.rs`
- `apply_noname_reviewed_output` 的 request input 组装已回收到 `noname_apply.rs`
- 本轮后 A2/A3 可视为阶段性完成;下一步若继续减压,应优先评估低风险 apply 或剧情生成主链中仍重复的编排,而不是扩大新的 apply scope

## P2:收口后继续推进

Expand Down
13 changes: 10 additions & 3 deletions docs/项目文档/03-规划/04-未来两周开发排期.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,12 @@

当前状态:

- 第三轮已完成
- 第五轮已完成
- `NoNameReviewedApplyRequest` 构造与 plotText manual apply helper 已从命令层下沉到 `noname_apply.rs`
- trace 获取 / result 回写的共用壳层已开始抽出并复用到两个 reviewed apply 命令
- human review intent 与 second guardrail 决策记录已从命令层下沉到 `noname_apply.rs`
- 下一轮若继续 A2,应只评估剩余低风险 apply / 生成主链重复编排,不新增 apply scope,不扩张 second guardrail 语义
- human review intent、人工复核结果写回与 second guardrail 预校验 / 决策记录已从命令层下沉到 `noname_apply.rs`
- `apply_noname_reviewed_output` 的 request input 组装已回收到 `noname_apply.rs`
- 当前 A2 在主开发线已阶段性完成;后续若继续 A2,应只评估剩余低风险 apply / 生成主链重复编排,不新增 apply scope,不扩张 second guardrail 语义

### 任务 A3:建立 `NoName` 主链变更的文档闭环

Expand All @@ -113,6 +114,12 @@
- 最小验证:
- 文档自查,无失效路径引用

当前状态:

- `NoName` 架构、现状评估、后续路线图已同步 reviewed apply 最新收口进度
- 两周排期文档已记录 A2 的第五轮下沉结果,后续 review 不再依赖口头补充
- 后续若继续推进主开发线,应维持“改主链实现就同步三份主文档和排期状态”的节奏

## 4.2 协作者线

### 任务 B1:记忆检索排序解释性增强
Expand Down
214 changes: 214 additions & 0 deletions src-tauri/src/noname_apply.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::noname_output_interface::NoNameControlledOutputDecision;
use crate::noname_trace::{NoNameHumanReviewDecision, NoNameSecondGuardrailDecision, NoNameTrace};
use crate::noname_types::{
NoNameApplyScope, NoNameMode, NoNameProposal, NoNameProposalStatus, NoNameTargetSegment,
Expand Down Expand Up @@ -171,6 +172,13 @@ fn next_apply_plan_order(trace: &NoNameTrace) -> u32 {
+ 1
}

fn find_controlled_output_review_index(trace: &NoNameTrace, request_id: &str) -> Option<usize> {
trace
.controlled_output_reviews
.iter()
.position(|review| review.request_id == request_id)
}

fn find_review_proposal<'a>(
trace: &'a NoNameTrace,
request_id: &str,
Expand Down Expand Up @@ -316,6 +324,45 @@ fn human_review_apply_intent_rejection_reason(
}
}

pub fn apply_human_review_decision_to_trace(
trace: &mut NoNameTrace,
request_id: &str,
decision: NoNameHumanReviewDecision,
reviewed_at: u64,
) -> Result<(), String> {
let review_index = find_controlled_output_review_index(trace, request_id)
.ok_or_else(|| format!("NoName controlled output review not found: {}", request_id))?;

let safe_apply_scope = {
let review = &trace.controlled_output_reviews[review_index];
if review.decision != NoNameControlledOutputDecision::NeedsReview
|| !review.requires_human_review
{
return Err(format!(
"NoName controlled output review does not require human review: {}",
request_id
));
}

review.safe_apply_scope.ok_or_else(|| {
format!(
"NoName controlled output review has no safe apply scope: {}",
request_id
)
})?
};

let review = &mut trace.controlled_output_reviews[review_index];
review.human_review_decision = Some(decision);
review.human_reviewed_at = Some(reviewed_at);
review.human_review_note = Some(decision.note().to_string());

trace.record_proposal_transition(format!("{}:human_review:{}", request_id, decision.as_str()));
record_human_review_apply_intent(trace, request_id, decision, Some(safe_apply_scope));

Ok(())
}

pub fn record_human_review_apply_intent(
trace: &mut NoNameTrace,
request_id: &str,
Expand Down Expand Up @@ -502,6 +549,32 @@ pub fn record_second_guardrail_decision(
Ok(())
}

pub fn resolve_second_guardrail_for_trace(
trace: &mut NoNameTrace,
request_id: &str,
decision: NoNameSecondGuardrailDecision,
) -> Result<(), String> {
let safe_apply_scope = trace
.controlled_output_reviews
.iter()
.find(|review| review.request_id == request_id)
.ok_or_else(|| format!("NoName controlled output review not found: {}", request_id))
.and_then(|review| {
if review.human_review_decision
!= Some(NoNameHumanReviewDecision::ApprovedForHigherApply)
{
return Err(format!(
"NoName controlled output review is not approved for second guardrail: {}",
request_id
));
}

Ok(review.safe_apply_scope)
})?;

record_second_guardrail_decision(trace, request_id, decision, safe_apply_scope)
}

fn trace_has_second_guardrail_allow(
trace: &NoNameTrace,
request_id: &str,
Expand Down Expand Up @@ -909,6 +982,15 @@ pub fn apply_reviewed_output_to_plot_state(
}
}

pub fn apply_reviewed_output_input_to_plot_state(
trace: NoNameTrace,
input: NoNameReviewedApplyRequestInput,
plot_state: PlotState,
) -> Result<NoNameReviewedApplyOutcome, String> {
let request = build_reviewed_apply_request(input)?;
apply_reviewed_output_to_plot_state(trace, request, plot_state)
}

pub fn apply_manual_plot_text_hint_to_plot_state(
trace: NoNameTrace,
request_id: &str,
Expand Down Expand Up @@ -1140,6 +1222,67 @@ mod tests {
.any(|entry| entry.ends_with(":apply_intent:awaiting_second_guardrail")));
}

#[test]
fn apply_human_review_decision_to_trace_updates_review_and_logs() {
let (mut trace, request_id) = make_trace_for_plot_text_review();

apply_human_review_decision_to_trace(
&mut trace,
&request_id,
NoNameHumanReviewDecision::ApprovedForHigherApply,
123,
)
.expect("human review decision should be applied to trace");

let review = &trace.controlled_output_reviews[0];
assert_eq!(
review.human_review_decision,
Some(NoNameHumanReviewDecision::ApprovedForHigherApply)
);
assert_eq!(review.human_reviewed_at, Some(123));
assert_eq!(
review.human_review_note.as_deref(),
Some(NoNameHumanReviewDecision::ApprovedForHigherApply.note())
);
assert!(trace.proposal_transition_log.iter().any(|entry| {
entry
== &format!(
"{}:human_review:{}",
request_id,
NoNameHumanReviewDecision::ApprovedForHigherApply.as_str()
)
}));
assert!(trace.apply_execution_log.iter().any(|entry| {
entry.target == "plot_text_hint" && entry.outcome == "awaiting_second_guardrail"
}));
}

#[test]
fn apply_human_review_decision_to_trace_rejects_missing_safe_scope_without_mutation() {
let (mut trace, request_id) = make_trace_for_plot_text_review();
trace.controlled_output_reviews[0].safe_apply_scope = None;

let error = apply_human_review_decision_to_trace(
&mut trace,
&request_id,
NoNameHumanReviewDecision::ApprovedForHigherApply,
456,
)
.expect_err("missing safe apply scope should fail");

assert_eq!(
error,
format!(
"NoName controlled output review has no safe apply scope: {}",
request_id
)
);
let review = &trace.controlled_output_reviews[0];
assert_eq!(review.human_review_decision, None);
assert_eq!(review.human_reviewed_at, None);
assert_eq!(review.human_review_note, None);
}

#[test]
fn human_review_apply_intent_rejects_non_second_guardrail_scope() {
let (mut trace, request_id) = make_trace_for_summary_apply();
Expand Down Expand Up @@ -1190,6 +1333,49 @@ mod tests {
);
}

#[test]
fn resolve_second_guardrail_for_trace_requires_review_approval() {
let (mut trace, request_id) = make_trace_for_plot_text_review();

let error = resolve_second_guardrail_for_trace(
&mut trace,
&request_id,
NoNameSecondGuardrailDecision::Allow,
)
.expect_err("second guardrail should require approved human review");

assert_eq!(
error,
format!(
"NoName controlled output review is not approved for second guardrail: {}",
request_id
)
);
}

#[test]
fn resolve_second_guardrail_for_trace_records_allow() {
let (mut trace, request_id) = make_trace_for_plot_text_review();
apply_human_review_decision_to_trace(
&mut trace,
&request_id,
NoNameHumanReviewDecision::ApprovedForHigherApply,
789,
)
.expect("human review should prepare second guardrail");

resolve_second_guardrail_for_trace(
&mut trace,
&request_id,
NoNameSecondGuardrailDecision::Allow,
)
.expect("second guardrail allow should be recorded");

assert!(trace.apply_execution_log.iter().any(|entry| {
entry.target == "plot_text_hint" && entry.outcome == "second_guardrail_allowed"
}));
}

#[test]
fn second_guardrail_allows_plot_augmentation_hint() {
let (mut trace, request_id) = make_trace_for_plot_augmentation_apply();
Expand Down Expand Up @@ -1283,6 +1469,34 @@ mod tests {
.any(|entry| entry.ends_with(":manual_apply:chapter_summary_hint")));
}

#[test]
fn reviewed_apply_input_can_write_chapter_summary_hint() {
let (trace, request_id) = make_trace_for_summary_apply();
let plot_state = make_plot_state("existing summary");

let outcome = apply_reviewed_output_input_to_plot_state(
trace,
NoNameReviewedApplyRequestInput {
request_id,
scope: NoNameApplyScope::ChapterSummaryHint,
chapter_index: Some(1),
segment_index: None,
expected_segment_text: None,
expected_summary: Some("existing summary".to_string()),
expected_generation_diagnostics: None,
expected_plot_augmentation_hints: None,
},
plot_state,
)
.expect("summary hint should be manually applied from request input");

assert!(outcome
.plot_state
.current_chapter
.summary
.starts_with("NoName summary hint: sect crisis; existing summary"));
}

#[test]
fn reviewed_apply_rejects_stale_chapter_summary_snapshot() {
let (trace, request_id) = make_trace_for_summary_apply();
Expand Down
Loading
Loading