Skip to content

fix(keys): the per-arm predictions artifact leaves the trader's prefix (alpha-engine-config-I9822) - #207

Merged
cipher813 merged 2 commits into
mainfrom
fix/arm-predictions-prefix-collision-i9822
Sep 11, 2026
Merged

fix(keys): the per-arm predictions artifact leaves the trader's prefix (alpha-engine-config-I9822)#207
cipher813 merged 2 commits into
mainfrom
fix/arm-predictions-prefix-collision-i9822

Conversation

@cipher813

Copy link
Copy Markdown
Contributor

Closes the open half of alpha-engine-config-I9822.

What was wrong

predictions/ held two artifact shapes:

Shape What it is
predictions/{trading_day}.json the trader's champion serving feed — the contract named in crucible/slots/__init__.py and in this repo's AGENTS.md
predictions/{arm~seg}/{trading_day}.json the per-arm artifact

A consumer doing store.list_keys("predictions/") saw both and had to discriminate by path depth. Nothing did — which is what kept this a design risk rather than a live defect, and also why it would have been found the hard way.

What changed

  • The per-arm shape moves to arm_predictions/, behind a named ARM_PREDICTIONS_PREFIX.
  • predictions_key(trading_day) is declared, so the trader contract has one source for its key instead of living only in prose. The key builder only — writing that artifact is the M slot's job and stays on -I10129. It is declared here because arm_predictions_key cannot be tested against a collision with a key that does not exist.

Moved while the prefix was EMPTY, and that is the point

Measured 2026-09-11: zero objects under predictions/ in the production store.

aws s3 ls --recursive s3://alpha-engine-crucible-v2/crucible/predictions/   ->  0

So this rename orphans nothing. The same change made after either shape had been written would have stranded those objects at an address no code resolves — which is -I10498, filed the same day for a feature layer stranded exactly that way by a content-hash move (532 sessions at vf795db2b5049, unreachable, while live code resolves v6df3c0a27b70 holding 7). The cheapest moment to separate two artifact shapes is before either exists.

Deliverable 1 was already done

The issue's first deliverable — move arm_predictions_key out of slots/inputs.py into keys.py — is already true on main: it is at crucible/keys.py:317 and slots/inputs.py:102 imports it from there. Only deliverable 2 remained. Noted so the issue's closing comment does not claim work that a previous change did.

The guard is proven red, not assumed

Reverting ARM_PREDICTIONS_PREFIX to "predictions/" and re-running:

FAILED TestPredictionsPrefixesCannotCollide::test_the_two_shapes_live_under_different_prefixes
FAILED TestPredictionsPrefixesCannotCollide::test_a_listing_of_the_feed_prefix_cannot_return_an_arm_artifact

Four assertions in total, including the adversarial case the shared prefix allowed — an arm whose id looks like a trading day — and one asserting arm_key_segment stays invertible and still refuses an arm_id that already contains ~, which is the issue's stated gotcha.

Two things found on the way

  1. crucible.keys.__all__ was out of alphabetical order before this change. The repo's own TestAllIsSortedAndDeduplicated caught my addition and the pre-existing disorder together. Re-sorted and de-duplicated: 79 entries.
  2. The tests went into tests/test_keys_prefixes.py, the file that already owns this class of assertion, rather than the tests/test_keys.py the issue named — which does not exist. Creating it would have made a second home for one question. Reality over the issue's guess.

Test plan

Full suite green before opening, under the repo's own invocation:

uv run --frozen pytest -q --ignore=tests/acceptance   ->  all passed

Worth recording: a bare python3 -m pytest in a worktree reports 8 spurious failures in tests/test_deploy.py with PackageNotFoundError: No package metadata was found for crucible — the laptop's global interpreter, not the repo environment. uv run --frozen is the invocation that measures the repo; the bare one measures the laptop.

Prepared by: Claude Opus 5 (1M context) via Claude Code

cipher813 and others added 2 commits September 11, 2026 10:52
…x (alpha-engine-config-I9822)

`predictions/` held two artifact shapes: the trader's champion serving feed
(`predictions/{trading_day}.json`, the contract named in `slots/__init__.py`
and AGENTS.md) and the per-arm artifact
(`predictions/{arm~seg}/{trading_day}.json`). A consumer doing
`store.list_keys("predictions/")` saw both and had to discriminate by path
depth. Nothing did, which is what made it a design risk rather than a live
defect.

The per-arm shape moves to `arm_predictions/`, and `predictions_key` is
declared so the trader contract has a single source for its key rather than
living only in prose.

MOVED WHILE THE PREFIX WAS EMPTY. Measured 2026-09-11: zero objects under
`predictions/` in the production store, so this orphaned nothing. The same
change after either shape had been written would have stranded those objects
at an address no code resolves - which is alpha-engine-config-I10498, filed
the same day for a feature layer stranded exactly that way by a content-hash
move. The cheapest moment to separate two artifact shapes is before either
exists, and that moment is now.

Deliverable 1 of the issue was already done: `arm_predictions_key` lives in
`crucible/keys.py` and `slots/inputs.py` imports it from there. Only
deliverable 2 remained.

The new guard is proven red, not assumed: reverting ARM_PREDICTIONS_PREFIX to
"predictions/" fails
test_the_two_shapes_live_under_different_prefixes and
test_a_listing_of_the_feed_prefix_cannot_return_an_arm_artifact.

Two things found on the way:
- `__all__` was out of alphabetical order before this change; the repo's own
  TestAllIsSortedAndDeduplicated caught my addition and the pre-existing
  disorder together. Re-sorted and de-duplicated, 79 entries.
- The tests went into `tests/test_keys_prefixes.py`, the file that already
  owns this class of assertion, rather than the `tests/test_keys.py` the
  issue named - which does not exist, and creating it would have made a
  second home for one question.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WARhC81oatq9czVeyaM66L
Prepared by: Claude Opus 5 (1M context) via Claude Code

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WARhC81oatq9czVeyaM66L
@cipher813
cipher813 force-pushed the fix/arm-predictions-prefix-collision-i9822 branch from 6587247 to 318dae5 Compare September 11, 2026 17:52
@cipher813
cipher813 merged commit 78893dd into main Sep 11, 2026
8 checks passed
@cipher813
cipher813 deleted the fix/arm-predictions-prefix-collision-i9822 branch September 11, 2026 17:57
@cipher813 cipher813 added the agent-merged Merged by an agent under an explicit in-session instruction or a standing exception label Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent-merged Merged by an agent under an explicit in-session instruction or a standing exception

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant