Skip to content

Preserve state keys#1897

Open
McHersheys wants to merge 1 commit into
iii-hq:mainfrom
McHersheys:fix/state-key-labels-1896
Open

Preserve state keys#1897
McHersheys wants to merge 1 commit into
iii-hq:mainfrom
McHersheys:fix/state-key-labels-1896

Conversation

@McHersheys

@McHersheys McHersheys commented Jun 22, 2026

Copy link
Copy Markdown

Fixes #1896.

Tests:

  • corepack pnpm --filter console-frontend test -- src/api/state/state.test.ts

  • corepack pnpm --filter console-frontend lint

  • corepack pnpm --filter console-frontend build

  • I license my contributions to this repository under Apache 2.0, and I have all necessary rights over the code I am contributing.

Summary by CodeRabbit

  • Improvements

    • Enhanced state item handling to flexibly support multiple API response formats
    • Improved key-value extraction with better fallback behavior when keys are missing
  • Tests

    • Added comprehensive test coverage for state item normalization across various input scenarios

@vercel

vercel Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
iii-website Skipped Skipped Jun 22, 2026 7:16am

Request Review

@iii-hq-ci

iii-hq-ci Bot commented Jun 22, 2026

Copy link
Copy Markdown

License agreement recorded

@McHersheys, your agreement has been recorded and you have been added to contributors.md. All future PRs from your account will pass this check automatically.

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 479e3bfe-217c-47a5-aa72-6f15f668f762

📥 Commits

Reviewing files that changed from the base of the PR and between 95908ac and fc62c41.

📒 Files selected for processing (2)
  • console/packages/console-frontend/src/api/state/state.test.ts
  • console/packages/console-frontend/src/api/state/state.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • console/packages/console-frontend/src/api/state/state.test.ts
  • console/packages/console-frontend/src/api/state/state.ts

📝 Walkthrough

Walkthrough

fetchStateItems in state.ts replaces a fixed { items: unknown[] } unwrap with internal normalization helpers that handle array and object/record response shapes. Key extraction follows a priority order: item.key, object entry key, embedded state_key, then a "(missing key N)" fallback. A new Vitest suite stubs fetch and validates all four extraction paths.

Changes

State item normalization and tests

Layer / File(s) Summary
Normalization helpers and fetchStateItems update
console/packages/console-frontend/src/api/state/state.ts
Adds isRecord, extractString, computeValue, makeItem, and normalizeStateItems internal helpers. normalizeStateItems detects array vs. object response shapes and extracts each item's key in priority order: item.key → object entry key → item.state_key"(missing key N)". fetchStateItems now calls normalizeStateItems instead of the old fixed unwrap.
Test suite for key/value mapping
console/packages/console-frontend/src/api/state/state.test.ts
Introduces a Vitest suite with a mockStateItems helper that stubs global fetch. Four test cases assert correct key and value output for: explicit key field, object-shaped map entries, embedded state_key, and missing-key fallback producing "(missing key 0)".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 No more item-0, item-1 in a row,
The keys were always there — now they show!
Arrays or objects, record or map,
We peek at state_key to close the gap.
With fallbacks tidy and stubs in place,
The console now wears its true key face. 🗝️

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is incomplete and does not follow the required template structure with 'What,' 'Why,' and 'Notes' sections. Expand the description to include structured sections explaining what changed, why it was changed, and any important notes for reviewers.
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Preserve state keys' directly and clearly describes the main change—implementing logic to preserve semantic state keys instead of replacing them with positional labels.
Linked Issues check ✅ Passed The code changes implement the key preservation strategy specified in issue #1896 by normalizing state items with priority-ordered key selection and fallback handling.
Out of Scope Changes check ✅ Passed All changes are directly related to the issue objective of preserving semantic state keys in the /states view and are within the scope of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
console/packages/console-frontend/src/api/state/state.test.ts (1)

32-39: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add a regression case for value.key beating a positional map key.

This test only proves plain map keys are used. It would not catch the /states regression where an object entry like item-0 masks an embedded semantic key.

Proposed test addition
   it('uses map keys for object-shaped item responses', async () => {
     mockStateItems({ items: { 'files/conscience/covenant.yaml': { raw: true } } })
 
     const { items } = await fetchStateItems('chambers')
 
     expect(items[0].key).toBe('files/conscience/covenant.yaml')
     expect(items[0].value).toEqual({ raw: true })
   })
+
+  it('uses explicit item keys before object entry keys', async () => {
+    mockStateItems({ items: { 'item-0': { key: 'worker', value: { status: 'ready' } } } })
+
+    const { items } = await fetchStateItems('chambers')
+
+    expect(items[0].key).toBe('worker')
+  })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/packages/console-frontend/src/api/state/state.test.ts` around lines
32 - 39, Add a regression test case to the test file that verifies when a value
object contains its own `key` property, the actual map key is still correctly
used and not overridden by the embedded key. Extend or create a new test in the
state.test.ts file that mocks a response where an item value contains a `key`
property (for example, `{ raw: true, key: 'some-other-key' }`), and assert that
`items[0].key` resolves to the map key (the object property name) and not the
embedded `value.key` property. This regression test should prevent regressions
like the `/states` issue where an embedded `key` property could incorrectly mask
the positional map key.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@console/packages/console-frontend/src/api/state/state.ts`:
- Around line 33-35: The stateItemValue function only unwraps item.value when
item.key or item.state_key are non-empty strings, but the code elsewhere accepts
item.id as a valid key source. Update the condition in the stateItemValue
function to also check nonEmptyString(item.id) alongside the existing checks for
item.key and item.state_key, so that the value is properly unwrapped regardless
of which key source is used.
- Around line 76-80: In the state.ts file within the map function that processes
rawItems, the priority order for selecting a key is incorrect. The code
currently uses "key || embeddedKey || fallbackKey(index)" which prioritizes the
Object.entries key before the embedded value.key property. Change the return
statement in makeStateItem to prioritize embeddedKey (which already checks
value.key first via nonEmptyString calls) over the map entry key, so the order
becomes "embeddedKey || key || fallbackKey(index)" instead. This ensures that an
explicit value.key property in the record takes precedence over the map entry
key before falling back to other alternatives.

---

Nitpick comments:
In `@console/packages/console-frontend/src/api/state/state.test.ts`:
- Around line 32-39: Add a regression test case to the test file that verifies
when a value object contains its own `key` property, the actual map key is still
correctly used and not overridden by the embedded key. Extend or create a new
test in the state.test.ts file that mocks a response where an item value
contains a `key` property (for example, `{ raw: true, key: 'some-other-key' }`),
and assert that `items[0].key` resolves to the map key (the object property
name) and not the embedded `value.key` property. This regression test should
prevent regressions like the `/states` issue where an embedded `key` property
could incorrectly mask the positional map key.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d38bc76f-66d8-4bf9-bd67-c3fdcb5b499e

📥 Commits

Reviewing files that changed from the base of the PR and between c4647cc and 95908ac.

📒 Files selected for processing (2)
  • console/packages/console-frontend/src/api/state/state.test.ts
  • console/packages/console-frontend/src/api/state/state.ts

Comment on lines +33 to +35
function stateItemValue(item: Record<string, unknown>): unknown {
if ('value' in item && (nonEmptyString(item.key) || nonEmptyString(item.state_key))) {
return item.value

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Unwrap value consistently when id is used as the key.

Line 63 accepts item.id as a key source, but Line 34 only unwraps item.value for key/state_key. { id: 'worker', value: {...} } would render the wrapper object instead of the actual state value.

Proposed fix
 function stateItemValue(item: Record<string, unknown>): unknown {
-  if ('value' in item && (nonEmptyString(item.key) || nonEmptyString(item.state_key))) {
+  if (
+    'value' in item &&
+    (nonEmptyString(item.key) || nonEmptyString(item.state_key) || nonEmptyString(item.id))
+  ) {
     return item.value
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function stateItemValue(item: Record<string, unknown>): unknown {
if ('value' in item && (nonEmptyString(item.key) || nonEmptyString(item.state_key))) {
return item.value
function stateItemValue(item: Record<string, unknown>): unknown {
if (
'value' in item &&
(nonEmptyString(item.key) || nonEmptyString(item.state_key) || nonEmptyString(item.id))
) {
return item.value
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/packages/console-frontend/src/api/state/state.ts` around lines 33 -
35, The stateItemValue function only unwraps item.value when item.key or
item.state_key are non-empty strings, but the code elsewhere accepts item.id as
a valid key source. Update the condition in the stateItemValue function to also
check nonEmptyString(item.id) alongside the existing checks for item.key and
item.state_key, so that the value is properly unwrapped regardless of which key
source is used.

Comment on lines +76 to +80
return Object.entries(rawItems).map(([key, value], index) => {
const embeddedKey = isRecord(value)
? (nonEmptyString(value.key) ?? nonEmptyString(value.state_key) ?? nonEmptyString(value.id))
: null
return makeStateItem(groupId, key || embeddedKey || fallbackKey(index), value)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Honor explicit value.key before the map entry key.

Line 80 currently chooses the Object.entries(...) key first, so { items: { 'item-0': { key: 'worker' } } } would still display item-0. The requested priority puts item.key before tuple/map keys.

Proposed fix
   if (isRecord(rawItems)) {
     return Object.entries(rawItems).map(([key, value], index) => {
-      const embeddedKey = isRecord(value)
-        ? (nonEmptyString(value.key) ?? nonEmptyString(value.state_key) ?? nonEmptyString(value.id))
+      const valueRecord = isRecord(value) ? value : null
+      const explicitKey = valueRecord ? nonEmptyString(valueRecord.key) : null
+      const embeddedKey = valueRecord
+        ? (nonEmptyString(valueRecord.state_key) ?? nonEmptyString(valueRecord.id))
         : null
-      return makeStateItem(groupId, key || embeddedKey || fallbackKey(index), value)
+      return makeStateItem(
+        groupId,
+        explicitKey ?? nonEmptyString(key) ?? embeddedKey ?? fallbackKey(index),
+        value,
+      )
     })
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return Object.entries(rawItems).map(([key, value], index) => {
const embeddedKey = isRecord(value)
? (nonEmptyString(value.key) ?? nonEmptyString(value.state_key) ?? nonEmptyString(value.id))
: null
return makeStateItem(groupId, key || embeddedKey || fallbackKey(index), value)
return Object.entries(rawItems).map(([key, value], index) => {
const valueRecord = isRecord(value) ? value : null
const explicitKey = valueRecord ? nonEmptyString(valueRecord.key) : null
const embeddedKey = valueRecord
? (nonEmptyString(valueRecord.state_key) ?? nonEmptyString(valueRecord.id))
: null
return makeStateItem(
groupId,
explicitKey ?? nonEmptyString(key) ?? embeddedKey ?? fallbackKey(index),
value,
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@console/packages/console-frontend/src/api/state/state.ts` around lines 76 -
80, In the state.ts file within the map function that processes rawItems, the
priority order for selecting a key is incorrect. The code currently uses "key ||
embeddedKey || fallbackKey(index)" which prioritizes the Object.entries key
before the embedded value.key property. Change the return statement in
makeStateItem to prioritize embeddedKey (which already checks value.key first
via nonEmptyString calls) over the map entry key, so the order becomes
"embeddedKey || key || fallbackKey(index)" instead. This ensures that an
explicit value.key property in the record takes precedence over the map entry
key before falling back to other alternatives.

@McHersheys McHersheys force-pushed the fix/state-key-labels-1896 branch from 95908ac to fc62c41 Compare June 22, 2026 07:16
@anthonyiscoding

Copy link
Copy Markdown
Contributor

Thanks for the PR! We'll take a look and get back to you

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

iii-console states view should preserve semantic state keys instead of item-N labels

3 participants