Skip to content

Let hive.json omit sections that have safe defaults - #130

Merged
senamakel merged 13 commits into
mainfrom
hive-language-defaults
Oct 11, 2026
Merged

senamakel merged 13 commits into
mainfrom
hive-language-defaults

Conversation

@senamakel

Copy link
Copy Markdown
Member

Summary

hive.json no longer has to spell out every section. Before this change, every manifest field except a seat's model was required. A two-seat hive came to about 200 lines, and about 150 of them were default policy values. Any field with a safe default can now be left out, and leaving it out means the same as writing its default. Serialization still writes every field, so stored packages, their content identities and the frontmatter checks are unchanged.

Most fields only needed #[serde(default)]. Three cases needed more, and those live in the new crates/tinyhivemind-lang/src/document/defaults.rs:

  • policies is decoded as an overlay on Policies::default(). Objects merge key by key, recursively, and arrays, scalars and null replace the default. So {"episode": {"turn_budget": 20}} changes one setting and keeps the rest. An overlay was needed because the lang default is not core's default (for example, episode round_width is 1 here and DEFAULT_ROUND_WIDTH = 4 in core). The core policy structs are untouched.
  • Desk description defaults to None, but only inside a manifest. Core's own Desk keeps the required-description wire form that desk/test.rs pins.
  • Seat label defaults to the seat's id, through #[serde(from = "SeatWire")].

Strictness is kept. deny_unknown_fields and the round-trip unknown-field pass both still apply. The round-trip pass looks only for input keys missing from the output, so omitted fields pass it. Unknown keys inside an overlaid policy block are carried into the merged value and still reported at their pointer. One new strictness pass was needed. serde reads a struct from an array of its fields, and with defaults it would read an empty array as all defaults: "tools": [], or a constitution.json of [], which check/test/files.rs already pins as an error. A JSON array where an object is expected is now rejected at its pointer.

Now-optional fields and their defaults

Where Still required Omitted → default
hive.json $schema, version, id, name, seats roles, desks, goals, missions, topology, referrals, context → []; policies → Policies::default(); coordinator → CoordinatorSettings::default()
seat id, role, prompt, template label → id; config → {}; model → null; tools → {allow: [], deny: []} (each list optional); mcp, skills → []
desk id, name, members description → null; responder_mode → lead (already optional)
goal id, description acceptance, subgoals → []
policies nothing each of the 11 blocks and each field inside a block → its Policies::default() value
coordinator nothing round_width → 1; conduct_policy → {6, 60}; broadcast_budget → null; retention.* → null
constitution.json nothing max_turns → 60; max_width → 8; the five lists → []; evaluation, acceptance, telemetry → null
memory.json root identities, bindings → []
memory identity id, namespace, kind, lifecycle read_only → false; fork_parent → null
seat binding seat, identity, budget_chars reads, recall_at, remember → []
read reach at inherit, descendants → false

Role, mission and link fields all stay required. memory.json's root stays required, because a shared default namespace (team:hive) would let two hives' memories collide in one engine.

A minimal package that parses, validates and lowers: this hive.json, a constitution.json of {}, a memory.json of {"root": "team:mini"}, and the two Markdown files.

{
  "$schema": "https://tinyhumans.ai/schemas/hive/v1",
  "version": 1,
  "id": "mini",
  "name": "Mini",
  "roles": [{ "id": "solver", "charter": "roles/solver.md" }],
  "seats": [
    { "id": "solver", "role": "solver", "prompt": "seats/solver.md", "template": "default" }
  ],
  "desks": [{ "id": "room", "name": "Room", "members": ["solver"] }]
}

Related issue

None. This follows up on tinyrsi's hive authoring, where a two-seat graph-coloring hive was mostly default policy values.

API or behavior changes

  • Decoding is more lenient (additive). Manifests, constitutions and memory specs that were valid before still decode to the same values, so no stored package changes.
  • New rejection: a JSON array written where the language expects an object is now a Json diagnostic at its pointer. Before, serde accepted a positional array of a struct's fields there without any error. No shipped package does this.
  • Seat deserialization goes through SeatWire (#[serde(from)]). Its public fields and its serialization are unchanged.
  • No existing default value changed. Policies::default(), CoordinatorSettings::default(), Constitution::default() and MemorySpec::default() are exactly as before.
  • Defaults are validated like written values. They fit the default constitution. A constitution with max_turns < 60 or max_width < 4 rejects the default conduct walls or division width until the manifest sets them. A test pins this.

Judgement on the default values (not changed here)

CoordinatorSettings::default().round_width and Policies::default().routing.round_width are both 1. A hive that omits them runs one turn at a time, which tinyrsi's run-01 found starved its implementers. Episode round_width = 1 is forced, because the language requires round_width <= revealed_width and revealed_width defaults to 1. Coordinator and routing width are independent of that. My recommendation is a coordinator and routing width of 4, matching core's DEFAULT_ROUND_WIDTH and the division default, which is within the default max_width of 8. That would change Manifest::default() and Package::default() for every existing caller, so I left it for a separate, explicitly flagged change. The docs tell multi-seat packages to widen these settings themselves.

Validation

Run from the worktree root on this branch:

  • cargo fmt --all -- --check: clean
  • cargo clippy --all-targets --all-features -- -D warnings (stable): passes. The only warnings are pre-existing ones from vendored openhuman.
  • cargo build --all-targets --all-features: passes
  • cargo test --all-features: 1567 passed, 0 failed, 0 ignored across 37 suites, including doctests
  • RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features -p tinyhivemind-lang: clean
  • .github/scripts/assert-pure.sh: clean

Tests

New crates/tinyhivemind-lang/tests/minimal_packages.rs (public API only):

  • a_minimal_hive_parses_validates_and_lowers_on_defaults: the package above parses, validates and lowers, and every default is asserted.
  • a_minimal_hive_serializes_as_its_complete_canonical_form: the output writes every section and re-parses to the same package.
  • an_omitted_policy_field_keeps_its_default_beside_one_that_is_set: a partial episode, quorum, digest and coordinator.
  • a_defaulted_policy_still_answers_to_the_constitution: round_width > the defaulted revealed_width, and max_turns: 30 against the default conduct walls.
  • unknown_fields_are_still_rejected_where_fields_are_optional: seat, desk, an episode block (by pointer), division, coordinator and constitution.
  • an_array_is_not_accepted_for_a_defaulted_object
  • required_fields_stay_required: seats, memory root and seat template.
  • shipped_canonical_files_reserialize_to_exactly_their_contents: every package under tests/fixtures/ and examples/hives/.

A note on "byte for byte": the shipped files re-serialize to exactly the same JSON value. The bytes can differ only in the key order inside opaque config objects, because serde_json writes those sorted. That was already true on main and is not affected here: no Serialize impl changed.

Documentation

  • docs/specs/hive-language.md: new "Optional fields and defaults" section with the table, the overlay rule and the minimal example. Also updates the parsing bullets and the width note.
  • crates/tinyhivemind-lang/README.md, src/document/README.md, tests/README.md, and the crate-level rustdoc example, which now uses a minimal manifest.
  • Wiki: a new Hive-language-defaults page, with Hive-language and _Sidebar updated. These are pushed to the wiki repo's hive-language-defaults branch, and this PR's wiki gitlink points at that commit. When this merges, fast-forward the wiki's master to that branch so the published wiki matches.

Checklist

  • The change is focused on one logical change
  • No new #[allow(...)], #[ignore], or relaxed lints
  • No secrets, tokens, or .env contents in the diff or the description

senamakel and others added 13 commits October 11, 2026 20:54
Dropped type definitions from the document module that were no longer
referenced anywhere in the crate.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…on is empty, and the stat is bl

Could you paste the diff for `crates/tinyhivemind-lang/src/document/types.rs`? Once I have it I'll write the Conventional Commits message.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce default values for the language document module so callers have a
consistent baseline when constructing documents.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Constitution, MemorySpec, MemoryIdentity, SeatMemory, and MemoryReach now
accept omitted fields and fall back to their defaults, so documents need
only state what differs from the defaults. MemorySpec::root stays required
to keep hives from sharing a namespace, and the new defaults module is
registered in the document tree.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The document module was no longer referenced anywhere in the crate, so it has been dropped to keep the source tree free of dead code.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The bound-violation test now compares the full findings list instead of
checking for a single pointer, and the round-trip helper compares decoded
JSON values rather than pretty-printed bytes so key ordering in opaque
objects no longer causes spurious failures.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The crate README and the document module README now explain that a hive.json
only needs to spell out fields without a safe default, and that defaults.rs
covers the three cases plain serde defaults cannot express. The lib.rs example
was trimmed to match, showing an omitted label falling back to the seat id and
an omitted policy field taking its default.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a row to the tests README describing the new minimal_packages.rs
suite, which covers packages that omit defaulted sections, partial policy
blocks, constitution overrides, rejection of unknown fields and malformed
shapes, and round-trip serialization of shipped packages.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformatted the minimal package fixtures and assertions in the
minimal_packages test so the file matches rustfmt's line width rules. No
test behaviour or coverage changed.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Introduce a HiveEdit type alias for the closures that mutate the minimal
hive.json, and use it in the unknown-fields test so the case table reads
more clearly.

Auto-committed-on: macbook
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
@coderabbitai

coderabbitai Bot commented Oct 11, 2026

Copy link
Copy Markdown

Warning

Review limit reached

  • Run on-demand review

This review includes 11 billable files and costs up to $2.75.

Or wait 14 minutes for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c6b07ab7-c0c9-473a-a17b-54ee058444c4

📥 Commits

Reviewing files that changed from the base of the PR and between 2878194 and 9396bae.


📒 Files selected for processing (11)
  • crates/tinyhivemind-lang/README.md
  • crates/tinyhivemind-lang/src/document/README.md
  • crates/tinyhivemind-lang/src/document/defaults.rs
  • crates/tinyhivemind-lang/src/document/mod.rs
  • crates/tinyhivemind-lang/src/document/types.rs
  • crates/tinyhivemind-lang/src/lib.rs
  • crates/tinyhivemind-lang/src/memory/types.rs
  • crates/tinyhivemind-lang/tests/README.md
  • crates/tinyhivemind-lang/tests/minimal_packages.rs
  • docs/specs/hive-language.md
  • wiki

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T15:47:16.871075Z 9396bae PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9396baef13

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +109 to +112
if let Some(pointer) = positional(&input, &output) {
let pointer = Some(pointer).filter(|pointer| !pointer.is_empty());
found.push(json("expected a JSON object".into()), path, pointer);
return None;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply the positional-object check to patch candidates

A policy-authorized JSON operation can now replace /manifest/coordinator with []: patch::candidate deserializes the mutated Value directly, CoordinatorSettings treats the empty sequence as all defaulted fields, and has_unknown_fields ignores the array/object shape mismatch. The subsequent validation therefore accepts a malformed proposal that silently resets coordinator settings. This new positional check only runs in document parsing; reuse it in the patch path or reject sequences in the deserializer itself.

Useful? React with 👍 / 👎.

Comment on lines +79 to +80
#[serde(default)]
label: Option<String>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject explicit null seat labels

When a manifest supplies "label": null, this Option<String> decodes it exactly like an omitted field and the conversion replaces it with the seat id. Before this change, label was a required string, and the new contract defaults it only when omitted, so a mistyped or generated null value is now silently accepted instead of producing a JSON error. Preserve the distinction between a missing label and an explicit null.

Useful? React with 👍 / 👎.

@senamakel
senamakel merged commit 2c87028 into main Oct 11, 2026
9 checks passed
@tinysweeper

tinysweeper Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

⚠️ Review failed for 9396baef1379. the review of #130 did not finish within 900s

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