Skip to content

catalog: the declaration/state split misses two depth-1 state-plane names (.harness-state.tmp-, driver-diagnostic) #512

Description

@schickling-assistant

Found while counting publication helpers for #508 (fsatomic). Pre-existing on main before that PR and unchanged by it — filed rather than fixed inside a refactor.

What is verified

catalog_transaction::collect_bundle_files decides whether an agent-directory child is state plane (skip) or declaration plane (hash into the bundle) with an exact-name list plus exactly one prefix:

let state = matches!(
    name_text,
    "resources" | "archive" | "inbox" | "status"
        | "harness-context" | "harness-state"
        | ".harness-state.seq" | ".harness-state.lock" | ".harness-context.lock"
) || name_text.starts_with(crate::status::TMP_STAGING_PREFIX);

(src/catalog_transaction.rs:2017-2028, applied only at relative_to_bundle.components().count() == 1.) Anything not matched falls through to add_regular — i.e. into the declaration bundle whose hash is rootSha256 — or, for a Prepared source, to the "prepared catalog contains state-plane path" bail.

is_writer_temporary does not cover these: its WRITER_TEMP_PREFIXES are the three declaration-plane writer temps (.agent.kdl.presentation-, .agent.kdl.publish-, .catalog-apply-file-, src/catalog_transaction.rs:31-35).

So .status.tmp-<pid>-<counter> is deliberately special-cased, and these depth-1 names are not:

  1. .harness-state.tmp-<pid>-<counter> — the harness-state record stages beside itself in the agent directory (src/harness_state.rs, write_json_atomic(path, record, &dir, ".harness-state")). Every live seat rewrites that record on transitions plus a refresh cadence, so this window is real rather than theoretical.
  2. driver-diagnostic and its .driver-diagnostic.tmp-<pid>-<counter> staging — driver_diagnostic::path is agent_dir.join("driver-diagnostic") (src/driver_diagnostic.rs:399-401), depth 1, and the record itself is absent from the list, not just its staging file.

Not affected, for the record: .message.tmp- (inside inbox/archive/resources, all skipped wholesale), .ctx.tmp- (context_dir is <agent>/resources/context), .harness-context.tmp- (stages in the catalog control plane, outside agents, by HC-R05).

The intent is documented by tests/catalog_apply.rs::harness_runtime_records_never_change_the_declaration_snapshot, which asserts rootSha256 is unchanged for exactly the five names in the list — so the list is the contract, and the two above are outside it.

What needs the owner's read

The consequence per case, which is why this is a report and not a patch:

  • For Current sources: does a staged .harness-state.tmp-… or a published driver-diagnostic actually change rootSha256, making the declaration digest depend on runtime timing? That is what the code path suggests, and it would make catalog digest/catalog snapshot nondeterministic under load.
  • For Prepared sources: the same names would bail! instead.
  • Is driver-diagnostic's absence deliberate (a record intended to travel with the declaration) or an oversight from when the record was added?

A fix has an obvious shape — have the state-plane test enumerate every record and staging prefix the state plane publishes, and have the walker consult the writers' own prefix constants (status::TMP_STAGING_PREFIX is now such a constant after #508, and message has the same shape) rather than a hand-maintained literal list. But which names belong in the state plane is a decision for whoever owns the declaration/state split, not a refactor's judgement call.

Posted on behalf of @schickling
field value
agent_identity dev3.direct.omp.43sz6ujq
session dev3.43sz6ujq
agent_persona generalist
agent_supervisor unavailable
agent_tool OMP
agent_tool_version 18.1.7
agent_runtime OMP 18.1.7
tooling_profile dotfiles@39a19af

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:catalogCatalog structure, declarations, transactions, and admission · Set: manualorigin:agentFiled or primarily produced by an AI agent · Set: manualtype:bugSomething broken or a regression · Set: manual

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions