fix: restore changes dropped by the upstream sync merge
The manual v0.3.6 sync (22132a4) was incomplete, and the follow-up merge (d803ae2) resolved conflicts in favour of that copy, silently reverting three shipped changes. main is already an ancestor of this branch, so re-merging cannot bring them back; restore them explicitly. - schema.sql: re-add the idempotent llm_profiles_format_check migration. CREATE TABLE IF NOT EXISTS leaves an existing table alone, so databases created before openai-responses kept the old two-value CHECK and rejected every openai-responses profile. - orchestration.go: re-add the reseedPlannerPrompt/reseedWorkerPrompt calls, dropped by a comment-alignment hunk while the functions stayed behind as dead code. Without them existing installs never receive the quantified acceptance check or the negative-conclusion evidence threshold. - CHANGELOG.md: restore the 0.3.6 entries, the @neouks credit and the correct norma version range. Also close three recovery gaps in the new cold-archive path: - traffic: write the staging journal even when no legacy host directory exists. Modern installs keep traffic in SQLite only, so a crash between the PostgreSQL commit and the SQLite commit left the archived task's exclusive traffic in the hot store with nothing for recovery to act on. - recoverTaskArchiveStages: treat a missing journal.json as a discardable directory instead of an error, which used to set recoveryFailed and stop the archive worker from starting for the rest of the process lifetime. - stageTaskArchiveFiles: replay the journal's renames when resuming an existing staging root. The journal is written before the first rename, so its presence did not guarantee a complete payload, and a resumed round could package an archive missing transcripts or workspace files.
This commit is contained in:
1 parent
0ec45bb45d
commit
21656cc2e3
5 files changed
+107
-13
No files matched your search
@@ -452,6 +452,8 @@ func (s *Server) seedOrchestrationTools() {
|
||||
s.unbindGoalMetDefault()
|
||||
s.reseedGoalsPrompt() // goals 提示词加入「抽操作约束」步 → 旧库追加一版新默认(一次性)
|
||||
s.reseedMainAgentPrompt() // mainagent 提示词加入「目标达成后 add_intent 反问是否建目标」(一次性)
|
||||
s.reseedPlannerPrompt() // planner 提示词:重写「0 意图」正当理由 + 加量化验收核对(一次性)
|
||||
s.reseedWorkerPrompt() // worker 提示词:加否定结论证据门槛(一次性)
|
||||
s.seedReporterAgent() // 预置「报告撰写」agent + 工具绑定 + finding 触发器(一次性)
|
||||
// 注:pentest 的默认工具绑定无需迁移——BuiltinToolSeeds 在全新初始化时就把
|
||||
// list_assets/insert_assets/report_finding/list_findings/list_companies 连同
|
||||
|
||||
@@ -96,6 +96,17 @@ func stageTaskArchiveFiles(dataDir string, archiveID int64, taskID string, explo
|
||||
if err := json.Unmarshal(raw, &stage.journal); err != nil {
|
||||
return nil, fmt.Errorf("read task archive staging journal: %w", err)
|
||||
}
|
||||
// The journal is written before the first rename, so its presence does not mean
|
||||
// the payload is complete: a previous round may have failed mid-loop with a
|
||||
// rollback that itself errored, which leaves the root in place. Replay the moves
|
||||
// rather than packaging a payload that is missing transcripts or workspace files.
|
||||
if err := os.MkdirAll(filepath.Join(stage.payload, "files"), archiveDirMode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := stage.applyMoves(); err != nil {
|
||||
_ = stage.rollback()
|
||||
return nil, err
|
||||
}
|
||||
return stage, nil
|
||||
} else if !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
@@ -133,20 +144,42 @@ func stageTaskArchiveFiles(dataDir string, archiveID int64, taskID string, explo
|
||||
_ = stage.rollback()
|
||||
return nil, err
|
||||
}
|
||||
for _, move := range targets {
|
||||
destination := filepath.Join(stage.payload, move.Relative)
|
||||
if err := os.MkdirAll(filepath.Dir(destination), archiveDirMode); err != nil {
|
||||
_ = stage.rollback()
|
||||
return nil, err
|
||||
}
|
||||
if err := os.Rename(move.Source, destination); err != nil {
|
||||
_ = stage.rollback()
|
||||
return nil, fmt.Errorf("stage task archive path %s: %w", move.Source, err)
|
||||
}
|
||||
if err := stage.applyMoves(); err != nil {
|
||||
_ = stage.rollback()
|
||||
return nil, err
|
||||
}
|
||||
return stage, nil
|
||||
}
|
||||
|
||||
// applyMoves performs the renames recorded in the journal. Entries already sitting
|
||||
// in the payload are skipped, so an interrupted staging round can be resumed in
|
||||
// place without moving anything twice.
|
||||
func (s *taskArchiveFileStage) applyMoves() error {
|
||||
for _, move := range s.journal.Moves {
|
||||
destination := filepath.Join(s.payload, move.Relative)
|
||||
if _, err := os.Lstat(destination); err == nil {
|
||||
continue
|
||||
} else if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
if _, err := os.Lstat(move.Source); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// Neither side exists: the path was removed outside the archive flow
|
||||
// after the journal was written. Nothing can be staged for it.
|
||||
continue
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(destination), archiveDirMode); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Rename(move.Source, destination); err != nil {
|
||||
return fmt.Errorf("stage task archive path %s: %w", move.Source, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *taskArchiveFileStage) rollback() error {
|
||||
if s == nil || s.done {
|
||||
return nil
|
||||
@@ -368,6 +401,18 @@ func recoverTaskArchiveStages(dataDir string, pg *pgdb.DB) error {
|
||||
}
|
||||
root := filepath.Join(parent, entry.Name())
|
||||
raw, err := os.ReadFile(filepath.Join(root, "journal.json"))
|
||||
if os.IsNotExist(err) {
|
||||
// Either staging died between creating the directory tree and writing the
|
||||
// journal — no rename had run, so payload holds nothing — or commit/rollback
|
||||
// failed part-way through removing the root, in which case payload only holds
|
||||
// copies already inside the package. Without a journal there is nothing to
|
||||
// roll back, and reporting an error here keeps the archive worker from ever
|
||||
// starting, so discard the directory instead.
|
||||
if err := os.RemoveAll(root); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
|
||||
Reference in new issue
Block a user