Skip to content

doc(BA-7803): fill the container registry and image adapter scenarios - #14503

Closed
jopemachine wants to merge 9 commits into
mainfrom
docs/BA-7803-adapter-scenarios
Closed

doc(BA-7803): fill the container registry and image adapter scenarios#14503
jopemachine wants to merge 9 commits into
mainfrom
docs/BA-7803-adapter-scenarios

Conversation

@jopemachine

Copy link
Copy Markdown
Member

Summary

  • Replace the TODO stubs in api/adapters/container_registry/KNOWLEDGE.md and api/adapters/image/KNOWLEDGE.md with the scenarios each adapter guarantees, following the shape api/adapters/domain/KNOWLEDGE.md set in test(BA-7779): PoC of scenario-table tests through the adapter layer #14448.
  • Every public adapter call carries at least one scenario — 6 calls / 33 rows for the container registry, 11 calls / 40 rows for images — so the tests under tests/scenario can be written one for one against them.
  • Every way a call can be refused gets its own row: the superadmin role, the entity gate, the ownership check ImageService runs after the gate, request validation, and the rbac relation gate behind the registry's allowed-project list.

Two asymmetries worth a reviewer's eye, both stated in the documents:

  • A registry URL is checked on update but not on create, so an ftp:// registry can be created and only refuses later when edited.
  • An image read answers differently depending on who asks: hide_agents blanks the installed-agent information for a non-superadmin.

Test plan

  • python3 scripts/knowledge/check.py passes
  • Scenario tests under tests/scenario are written against these rows (follow-up)

Resolves BA-7803

🤖 Generated with Claude Code

https://claude.ai/code/session_013TYyokzEfpr9AF2NnHtyXB

jopemachine and others added 2 commits September 11, 2026 10:53
Replace the TODO stubs with the scenarios each adapter guarantees, so the
integration tests under tests/scenario can be written one for one against them.

Every public adapter call gets at least one scenario, and every way a call can
be refused gets its own row: the superadmin role, the entity gate, the ownership
check the image service runs after the gate, request validation, and the rbac
relation gate behind the registry's allowed-project list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TYyokzEfpr9AF2NnHtyXB
@github-actions github-actions Bot added the size:L 100~500 LoC label Sep 11, 2026
jopemachine and others added 3 commits September 11, 2026 11:04
Follow the conventions #14501 settled, which the first draft missed:

- Drop the rows a request type already refuses (a blank URL, a blank registry
  name). The adapter does not guarantee those.
- Add the enforcement-switch row to each gate, so the two answer differently:
  the registry's own calls stay refused because a role guards them, while the
  allowed-project link goes through because the graph does.
- Name the permission the caller holds in every 상황 cell, rather than "권한 없음".
- Keep 아직 적지 않은 것 for adapter calls only; every call is covered, so it
  says so, and the wirings that never reach an adapter moved to the header.

Also correct two things the first draft got wrong:

- The registry search does not refuse a mixed pagination mode. The request type
  carries cursor fields but the adapter reads only limit and offset, so a cursor
  is dropped in silence; that is now written as a question to settle.
- Replace 물리기/물린다 for the soft delete with 잊기, the adapter's own word
  (`admin_forget`), and say in the section that the row survives.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TYyokzEfpr9AF2NnHtyXB
The check is not a scheme allowlist. A URL that starts with neither `http://`
nor `https://` has `http://` prepended before it is parsed, so `ftp://reg.example`
parses with a non-empty host and passes, as does a string with spaces in it. The
only URL refused is one whose host part comes out empty.

Two rows claimed otherwise: create used `ftp://` as its example of a refused
address, and update said a non-http scheme is refused. Both now name the missing
host, and the update section says what the check looks at so the next reader does
not have to re-derive it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TYyokzEfpr9AF2NnHtyXB
- Call the listing 검색 rather than 훑기. 훑기 said nothing on its own, and this
  concern already has a rescan (`rescan_images`) that a reader would confuse it
  with; the header now names that one 리스캔 and says it never reaches an adapter.
- Say "필수 값만 주고 만든다" instead of counting the required fields, which told
  the reader nothing and would go stale the moment a field is added.
- Split every sentence that carried more than one idea. The image header replaces
  a paragraph about the three gates with a table of them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TYyokzEfpr9AF2NnHtyXB
Every row of the adapter's KNOWLEDGE.md becomes one scenario, six modules for
the six calls it offers: 31 rows over creating, searching, reading, editing,
allowing and retiring.

- Add `AllowProject`, the write spec that links a project to a registry, so the
  rows about removing a link start from one that is really there.
- Add the components these tables are written against, including the given that
  hands a caller either scope of the allowed-project pair, or neither. The
  relation gate asks for both, so one-sided is a row of its own.
- Correct one row in the document while transcribing it: creating a registry
  with an allowed project can only be a superadmin's, because the relation gate
  asks for permission on a registry that does not exist yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TYyokzEfpr9AF2NnHtyXB
@github-actions github-actions Bot added size:XL 500~ LoC and removed size:L 100~500 LoC labels Sep 11, 2026
jopemachine and others added 3 commits September 11, 2026 11:42
Every row of the adapter's KNOWLEDGE.md becomes one scenario, six modules for
the eleven calls it offers: 43 rows over searching, reading, forgetting,
retiring, aliasing and editing.

- Extend `SeedImage` with the status and the customized-owner pair, so a
  scenario can lay an image its caller owns and one a purge is working through.
- Add `SeedAlias`, and the components these tables are written against. The role
  that reaches an image sits on its registry, because that is the scope an image
  is created under.
- Leave the agent registry and the image cache unwired: none of the eleven calls
  read them, and a scenario that turns out to need one fails saying so.

The two rows that matter most are the ones where a granted caller is still
refused an image nobody owns, and where turning enforcement off opens the gate
but not the ownership check behind it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TYyokzEfpr9AF2NnHtyXB
CI ran these rows for the first time and four claims did not hold. The document
said what the code looked like it would do; these say what it does.

- A registry created without them comes back with `ssl_verify` and `is_global`
  true, not empty: the columns default to true.
- A permission row holds one operation bit, so the roles that reach a registry
  link or an image lay one row per bit rather than a mask.
- A duplicate alias is refused as a unique-constraint violation before the
  repository's own error reaches the caller.
- Restoring cannot reach a forgotten image, and forgetting cannot reach one a
  purge is working through. Both are looked up by id where only a live image is
  visible, so both answer that the image is not there. The document now says so
  and asks whether restore should stay that way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TYyokzEfpr9AF2NnHtyXB
A `When` held two or three booleans where one value belongs, so a combination
that means nothing was expressible and `describe` had to rebuild the state from
the flags every time. Each of those is now a value that says what it is:

| was | is |
|---|---|
| `allowing` / `allowing_missing` | `allowed: NoProjects / TheLaidProject / AProjectThatIsGone` |
| `adding` / `removing` / `at_missing` | `change: Adding / Removing / AddingWhatIsGone` |
| `at_missing` | `at: TheLaidRegistry / TheLaidImage / AnIdThatHoldsNothing` |
| `first` / `after` / `limit` / `narrowed` | `paging: ByOffset / ByCursor / ByABrokenCursor / ByTwoModesAtOnce` |

`describe` now asks the value what it says, so the sentence and the request can
no longer disagree, and a caller reads the situation without opening the class.

Also rename what the deployment scenarios named better: the three-noun pile-ups
(`ARegistryAProjectAndSomeone`) become `ARegistryAndAProjectToAllow`, `SomeoneAlone`
becomes `NoRegistryYet`, and `RoleOver[S]` becomes `SomeoneLinkingIn[ScopeData]`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013TYyokzEfpr9AF2NnHtyXB
@jopemachine

Copy link
Copy Markdown
Member Author

Split into #14519 (container registry, BA-7803) and #14520 (image, BA-7837). The concern holds two entities with an adapter each, and one PR of 4.7k lines was too much to review in one sitting.

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

Labels

size:XL 500~ LoC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant