Writers that created or rewrote brain files by path followed symlinks: - promote/reject: os.MkdirAll + os.WriteFile of the destination, then os.Remove of the raw source. Now PutRegular + brainfs.Remove; the source is kept when the write is refused. - StampProvenanceFile (brain_edit's re-stamp): provenance.ApplyFile read and rewrote by path. Now ReadRegular + WriteRegular; ApplyFile, now unused, is removed. - BuildWingIndex (_index.md) and wiki.RebuildIndex (wiki/index.md): now PutRegular. - wiki.AppendLog (log.md) and AutoTunnel's fuzzy-candidate log (raw/tunnel-candidates-*.md): now AppendRegular; tunnel's fileExists, now unused, is removed. - New brainfs.Remove: clean path, real parent, removes a final symlink itself, never its target. brain_edit's own atomic rename was already refused behind a symlinked directory by #187's read check; a guard test pins it. Pipeline writes, BackfillRefs, the watcher's raw/ markers and the session archive in cmd/server follow in a separate PR (#158). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -127,18 +127,16 @@ func PromoteNote(brainDir string, opts PromoteOptions) (string, error) {
|
||||
}
|
||||
fm.set("promoted_at", now)
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
|
||||
return "", fmt.Errorf("create wing dir: %w", err)
|
||||
}
|
||||
// Write-then-delete: the source survives any write failure.
|
||||
// Write-then-delete: the source survives any write failure. Both go
|
||||
// through the brain root (hyperguild#188).
|
||||
promoted, err := StampProvenance(fm.render()+body, opts.Origin, opts.WrittenBy)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.WriteFile(dest, []byte(promoted), 0o644); err != nil {
|
||||
if _, err := putBrainFile(brainDir, dest, []byte(promoted)); err != nil {
|
||||
return "", fmt.Errorf("write promoted note: %w", err)
|
||||
}
|
||||
if err := os.Remove(src); err != nil {
|
||||
if err := removeBrainFile(brainDir, src); err != nil {
|
||||
return "", fmt.Errorf("promoted note written but source removal failed: %w", err)
|
||||
}
|
||||
|
||||
@@ -196,14 +194,12 @@ func RejectNote(brainDir string, opts RejectOptions) (string, error) {
|
||||
fm.set("reject_reason", opts.Reason)
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil {
|
||||
return "", fmt.Errorf("create rejected dir: %w", err)
|
||||
}
|
||||
// Write-then-delete: the source survives any write failure.
|
||||
if err := os.WriteFile(dest, []byte(fm.render()+body), 0o644); err != nil {
|
||||
// Write-then-delete: the source survives any write failure. Both go
|
||||
// through the brain root (hyperguild#188).
|
||||
if _, err := putBrainFile(brainDir, dest, []byte(fm.render()+body)); err != nil {
|
||||
return "", fmt.Errorf("write rejected note: %w", err)
|
||||
}
|
||||
if err := os.Remove(src); err != nil {
|
||||
if err := removeBrainFile(brainDir, src); err != nil {
|
||||
return "", fmt.Errorf("rejected note written but source removal failed: %w", err)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user