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:
Autumn-27 committed 2026-08-29 01:37:46 -04:00
1 parent 0ec45bb45d
commit 21656cc2e3
5 files changed
+107 -13

No files matched your search

+7 -2
View File
@@ -911,7 +911,12 @@ func (t *Traffic) stageTreesForArchive(dirs, hosts []string, archiveID, taskID i
}
planned = append(planned, stagedTrafficPath{source: source})
}
if len(planned) == 0 {
// 归档路径即使一个历史 host 目录都没有(新装机的流量只落在 SQLite + _blobs)
// 也必须留下 journal:崩溃点若落在 PostgreSQL 提交与 SQLite 提交之间,重启后
// SQLite 事务被回滚,只有这份 journal 能让恢复流程补做 host 行的删除。少了它,
// 已转冷任务的独占流量会永久留在热库里。
uniqueHosts := uniqueArchiveHosts(hosts)
if len(planned) == 0 && len(uniqueHosts) == 0 {
return "", nil, nil
}
parent := filepath.Join(t.dir, "_delete_staging")
@@ -921,7 +926,7 @@ func (t *Traffic) stageTreesForArchive(dirs, hosts []string, archiveID, taskID i
if stageDir, err = os.MkdirTemp(parent, "hosts-"); err != nil {
return "", nil, fmt.Errorf("创建流量暂存目录: %w", err)
}
journal := hostDeleteStageJournal{Version: 1, ArchiveID: archiveID, TaskID: taskID, Hosts: uniqueArchiveHosts(hosts)}
journal := hostDeleteStageJournal{Version: 1, ArchiveID: archiveID, TaskID: taskID, Hosts: uniqueHosts}
for i := range planned {
planned[i].staged = filepath.Join(stageDir, fmt.Sprintf("%d-%s", i, filepath.Base(planned[i].source)))
journal.Moves = append(journal.Moves, hostDeleteStageMove{Source: planned[i].source, Staged: planned[i].staged})