Skip to content

Nightly: refuse curated inputs changed before the run; policy v2 - #83

Merged
Deicyde merged 2 commits into
mainfrom
claude/nightly-curated-inputs
Oct 18, 2026
Merged

Deicyde merged 2 commits into
mainfrom
claude/nightly-curated-inputs

Conversation

@Deicyde

@Deicyde Deicyde commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Hold until the release being built now is promoted. site/ops/brain-nightly.sh is release-affecting. If this merges before that promotion, main changes a release-affecting path after that release's authority commit, and the promoter will refuse it. Merge after the promotion, or build the release after this merges.

Problem

brain-nightly.sh builds the Brain from its working tree. Its uncommitted-source check (section 4) names code only. It never looks at the reducer's curated inputs: bot/state/*.json, catalog/data/*, brain/data/*, all curated_git_input in brain/authority/reducer-inputs-v1.json. So two kinds of local state were frozen into a release whose Git authority lacked those bytes:

  • a local edit on the nightly host, such as python3 bot/brain_queue.py, which bot/README.md documents;
  • an ignored file the reducer reads: bot/state/pool_candidates.json is gitignored, and brain/build_cells.py reads it when present.

Change

  • A curated-input check in section 4, right after the uncommitted-source check.
    • It reads the inventory's curated_git_input paths and runs git status --ignored=matching --untracked-files=all --porcelain on them. It reports tracked inputs that differ from HEAD ( M, D, staged) and ignored or untracked ones that exist (!!, ??).
    • If any are found, the release aborts with curated reducer inputs differ from HEAD; the Git authority would be false and the paths.
    • An invalid inventory or a failed git status aborts with its own message, the same way the source check fails closed.
    • --untracked-files=all keeps it working when the host repo sets status.showUntrackedFiles=no.
  • The fold's outputs are exempt.
    • brain/fold_proposals.py rewrites these curated inputs on every run: brain/data/{container_links,discovery_proposals,discovery_rejected,ext_anchor_links,fc_links}.jsonl and catalog/data/<key>_links.jsonl (today tauceti_links.jsonl), and it appends to catalog/data/{grounding_overrides,universe_extension}.jsonl.
    • The nightly never commits or reverts those writes, so any check over them, before or after the fold, would block every publish once the fold's output drifts from HEAD.
    • Nothing in the run writes the other ten curated inputs (the five bot/state files, source_registry, rebuild_grounding, decl_renames, community_edges, frontier_suitability_overrides), so checking them at release time also catches edits made during the run.
  • The uncommitted-source check no longer names site/ops/brain-canary.py. The nightly hasn't run it since 81c33d4d; the promoter and .github/workflows/brain-canary.yml do.
  • POLICY is now wikilean.release-neutral-paths/v2, for the rule changes in Release policy: AGENTS.md and CONTRIBUTING.md are release-neutral #68 (AGENTS.md and CONTRIBUTING.md neutral) and site/ops: the reducer's bot/state inputs are release-affecting #75 (the bot/state inputs release-affecting). The ID isn't persisted: the promoter keeps it in memory and records only neutral_changes.
  • site/ops/README.md says what the gate refuses and how to clear it.

Operator notes

  • First nightly after merge: a host with a stray bot/state/pool_candidates.json, or uncommitted edits to the checked curated inputs, stops publishing shadow releases until those are committed, reverted or removed. The log lists the paths. The operator checkout ran the laptop poller until 2026-08-23, which wrote pool_candidates.json, so check for it.
  • Community harvest: with WIKILEAN_COMMUNITY_HARVEST=1 (default 0), nightly-moderate.sh rewrites brain/data/community_edges.jsonl. That now blocks the next Brain publish until the harvest output is committed.

Not changed, and worth a decision

The fold's own output still reaches the release uncommitted. The release records those bytes (the legacy declared-input root hashes the working tree), but its authority commit doesn't hold them. Hand edits to those seven fold-written files are also not flagged. Whether fold output must be committed before a release is frozen is left open.

Tests (site/ops/test_brain_nightly.py)

  • test_changed_curated_inputs_block_the_release_except_fold_outputs extracts the check from the script, runs it under bash against a temporary git repo, and checks that the check sits after the fold and before the abort elif. Cases:
    • a clean tree: publishes;
    • changed non-curated and fold-written files: publishes;
    • an edited tracked input plus a present ignored input: M … and !! …;
    • the same with status.showUntrackedFiles=no;
    • an invalid inventory: fails closed.
  • test_curated_check_exempts_exactly_the_fold_outputs pins the exemption list to the curated .jsonl files fold_proposals.py names, plus the <key>_links pattern.
  • The cleanliness test asserts the script doesn't name brain-canary.py.
  • In site/ops/test_brain_release_authority.py, the canary test becomes test_every_file_the_nightly_names_is_release_affecting.

I didn't run these locally because machine load was above the agreed limit; CI runs them. bash -n passes under bash and /bin/bash 3.2, and the exemption pin was checked by hand against the current files.

This PR's commits sit directly on main (43d3ed74).

brain-nightly.sh builds the Brain from its working tree, and its
uncommitted-source check named only code. A local edit to a curated
reducer input (bot/state/*.json, catalog/data/*, brain/data/*) or an
ignored file the reducer reads (bot/state/pool_candidates.json) was frozen
into a release whose Git authority lacked those bytes.

A new section 0 records, before ingest and the proposal fold, every
curated_git_input in reducer-inputs-v1.json that differs from HEAD or
exists while ignored (git status --ignored=matching); section 4 then
refuses to publish and lists them. The check runs before the run because
fold_proposals.py itself writes several curated inputs; those writes are
not flagged.

The source check also stops naming site/ops/brain-canary.py, which the
nightly has not run since afdf3a6; the promoter runs it. The policy test
now expects no neutral file among the nightly's explicit names.

POLICY becomes wikilean.release-neutral-paths/v2, for the rule changes in
#68 and #75. The ID is not persisted: the promoter keeps it in memory and
records only neutral_changes.
Review fixes for the curated-input gate:
- brain/fold_proposals.py rewrites several curated inputs on every run, and
  the nightly never commits or reverts them, so a check at the start of the
  next run flagged the previous night's fold output and would block every
  publish once the fold drifted from HEAD. The check moves to section 4,
  next to the uncommitted-source check, and exempts the fold's outputs
  (FOLD_OUTPUTS plus catalog/data/*_links.jsonl). Nothing in the run writes
  the remaining curated inputs, so checking late also catches edits made
  during the run.
- git status gets --untracked-files=all: with status.showUntrackedFiles=no
  in the repository config, --ignored=matching alone fails.
- Failures abort with their own messages, as the source check does.
- A test pins FOLD_OUTPUTS to the curated .jsonl files fold_proposals.py
  names; the behavioural test covers fold outputs, showUntrackedFiles=no
  and the fail-closed path.
- The canary removal is cited as 81c33d4, its SHA on the current history.
- site/ops/README.md says what the gate refuses and how to clear it.
@Deicyde

Deicyde commented Oct 18, 2026

Copy link
Copy Markdown
Owner Author

Recommendation: merge, but only after the release being built now is promoted (brain-nightly.sh is release-affecting). An independent adversarial review of the first head (fb4c3907) found one design flaw and four smaller issues. All are fixed in 17d91e73, and CI is green (run 39082095320: 25 nightly tests, 10 policy tests).

  • Fixed, the design flaw:
    • The first version checked curated inputs at the start of the run. fold_proposals.py rewrites seven curated files plus <key>_links.jsonl every night, and the nightly never commits or reverts them. So the next run would have flagged the previous night's fold output and blocked every publish once the fold drifted from HEAD.
    • The check now runs in section 4 and exempts the fold's outputs. A test pins the exemption list to the files fold_proposals.py writes.
  • Fixed, smaller:
    • --untracked-files=all, so status.showUntrackedFiles=no can't fail every publish.
    • Separate abort messages for an invalid inventory and a git status failure.
    • fc_links.jsonl added to the body's list of fold writes.
    • The canary-removal SHA updated to 81c33d4d for the current history.
    • The operator note moved into site/ops/README.md.
  • Verified by the review:
    • Control flow under set -uo pipefail and /bin/bash 3.2.
    • Git semantics for M, D, staged, ?? and !!.
    • Nothing before the check writes the checked inputs.
    • The existing gate tests are unaffected.
    • _nightly_watched() still parses the right command.
    • POLICY is used only in memory.
    • No conflicts with the other open PRs.
  • Still open, as decisions rather than fixes:
    • Fold output: it reaches releases uncommitted, and hand edits to those seven files aren't flagged.
    • Community harvest: with WIKILEAN_COMMUNITY_HARVEST=1, its rewrite of community_edges.jsonl blocks the next publish until committed.

@Deicyde
Deicyde merged commit eaa5950 into main Oct 18, 2026
4 checks passed
@Deicyde

Deicyde commented Oct 18, 2026

Copy link
Copy Markdown
Owner Author

Re-review on main (eaa5950b): follow-up needed (docs and one tightening). The gate works; no revert.

Verified:

Release B: whoever builds or promotes it should read this.

Follow-ups:

  • Operator docs: they don't say that two documented actions now block Brain publishes until their output is committed:
    • WIKILEAN_COMMUNITY_HARVEST=1 (site/ops/README.md:77-81, site/ops/nightly.local.env.example:16-20) rewrites community_edges.jsonl;
    • running bot/brain_queue.py in the nightly checkout (bot/README.md:76-77) rewrites brain_queue.json.
  • Tightening: fnmatchcase("catalog/data/*_links.jsonl") lets * cross /. It should match only catalog/data/<key>_links.jsonl, and the fold writes just the frontier_sources keys. Harmless today, since tauceti_links.jsonl is the only curated match.

Your call:

  • Append-only files: the fold only appends to grounding_overrides.jsonl and universe_extension.jsonl. A "HEAD's bytes are a prefix of the working file" check would still catch hand edits to those two.
  • First failure only: when both source and curated inputs are dirty, the log names only the source failure.
  • Local commits: "commit" in the abort message can produce a local commit that isn't on main, which the promoter then refuses. The source check has the same issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant