From c009826b655b689b290326d1fd76ea9ebee3b8c6 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sat, 30 May 2026 18:06:20 +0000 Subject: [PATCH 01/49] self-development: broaden substring-assertion rule to forwarding tests The existing "vacuous substring assertions" convention was scoped only to printer/formatter tests, leaving stream/forwarding tests uncovered. In #1189 both the Kelos reviewer and cubic independently flagged a forwarder test that used strings.Contains per line, which passes even when lines are dropped, duplicated, or reordered. Extend the rule to require full byte-equality comparison for writer/pipe forwarding tests, mirroring the assertForwarded fix landed in that PR. Co-Authored-By: Claude Opus 4.8 --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 96c57abe2..9b52d3d1a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,7 +19,7 @@ - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. -- **Avoid vacuous substring assertions in printer/formatter tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. +- **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. - **Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker.** If the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. - **Qualify cross-CRD field references with the owning kind in docs.** In a CRD reference section, write `Task.spec.podOverrides.env` rather than bare `podOverrides.env` when describing a field that lives on a sibling CRD. A reader of one CRD's reference page should be able to locate the cited field without already knowing the layout of the others. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 6f93c08c2..6aab66b6d 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -40,7 +40,7 @@ spec: - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. - - Avoid vacuous substring assertions in printer/formatter tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. + - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. - Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker: if the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. - Qualify cross-CRD field references with the owning kind in docs: in a CRD reference section, write `Task.spec.podOverrides.env` rather than bare `podOverrides.env` when describing a field that lives on a sibling CRD. A reader of one CRD's reference page should be able to locate the cited field without already knowing the layout of the others. - PRs that only modify files under `self-development/` are internal agent improvements: use `/kind cleanup` and write "NONE" in the `release-note` block, even when the change fixes a bug or adds a feature in agent behavior. Classify by file location, not by problem nature. From bed2c114d7910e2fe8e79dad32f7bbc56465f0c9 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sun, 31 May 2026 18:03:29 +0000 Subject: [PATCH 02/49] self-development: require author/subject filters on autonomous-run triggers PR #1241 (Add kanon-development TaskSpawners) shipped event-driven triggers without author scoping. The Kelos/cubic review flagged this twice and both fixes were applied in that PR: - P1 (confidence 9) on kanon-pr-responder.yaml: add an `author` filter to the `pull_request_review` trigger; without it, unapproved review comments can trigger autonomous task runs. - P2 on kanon-reviewer.yaml: the `issue_comment` trigger was not scoped to pull requests, so `/kelos review` on a regular issue could start the reviewer and make the PR-only command fail. Add a TaskSpawner convention so future spawners ship the `author` allowlist and `commentOn: PullRequest` scoping up front instead of being corrected in review. Existing conventions only said issue_comment fires for both subjects; they did not state the baseline that every autonomous-run trigger must be restricted to a trusted author and the intended subject. Co-Authored-By: Claude Opus 4.8 --- self-development/agentconfig.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 6aab66b6d..0a54640f2 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -48,4 +48,5 @@ spec: - Prefer webhook-based triggers (`githubWebhook`) over poll-based (`githubPullRequests`) for real-time event-driven tasks - The `{{.Branch}}` template variable is empty for issue-only events; use `{{with index . "Branch"}}{{.}}{{else}}main{{end}}` when it may be empty - The `issue_comment` webhook event fires for both issues and pull requests; design prompts to detect and handle both contexts + - Restrict every trigger that can launch an autonomous run to a trusted author and the intended subject: give `issue_comment` and `pull_request_review` filters an `author` allowlist (e.g. `author: gjkim42`) so an unapproved comment or review cannot start a task and spend tokens, and add `commentOn: PullRequest` to filters whose command is PR-only (e.g. `/kelos review`) so the same command on a plain issue does not start a PR-only task. An unfiltered event trigger is reachable by anyone who can comment - Do not include manual PR branch checkout instructions in prompts — Kelos already checks out the PR branch automatically From a3d67bb1697395e6ff160b11e7e973563d877a5c Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Mon, 1 Jun 2026 18:04:08 +0000 Subject: [PATCH 03/49] self-development: distinguish "not found" from "lookup failed" in handlers PR #1238 (Add WebhookGateway CRD) shipped request handlers that conflated API lookup errors with empty results. The review flagged this repeatedly and the maintainer fixed every instance: - A TaskSpawner List error was treated as an empty match set, so a transient API failure silently dropped the webhook with a 200; the fix returns the error so the handler responds 5xx and the sender retries. - The gateway Get returned 404 unconditionally; the fix returns 404 only on IsNotFound and 5xx on RBAC/transient errors. Add a coding convention so handlers and reconcilers propagate lookup errors as 5xx (sender redelivers) instead of answering 200 with an empty result. This is distinct from the existing "fail fast on invalid configuration" rule, which covers startup config/secrets rather than request-time API reads. Co-Authored-By: Claude Opus 4.8 --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 9b52d3d1a..f4093734b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,7 @@ - **Kubernetes resource comparison.** Use semantic `.Equal()` or `.Cmp()` methods for `resource.Quantity` comparisons, not `reflect.DeepEqual` — structurally different Quantity values can be semantically identical (e.g., `1000m` vs `1` CPU). - **Never use `os.Getenv()` for secrets as Go `flag` defaults.** Go's `flag` package prints default values in usage/help output, which leaks secret values. Instead, use an empty default and read the env var after `flag.Parse()`. - **Fail fast on invalid configuration.** Do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing. Return an error or exit immediately instead of returning nil or empty values that mask the failure. +- **Distinguish "not found" from "lookup failed" in request handlers.** In a webhook/HTTP handler or reconciler, a `List`/`Get` that errors is not the same as an empty result. Do not treat a failed lookup as "no match" and answer `200` — that silently drops the event with no redelivery. Propagate the error so the handler returns `5xx` and the sender retries; return `404` only on `IsNotFound`, and `5xx` on RBAC or transient errors. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. - **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 0a54640f2..a71884624 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -34,6 +34,7 @@ spec: - Kubernetes resource comparison: use semantic `.Equal()` or `.Cmp()` methods for `resource.Quantity` comparisons, not `reflect.DeepEqual` - Never use `os.Getenv()` for secrets as Go `flag` defaults: Go's `flag` package prints default values in usage/help output, which leaks secret values; use an empty default and read the env var after `flag.Parse()` - Fail fast on invalid configuration: do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing; return an error or exit immediately + - Distinguish "not found" from "lookup failed" in request handlers: in a webhook/HTTP handler or reconciler, a `List`/`Get` that errors is not the same as an empty result; do not treat a failed lookup as "no match" and answer `200` (which silently drops the event with no redelivery), but propagate the error so the handler returns `5xx` and the sender retries — return `404` only on `IsNotFound`, and `5xx` on RBAC or transient errors - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. From b94501327c130c547a952b6fae90172065df9502 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Wed, 15 Jul 2026 18:05:54 +0000 Subject: [PATCH 04/49] self-development: honor reduced-motion preferences --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index f4093734b..b5a0b9cbd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,6 +17,7 @@ - **Import versioned Kelos APIs with aliases.** Prefer aliasing the current versioned API import as `kelos` when a file uses only one API version. Use explicit aliases like `kelosv1alpha1` only when a file genuinely needs multiple versions. - **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. - **Keep `docs/reference.md` user-facing.** Document API fields, configuration, validation, defaults, and observable operational behavior. Do not describe controller or runtime implementation details unless users need them to configure, operate, or troubleshoot Kelos. Prefer outcomes over mechanisms, and remove nonessential internal detail instead of moving it to a separate architecture document. +- **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index a71884624..eacc75437 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -38,6 +38,7 @@ spec: - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. + - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. From e6c3e03c60c30a32c42f0db04b8b0075288ad2cd Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Mon, 27 Jul 2026 18:10:25 +0000 Subject: [PATCH 05/49] self-development: add security, state, and behavior-test conventions --- AGENTS.md | 3 +++ self-development/agentconfig.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index b5a0b9cbd..e7c863a4a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,7 +10,9 @@ - **Kubernetes resource comparison.** Use semantic `.Equal()` or `.Cmp()` methods for `resource.Quantity` comparisons, not `reflect.DeepEqual` — structurally different Quantity values can be semantically identical (e.g., `1000m` vs `1` CPU). - **Never use `os.Getenv()` for secrets as Go `flag` defaults.** Go's `flag` package prints default values in usage/help output, which leaks secret values. Instead, use an empty default and read the env var after `flag.Parse()`. - **Fail fast on invalid configuration.** Do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing. Return an error or exit immediately instead of returning nil or empty values that mask the failure. +- **Protect credentials in configuration and transport.** Values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - **Distinguish "not found" from "lookup failed" in request handlers.** In a webhook/HTTP handler or reconciler, a `List`/`Get` that errors is not the same as an empty result. Do not treat a failed lookup as "no match" and answer `200` — that silently drops the event with no redelivery. Propagate the error so the handler returns `5xx` and the sender retries; return `404` only on `IsNotFound`, and `5xx` on RBAC or transient errors. +- **Protect asynchronous state transitions from stale work.** Before an asynchronous completion mutates shared state, verify it still belongs to the current resource, Session, dialog, or request generation, or prevent that state from being replaced while the operation is pending. Before a destructive lifecycle transition based on observed status, coordinate or revalidate so newly accepted work cannot race with the transition. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. - **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. @@ -21,6 +23,7 @@ - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. +- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. - **Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker.** If the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. - **Qualify cross-CRD field references with the owning kind in docs.** In a CRD reference section, write `Task.spec.podOverrides.env` rather than bare `podOverrides.env` when describing a field that lives on a sibling CRD. A reader of one CRD's reference page should be able to locate the cited field without already knowing the layout of the others. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index eacc75437..3b08b5374 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -34,7 +34,9 @@ spec: - Kubernetes resource comparison: use semantic `.Equal()` or `.Cmp()` methods for `resource.Quantity` comparisons, not `reflect.DeepEqual` - Never use `os.Getenv()` for secrets as Go `flag` defaults: Go's `flag` package prints default values in usage/help output, which leaks secret values; use an empty default and read the env var after `flag.Parse()` - Fail fast on invalid configuration: do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing; return an error or exit immediately + - Protect credentials in configuration and transport: values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - Distinguish "not found" from "lookup failed" in request handlers: in a webhook/HTTP handler or reconciler, a `List`/`Get` that errors is not the same as an empty result; do not treat a failed lookup as "no match" and answer `200` (which silently drops the event with no redelivery), but propagate the error so the handler returns `5xx` and the sender retries — return `404` only on `IsNotFound`, and `5xx` on RBAC or transient errors + - Protect asynchronous state transitions from stale work: before an asynchronous completion mutates shared state, verify it still belongs to the current resource, Session, dialog, or request generation, or prevent that state from being replaced while the operation is pending. Before a destructive lifecycle transition based on observed status, coordinate or revalidate so newly accepted work cannot race with the transition. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. @@ -42,6 +44,7 @@ spec: - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. + - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. - Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker: if the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. - Qualify cross-CRD field references with the owning kind in docs: in a CRD reference section, write `Task.spec.podOverrides.env` rather than bare `podOverrides.env` when describing a field that lives on a sibling CRD. A reader of one CRD's reference page should be able to locate the cited field without already knowing the layout of the others. From 83a13d0d17b62aa9ef32ee693af44564d991dbd4 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sat, 8 Aug 2026 18:12:11 +0000 Subject: [PATCH 06/49] self-development: add replay and documentation conventions --- AGENTS.md | 2 ++ self-development/agentconfig.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index e7c863a4a..a0c3529f1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,12 +18,14 @@ - **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. - **Import versioned Kelos APIs with aliases.** Prefer aliasing the current versioned API import as `kelos` when a file uses only one API version. Use explicit aliases like `kelosv1alpha1` only when a file genuinely needs multiple versions. - **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. +- **Keep documentation complete and internally consistent.** When changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. - **Keep `docs/reference.md` user-facing.** Document API fields, configuration, validation, defaults, and observable operational behavior. Do not describe controller or runtime implementation details unless users need them to configure, operate, or troubleshoot Kelos. Prefer outcomes over mechanisms, and remove nonessential internal detail instead of moving it to a separate architecture document. - **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. - **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. +- **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. - **Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker.** If the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. - **Qualify cross-CRD field references with the owning kind in docs.** In a CRD reference section, write `Task.spec.podOverrides.env` rather than bare `podOverrides.env` when describing a field that lives on a sibling CRD. A reader of one CRD's reference page should be able to locate the cited field without already knowing the layout of the others. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 3b08b5374..1e4e07f5a 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -40,11 +40,13 @@ spec: - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. + - Keep documentation complete and internally consistent: when changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. + - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. - Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker: if the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. - Qualify cross-CRD field references with the owning kind in docs: in a CRD reference section, write `Task.spec.podOverrides.env` rather than bare `podOverrides.env` when describing a field that lives on a sibling CRD. A reader of one CRD's reference page should be able to locate the cited field without already knowing the layout of the others. From 211dc858401ed4b627914653c37c19bbcd7261d5 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Mon, 10 Aug 2026 18:10:48 +0000 Subject: [PATCH 07/49] self-development: add identity and client-contract conventions PR #1629 reviews showed that bounding a controller-owned StatefulSet name without accounting for ControllerRevision suffixes could either remain invalid or break workload and PVC identity. PR #1630 reviews found inconsistent attachment limits and empty-input behavior across web, TUI, and plain terminal clients, plus server failures classified as client errors by matching message text. Encode the durable-name, shared-client-contract, and typed error-classification expectations in the project and shared Kelos agent instructions. --- AGENTS.md | 4 +++- self-development/agentconfig.yaml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index a0c3529f1..4567e2f95 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,10 +11,11 @@ - **Never use `os.Getenv()` for secrets as Go `flag` defaults.** Go's `flag` package prints default values in usage/help output, which leaks secret values. Instead, use an empty default and read the env var after `flag.Parse()`. - **Fail fast on invalid configuration.** Do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing. Return an error or exit immediately instead of returning nil or empty values that mask the failure. - **Protect credentials in configuration and transport.** Values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. -- **Distinguish "not found" from "lookup failed" in request handlers.** In a webhook/HTTP handler or reconciler, a `List`/`Get` that errors is not the same as an empty result. Do not treat a failed lookup as "no match" and answer `200` — that silently drops the event with no redelivery. Propagate the error so the handler returns `5xx` and the sender retries; return `404` only on `IsNotFound`, and `5xx` on RBAC or transient errors. +- **Classify request failures by cause.** When mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases; return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result — propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - **Protect asynchronous state transitions from stale work.** Before an asynchronous completion mutates shared state, verify it still belongs to the current resource, Session, dialog, or request generation, or prevent that state from being replaced while the operation is pending. Before a destructive lifecycle transition based on observed status, coordinate or revalidate so newly accepted work cannot race with the transition. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. +- **Treat controller-generated Kubernetes names as durable identities.** Account for every downstream suffix and label limit when deriving names, including Pod, PVC, and ControllerRevision names, rather than checking only the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. - **Import versioned Kelos APIs with aliases.** Prefer aliasing the current versioned API import as `kelos` when a file uses only one API version. Use explicit aliases like `kelosv1alpha1` only when a file genuinely needs multiple versions. - **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. @@ -25,6 +26,7 @@ - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. - **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. +- **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. - **Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker.** If the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 1e4e07f5a..58c6fed99 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -35,10 +35,11 @@ spec: - Never use `os.Getenv()` for secrets as Go `flag` defaults: Go's `flag` package prints default values in usage/help output, which leaks secret values; use an empty default and read the env var after `flag.Parse()` - Fail fast on invalid configuration: do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing; return an error or exit immediately - Protect credentials in configuration and transport: values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - - Distinguish "not found" from "lookup failed" in request handlers: in a webhook/HTTP handler or reconciler, a `List`/`Get` that errors is not the same as an empty result; do not treat a failed lookup as "no match" and answer `200` (which silently drops the event with no redelivery), but propagate the error so the handler returns `5xx` and the sender retries — return `404` only on `IsNotFound`, and `5xx` on RBAC or transient errors + - Classify request failures by cause: when mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases, and return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result; propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - Protect asynchronous state transitions from stale work: before an asynchronous completion mutates shared state, verify it still belongs to the current resource, Session, dialog, or request generation, or prevent that state from being replaced while the operation is pending. Before a destructive lifecycle transition based on observed status, coordinate or revalidate so newly accepted work cannot race with the transition. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. + - Treat controller-generated Kubernetes names as durable identities: account for every downstream suffix and label limit when deriving names, including Pod, PVC, and ControllerRevision names, rather than checking only the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. - Keep documentation complete and internally consistent: when changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. @@ -46,6 +47,7 @@ spec: - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. + - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. - Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker: if the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. From 22f73753edbb0963f246f32000df0fd75a54d544 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Tue, 11 Aug 2026 18:08:40 +0000 Subject: [PATCH 08/49] self-development: preserve prompt and name contracts --- AGENTS.md | 3 ++- self-development/agentconfig.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 4567e2f95..7ba577eb6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,11 +15,12 @@ - **Protect asynchronous state transitions from stale work.** Before an asynchronous completion mutates shared state, verify it still belongs to the current resource, Session, dialog, or request generation, or prevent that state from being replaced while the operation is pending. Before a destructive lifecycle transition based on observed status, coordinate or revalidate so newly accepted work cannot race with the transition. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. -- **Treat controller-generated Kubernetes names as durable identities.** Account for every downstream suffix and label limit when deriving names, including Pod, PVC, and ControllerRevision names, rather than checking only the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. +- **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. - **Import versioned Kelos APIs with aliases.** Prefer aliasing the current versioned API import as `kelos` when a file uses only one API version. Use explicit aliases like `kelosv1alpha1` only when a file genuinely needs multiple versions. - **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. - **Keep documentation complete and internally consistent.** When changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. +- **Preserve prompt contracts when consolidating agent instructions.** Before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. - **Keep `docs/reference.md` user-facing.** Document API fields, configuration, validation, defaults, and observable operational behavior. Do not describe controller or runtime implementation details unless users need them to configure, operate, or troubleshoot Kelos. Prefer outcomes over mechanisms, and remove nonessential internal detail instead of moving it to a separate architecture document. - **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 58c6fed99..2d979d0ae 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -39,9 +39,10 @@ spec: - Protect asynchronous state transitions from stale work: before an asynchronous completion mutates shared state, verify it still belongs to the current resource, Session, dialog, or request generation, or prevent that state from being replaced while the operation is pending. Before a destructive lifecycle transition based on observed status, coordinate or revalidate so newly accepted work cannot race with the transition. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. - - Treat controller-generated Kubernetes names as durable identities: account for every downstream suffix and label limit when deriving names, including Pod, PVC, and ControllerRevision names, rather than checking only the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. + - Treat controller-generated Kubernetes names as durable identities: validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. - Keep documentation complete and internally consistent: when changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. + - Preserve prompt contracts when consolidating agent instructions: before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. From 9e41cb39a9f691bd5ee7302fa24403496ec26bed Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Wed, 12 Aug 2026 18:11:23 +0000 Subject: [PATCH 09/49] self-development: refine review-derived agent rules --- AGENTS.md | 2 ++ self-development/agentconfig.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 7ba577eb6..decd26ac7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -23,6 +23,7 @@ - **Preserve prompt contracts when consolidating agent instructions.** Before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. - **Keep `docs/reference.md` user-facing.** Document API fields, configuration, validation, defaults, and observable operational behavior. Do not describe controller or runtime implementation details unless users need them to configure, operate, or troubleshoot Kelos. Prefer outcomes over mechanisms, and remove nonessential internal detail instead of moving it to a separate architecture document. - **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. +- **Audit responsive rules when changing web controls.** When adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. @@ -30,6 +31,7 @@ - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. +- **Use lease protection after rebasing published branches.** Rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. - **Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker.** If the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. - **Qualify cross-CRD field references with the owning kind in docs.** In a CRD reference section, write `Task.spec.podOverrides.env` rather than bare `podOverrides.env` when describing a field that lives on a sibling CRD. A reader of one CRD's reference page should be able to locate the cited field without already knowing the layout of the others. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 2d979d0ae..274604257 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -44,6 +44,7 @@ spec: - Keep documentation complete and internally consistent: when changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. - Preserve prompt contracts when consolidating agent instructions: before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. + - Audit responsive rules when changing web controls: when adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. @@ -51,6 +52,7 @@ spec: - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. + - Use lease protection after rebasing published branches: rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. - Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker: if the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. - Qualify cross-CRD field references with the owning kind in docs: in a CRD reference section, write `Task.spec.podOverrides.env` rather than bare `podOverrides.env` when describing a field that lives on a sibling CRD. A reader of one CRD's reference page should be able to locate the cited field without already knowing the layout of the others. - PRs that only modify files under `self-development/` are internal agent improvements: use `/kind cleanup` and write "NONE" in the `release-note` block, even when the change fixes a bug or adds a feature in agent behavior. Classify by file location, not by problem nature. From ab82e155cfea92cd82ff7c39ac576bfcc3d38e50 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Thu, 13 Aug 2026 18:09:41 +0000 Subject: [PATCH 10/49] self-development: refine stale-state and assertion guidance --- AGENTS.md | 4 ++-- self-development/agentconfig.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index decd26ac7..b565142c3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,7 @@ - **Fail fast on invalid configuration.** Do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing. Return an error or exit immediately instead of returning nil or empty values that mask the failure. - **Protect credentials in configuration and transport.** Values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - **Classify request failures by cause.** When mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases; return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result — propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. -- **Protect asynchronous state transitions from stale work.** Before an asynchronous completion mutates shared state, verify it still belongs to the current resource, Session, dialog, or request generation, or prevent that state from being replaced while the operation is pending. Before a destructive lifecycle transition based on observed status, coordinate or revalidate so newly accepted work cannot race with the transition. +- **Protect asynchronous state transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. - **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. @@ -27,7 +27,7 @@ - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. -- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. +- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 274604257..29e6fa023 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -36,7 +36,7 @@ spec: - Fail fast on invalid configuration: do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing; return an error or exit immediately - Protect credentials in configuration and transport: values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - Classify request failures by cause: when mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases, and return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result; propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - - Protect asynchronous state transitions from stale work: before an asynchronous completion mutates shared state, verify it still belongs to the current resource, Session, dialog, or request generation, or prevent that state from being replaced while the operation is pending. Before a destructive lifecycle transition based on observed status, coordinate or revalidate so newly accepted work cannot race with the transition. + - Protect asynchronous state transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. - Treat controller-generated Kubernetes names as durable identities: validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. @@ -48,7 +48,7 @@ spec: - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. - - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. + - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. From 17f04e0227e20e8740add7ca4596c0b865d10b54 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sat, 15 Aug 2026 18:10:41 +0000 Subject: [PATCH 11/49] self-development: require cleanup of test background work --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index b565142c3..0038db7b4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,7 @@ - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. -- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. +- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 29e6fa023..d83dc1012 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -48,7 +48,7 @@ spec: - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. - - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. + - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. From 01c857ff6a175f7f9d058a30da9d5e3d1330acd6 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sun, 16 Aug 2026 18:09:22 +0000 Subject: [PATCH 12/49] self-development: tighten test-double contracts --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 0038db7b4..da626f5bc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,7 @@ - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. -- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. +- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index d83dc1012..9296e27d3 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -48,7 +48,7 @@ spec: - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. - - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. + - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. From be8c92a05e1159e7a919b213793427a9ff918cae Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Mon, 17 Aug 2026 18:14:27 +0000 Subject: [PATCH 13/49] self-development: cover lifecycle state precedence --- AGENTS.md | 4 ++-- self-development/agentconfig.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index da626f5bc..4d7cbeef2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,7 @@ - **Fail fast on invalid configuration.** Do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing. Return an error or exit immediately instead of returning nil or empty values that mask the failure. - **Protect credentials in configuration and transport.** Values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - **Classify request failures by cause.** When mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases; return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result — propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. -- **Protect asynchronous state transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. +- **Protect state-dependent transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. - **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. @@ -27,7 +27,7 @@ - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. -- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. +- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 9296e27d3..faf5f3604 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -36,7 +36,7 @@ spec: - Fail fast on invalid configuration: do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing; return an error or exit immediately - Protect credentials in configuration and transport: values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - Classify request failures by cause: when mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases, and return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result; propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - - Protect asynchronous state transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. + - Protect state-dependent transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. - Treat controller-generated Kubernetes names as durable identities: validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. @@ -48,7 +48,7 @@ spec: - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. - - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. + - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. From d7484f81b616afc4ff076db356abf3d2aff68760 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Wed, 19 Aug 2026 18:06:04 +0000 Subject: [PATCH 14/49] self-development: require realistic keyed and set fixtures --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 4d7cbeef2..dbe123fb3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,7 @@ - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. -- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. +- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index faf5f3604..c4cf8b862 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -48,7 +48,7 @@ spec: - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. - - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. + - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. From ce24d4739184814d2560d9a076c63f970bf61204 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Thu, 20 Aug 2026 18:10:16 +0000 Subject: [PATCH 15/49] self-development: add upgrade, API, and keyboard conventions --- AGENTS.md | 3 +++ self-development/agentconfig.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index dbe123fb3..db2ef6cf5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,7 +15,9 @@ - **Protect state-dependent transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. +- **Test API mutability through updates.** When making an API field mutable or changing transition validation, cover every newly allowed presence transition the contract promises, such as unset-to-set and set-to-unset, and at least one invalid `Update` that proves the remaining validation still applies. Create-only validation tests do not pin update behavior. - **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. +- **Make component renames upgrade-safe.** Before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. - **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. - **Import versioned Kelos APIs with aliases.** Prefer aliasing the current versioned API import as `kelos` when a file uses only one API version. Use explicit aliases like `kelosv1alpha1` only when a file genuinely needs multiple versions. - **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. @@ -24,6 +26,7 @@ - **Keep `docs/reference.md` user-facing.** Document API fields, configuration, validation, defaults, and observable operational behavior. Do not describe controller or runtime implementation details unless users need them to configure, operate, or troubleshoot Kelos. Prefer outcomes over mechanisms, and remove nonessential internal detail instead of moving it to a separate architecture document. - **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - **Audit responsive rules when changing web controls.** When adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. +- **Preserve keyboard focus in dynamic web controls.** When adding menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, dismissal, and focus restoration in browser-level tests. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index c4cf8b862..32090ff19 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -39,12 +39,15 @@ spec: - Protect state-dependent transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. + - Test API mutability through updates: when making an API field mutable or changing transition validation, cover every newly allowed presence transition the contract promises, such as unset-to-set and set-to-unset, and at least one invalid `Update` that proves the remaining validation still applies. Create-only validation tests do not pin update behavior. - Treat controller-generated Kubernetes names as durable identities: validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. + - Make component renames upgrade-safe: before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. - Keep documentation complete and internally consistent: when changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. - Preserve prompt contracts when consolidating agent instructions: before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - Audit responsive rules when changing web controls: when adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. + - Preserve keyboard focus in dynamic web controls: when adding menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, dismissal, and focus restoration in browser-level tests. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. From 9b2643222caa77d8bc3035703bfb824f2485e00c Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Fri, 21 Aug 2026 18:11:04 +0000 Subject: [PATCH 16/49] self-development: require keyboard navigation for custom widgets Roving tabindex removes inactive controls from sequential focus, so custom tablists and menus need explicit Arrow and Home/End navigation. Extend the existing focus-restoration rule to cover the complete keyboard interaction model and browser-level navigation tests. --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index db2ef6cf5..2ad66bc16 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,7 +26,7 @@ - **Keep `docs/reference.md` user-facing.** Document API fields, configuration, validation, defaults, and observable operational behavior. Do not describe controller or runtime implementation details unless users need them to configure, operate, or troubleshoot Kelos. Prefer outcomes over mechanisms, and remove nonessential internal detail instead of moving it to a separate architecture document. - **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - **Audit responsive rules when changing web controls.** When adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. -- **Preserve keyboard focus in dynamic web controls.** When adding menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, dismissal, and focus restoration in browser-level tests. +- **Preserve keyboard operability in dynamic web controls.** Implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 32090ff19..cc58fee7c 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -47,7 +47,7 @@ spec: - Preserve prompt contracts when consolidating agent instructions: before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - Audit responsive rules when changing web controls: when adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. - - Preserve keyboard focus in dynamic web controls: when adding menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, dismissal, and focus restoration in browser-level tests. + - Preserve keyboard operability in dynamic web controls: implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. From 325813734793ba7f10514b4beba621aab06032f7 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sat, 22 Aug 2026 18:11:51 +0000 Subject: [PATCH 17/49] self-development: require complete independent behavior tests --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 2ad66bc16..933a7b1a4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,7 +29,7 @@ - **Preserve keyboard operability in dynamic web controls.** Implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. -- **Test the happy path, not only the early-return guards.** When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), unit tests must include at least one positive case verifying the primary action runs with the right arguments — not just the no-op branches. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is covered. +- **Test every distinct behavior path.** When changed code has early-return or idempotent guards and a primary action, cover the primary action and every contractually distinct guard. For switch, table, or derivation logic, inventory and exercise every relevant source variant; use exact-set assertions when omissions matter. Arrange each case so it reaches the intended path and would fail if that path were removed or bypassed by an earlier condition. Tests must establish their own prerequisites or use explicit shared setup, include every runtime dependency, and remain executable independently. If production code lacks a seam, add one (interface, function field, fake client) so the behavior is testable. - **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index cc58fee7c..d3dd190e0 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -50,7 +50,7 @@ spec: - Preserve keyboard operability in dynamic web controls: implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - - Test the happy path, not only early-return guards: when a handler has both guard branches and a primary action (post a message, create a resource, emit a metric), include at least one positive test verifying the primary action runs with the right arguments. If the production code lacks a seam, add one (interface, function field, fake client) so the happy path is testable. + - Test every distinct behavior path: when changed code has early-return or idempotent guards and a primary action, cover the primary action and every contractually distinct guard. For switch, table, or derivation logic, inventory and exercise every relevant source variant; use exact-set assertions when omissions matter. Arrange each case so it reaches the intended path and would fail if that path were removed or bypassed by an earlier condition. Tests must establish their own prerequisites or use explicit shared setup, include every runtime dependency, and remain executable independently. If production code lacks a seam, add one (interface, function field, fake client) so the behavior is testable. - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. From 9f2fecd603c88a261c2196a608d2b815126a90e3 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sun, 23 Aug 2026 18:10:56 +0000 Subject: [PATCH 18/49] self-development: centralize lockstep behavior --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 933a7b1a4..bfd6fef8c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,6 +4,7 @@ - **Use Makefile targets** instead of discovering build/test commands yourself. - **Keep changes minimal.** Do not refactor, reorganize, or 'improve' code beyond what was explicitly requested. - **For CI/release workflows**, always use existing Makefile targets rather than reimplementing build logic in YAML. +- **Centralize behavior that must stay in lockstep.** When two code paths intentionally implement the same behavior and differ only by an input or action, share the implementation instead of copying it. Route repeated build and generation commands through one Makefile target so their flags cannot drift. Do not abstract incidental similarity. - **Better tests.** Always try to add or improve tests(including integration, e2e) when modifying code. - **Logging conventions.** Start log messages with capital letters and do not end with punctuation. - **Commit messages.** Do not include PR links in commit messages. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index d3dd190e0..6d4f0a7df 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -26,6 +26,7 @@ spec: - `make test` — run all unit tests - `make test-integration` — run integration tests - `make build` — build binary + - Centralize behavior that must stay in lockstep: when two code paths intentionally implement the same behavior and differ only by an input or action, share the implementation instead of copying it. Route repeated build and generation commands through one Makefile target so their flags cannot drift. Do not abstract incidental similarity. - When polling for a process to finish in shell, do not use `pgrep -f 'CMD_NAME'`: the caller's own argv contains the pattern, so pgrep matches itself and the loop never exits (deadlocks the pod). Either capture the PID at launch (`cmd & PID=$!`) and poll with `kill -0 "$PID"` / `wait "$PID"`, or exclude self from the match (`pgrep -f 'CMD_NAME' | grep -vw $$`). - Always try to add or improve tests when modifying code - Logging conventions: start log messages with capital letters and do not end with punctuation From 06ff71fb56517ee781c6ccc8fdf6093188430736 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Mon, 24 Aug 2026 18:09:57 +0000 Subject: [PATCH 19/49] self-development: preserve web accessibility across layouts --- AGENTS.md | 3 ++- self-development/agentconfig.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index bfd6fef8c..174cf5444 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,7 +26,8 @@ - **Preserve prompt contracts when consolidating agent instructions.** Before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. - **Keep `docs/reference.md` user-facing.** Document API fields, configuration, validation, defaults, and observable operational behavior. Do not describe controller or runtime implementation details unless users need them to configure, operate, or troubleshoot Kelos. Prefer outcomes over mechanisms, and remove nonessential internal detail instead of moving it to a separate architecture document. - **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. -- **Audit responsive rules when changing web controls.** When adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. +- **Preserve readable contrast across web UI themes.** When changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. +- **Audit responsive rules when changing web controls.** When adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. - **Preserve keyboard operability in dynamic web controls.** Implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 6d4f0a7df..641c8cefb 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -47,7 +47,8 @@ spec: - Keep documentation complete and internally consistent: when changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. - Preserve prompt contracts when consolidating agent instructions: before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - - Audit responsive rules when changing web controls: when adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. + - Preserve readable contrast across web UI themes: when changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. + - Audit responsive rules when changing web controls: when adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. - Preserve keyboard operability in dynamic web controls: implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. From 200e00986cb345a24482d75a2642f67b10a02a0f Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Tue, 25 Aug 2026 18:09:21 +0000 Subject: [PATCH 20/49] self-development: preserve Unicode text semantics --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 174cf5444..88d94149f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,6 +9,7 @@ - **Logging conventions.** Start log messages with capital letters and do not end with punctuation. - **Commit messages.** Do not include PR links in commit messages. - **Kubernetes resource comparison.** Use semantic `.Equal()` or `.Cmp()` methods for `resource.Quantity` comparisons, not `reflect.DeepEqual` — structurally different Quantity values can be semantically identical (e.g., `1000m` vs `1` CPU). +- **Preserve Unicode semantics in text processing.** When a limit is defined in characters, count Unicode code points rather than bytes. When enforcing byte limits or truncating UTF-8 text, cut only at valid code-point boundaries so user-visible output remains valid UTF-8. Cover affected limits with non-ASCII test cases. - **Never use `os.Getenv()` for secrets as Go `flag` defaults.** Go's `flag` package prints default values in usage/help output, which leaks secret values. Instead, use an empty default and read the env var after `flag.Parse()`. - **Fail fast on invalid configuration.** Do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing. Return an error or exit immediately instead of returning nil or empty values that mask the failure. - **Protect credentials in configuration and transport.** Values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 641c8cefb..b3cbaf093 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -33,6 +33,7 @@ spec: - Commit messages: do not include PR links in commit messages - When making structural changes (adding new files, configs, or components), update related documentation (especially README files) to stay in sync - Kubernetes resource comparison: use semantic `.Equal()` or `.Cmp()` methods for `resource.Quantity` comparisons, not `reflect.DeepEqual` + - Preserve Unicode semantics in text processing: when a limit is defined in characters, count Unicode code points rather than bytes. When enforcing byte limits or truncating UTF-8 text, cut only at valid code-point boundaries so user-visible output remains valid UTF-8. Cover affected limits with non-ASCII test cases. - Never use `os.Getenv()` for secrets as Go `flag` defaults: Go's `flag` package prints default values in usage/help output, which leaks secret values; use an empty default and read the env var after `flag.Parse()` - Fail fast on invalid configuration: do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing; return an error or exit immediately - Protect credentials in configuration and transport: values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. From 7ccd20f5b7fd1b613076919398ab948d059822f7 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Wed, 26 Aug 2026 18:08:16 +0000 Subject: [PATCH 21/49] self-development: preserve behavior across web layout changes --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 88d94149f..ef35d5e39 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,6 +29,7 @@ - **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - **Preserve readable contrast across web UI themes.** When changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. - **Audit responsive rules when changing web controls.** When adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. +- **Preserve UI behavior when restructuring web views.** When moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. - **Preserve keyboard operability in dynamic web controls.** Implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index b3cbaf093..17c355a47 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -50,6 +50,7 @@ spec: - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - Preserve readable contrast across web UI themes: when changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. - Audit responsive rules when changing web controls: when adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. + - Preserve UI behavior when restructuring web views: when moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. - Preserve keyboard operability in dynamic web controls: implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. From 1422615bf5e28a86fe2b652014c62aee91b9e70a Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Thu, 27 Aug 2026 18:10:42 +0000 Subject: [PATCH 22/49] self-development: bound work on hot paths --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index ef35d5e39..f252afa88 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,6 +37,7 @@ - **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. +- **Bound work on long-lived data and hot paths.** Do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. - **Use lease protection after rebasing published branches.** Rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. - **Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker.** If the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 17c355a47..84ad66feb 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -58,6 +58,7 @@ spec: - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. + - Bound work on long-lived data and hot paths: do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. - Use lease protection after rebasing published branches: rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. - Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker: if the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. From 2e58337f61bce31b8876c4ffdc1439a9f4926c92 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Fri, 28 Aug 2026 18:10:07 +0000 Subject: [PATCH 23/49] self-development: validate conversion preservation data --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index f252afa88..2d3af2552 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,6 +17,7 @@ - **Protect state-dependent transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. +- **Revalidate preservation data during API conversion.** Treat annotations or other storage used to carry latest-version fields through older API versions as user-controlled input. Before restoring decoded data into the hub object, validate it against the destination field's full schema, including item and count bounds, patterns, and set uniqueness; do not assume syntactically valid data is schema-valid or that conversion output will be revalidated. Test syntactically valid but out-of-schema preservation values in addition to malformed encodings. - **Test API mutability through updates.** When making an API field mutable or changing transition validation, cover every newly allowed presence transition the contract promises, such as unset-to-set and set-to-unset, and at least one invalid `Update` that proves the remaining validation still applies. Create-only validation tests do not pin update behavior. - **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - **Make component renames upgrade-safe.** Before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 84ad66feb..e57bde044 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -41,6 +41,7 @@ spec: - Protect state-dependent transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. + - Revalidate preservation data during API conversion: treat annotations or other storage used to carry latest-version fields through older API versions as user-controlled input. Before restoring decoded data into the hub object, validate it against the destination field's full schema, including item and count bounds, patterns, and set uniqueness; do not assume syntactically valid data is schema-valid or that conversion output will be revalidated. Test syntactically valid but out-of-schema preservation values in addition to malformed encodings. - Test API mutability through updates: when making an API field mutable or changing transition validation, cover every newly allowed presence transition the contract promises, such as unset-to-set and set-to-unset, and at least one invalid `Update` that proves the remaining validation still applies. Create-only validation tests do not pin update behavior. - Treat controller-generated Kubernetes names as durable identities: validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - Make component renames upgrade-safe: before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. From 1d6ccfc442dc8b39fab88f4a20d235c2868e7b1a Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sun, 30 Aug 2026 18:10:05 +0000 Subject: [PATCH 24/49] self-development: bound CR status aggregation --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 2d3af2552..8ad87d4f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,7 +38,7 @@ - **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. -- **Bound work on long-lived data and hot paths.** Do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. +- **Bound work and persisted state on long-lived data and hot paths.** Do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. - **Use lease protection after rebasing published branches.** Rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. - **Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker.** If the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index e57bde044..99106d9e3 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -59,7 +59,7 @@ spec: - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - - Bound work on long-lived data and hot paths: do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. + - Bound work and persisted state on long-lived data and hot paths: do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. - Use lease protection after rebasing published branches: rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. - Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker: if the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. From 13dab6c2aabd23285fb41792116ea260241aa9ae Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Tue, 1 Sep 2026 18:13:20 +0000 Subject: [PATCH 25/49] self-development: add configuration review safeguards --- AGENTS.md | 2 ++ self-development/agentconfig.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 8ad87d4f6..0dee9d411 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,12 +15,14 @@ - **Protect credentials in configuration and transport.** Values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - **Classify request failures by cause.** When mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases; return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result — propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - **Protect state-dependent transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. +- **Keep auxiliary lease refresh failures from terminating healthy primary work.** For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. - **Revalidate preservation data during API conversion.** Treat annotations or other storage used to carry latest-version fields through older API versions as user-controlled input. Before restoring decoded data into the hub object, validate it against the destination field's full schema, including item and count bounds, patterns, and set uniqueness; do not assume syntactically valid data is schema-valid or that conversion output will be revalidated. Test syntactically valid but out-of-schema preservation values in addition to malformed encodings. - **Test API mutability through updates.** When making an API field mutable or changing transition validation, cover every newly allowed presence transition the contract promises, such as unset-to-set and set-to-unset, and at least one invalid `Update` that proves the remaining validation still applies. Create-only validation tests do not pin update behavior. - **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - **Make component renames upgrade-safe.** Before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. +- **Keep configurable ServiceAccount identities in lockstep.** When a Helm value controls a ServiceAccount name, use it consistently in the workload, the rendered ServiceAccount, and every RBAC subject. Reject names reserved by other chart-managed accounts to avoid duplicate objects, and render-test a non-default valid name plus every reserved name. - **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. - **Import versioned Kelos APIs with aliases.** Prefer aliasing the current versioned API import as `kelos` when a file uses only one API version. Use explicit aliases like `kelosv1alpha1` only when a file genuinely needs multiple versions. - **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 99106d9e3..4d8ac5fb1 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -39,12 +39,14 @@ spec: - Protect credentials in configuration and transport: values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - Classify request failures by cause: when mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases, and return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result; propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - Protect state-dependent transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. + - Keep auxiliary lease refresh failures from terminating healthy primary work: for long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. - Revalidate preservation data during API conversion: treat annotations or other storage used to carry latest-version fields through older API versions as user-controlled input. Before restoring decoded data into the hub object, validate it against the destination field's full schema, including item and count bounds, patterns, and set uniqueness; do not assume syntactically valid data is schema-valid or that conversion output will be revalidated. Test syntactically valid but out-of-schema preservation values in addition to malformed encodings. - Test API mutability through updates: when making an API field mutable or changing transition validation, cover every newly allowed presence transition the contract promises, such as unset-to-set and set-to-unset, and at least one invalid `Update` that proves the remaining validation still applies. Create-only validation tests do not pin update behavior. - Treat controller-generated Kubernetes names as durable identities: validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - Make component renames upgrade-safe: before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. + - Keep configurable ServiceAccount identities in lockstep: when a Helm value controls a ServiceAccount name, use it consistently in the workload, the rendered ServiceAccount, and every RBAC subject. Reject names reserved by other chart-managed accounts to avoid duplicate objects, and render-test a non-default valid name plus every reserved name. - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. - Keep documentation complete and internally consistent: when changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. - Preserve prompt contracts when consolidating agent instructions: before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. From 2b343f44667cd87f22f85c5047f7cd4d95866b01 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Wed, 2 Sep 2026 18:11:39 +0000 Subject: [PATCH 26/49] self-development: add provider and verification safeguards --- AGENTS.md | 3 +++ self-development/agentconfig.yaml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 0dee9d411..848bada35 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,6 +13,8 @@ - **Never use `os.Getenv()` for secrets as Go `flag` defaults.** Go's `flag` package prints default values in usage/help output, which leaks secret values. Instead, use an empty default and read the env var after `flag.Parse()`. - **Fail fast on invalid configuration.** Do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing. Return an error or exit immediately instead of returning nil or empty values that mask the failure. - **Protect credentials in configuration and transport.** Values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. +- **Verify downloaded artifacts fail closed.** When a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. +- **Keep provider integrations isolated end to end.** When adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - **Classify request failures by cause.** When mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases; return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result — propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - **Protect state-dependent transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - **Keep auxiliary lease refresh failures from terminating healthy primary work.** For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. @@ -38,6 +40,7 @@ - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Test every distinct behavior path.** When changed code has early-return or idempotent guards and a primary action, cover the primary action and every contractually distinct guard. For switch, table, or derivation logic, inventory and exercise every relevant source variant; use exact-set assertions when omissions matter. Arrange each case so it reaches the intended path and would fail if that path were removed or bypassed by an earlier condition. Tests must establish their own prerequisites or use explicit shared setup, include every runtime dependency, and remain executable independently. If production code lacks a seam, add one (interface, function field, fake client) so the behavior is testable. - **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. +- **Synchronize state shared with test goroutines.** State written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - **Bound work and persisted state on long-lived data and hot paths.** Do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 4d8ac5fb1..8e5676887 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -37,6 +37,8 @@ spec: - Never use `os.Getenv()` for secrets as Go `flag` defaults: Go's `flag` package prints default values in usage/help output, which leaks secret values; use an empty default and read the env var after `flag.Parse()` - Fail fast on invalid configuration: do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing; return an error or exit immediately - Protect credentials in configuration and transport: values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. + - Verify downloaded artifacts fail closed: when a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. + - Keep provider integrations isolated end to end: when adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - Classify request failures by cause: when mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases, and return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result; propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - Protect state-dependent transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - Keep auxiliary lease refresh failures from terminating healthy primary work: for long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. @@ -59,6 +61,7 @@ spec: - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Test every distinct behavior path: when changed code has early-return or idempotent guards and a primary action, cover the primary action and every contractually distinct guard. For switch, table, or derivation logic, inventory and exercise every relevant source variant; use exact-set assertions when omissions matter. Arrange each case so it reaches the intended path and would fail if that path were removed or bypassed by an earlier condition. Tests must establish their own prerequisites or use explicit shared setup, include every runtime dependency, and remain executable independently. If production code lacks a seam, add one (interface, function field, fake client) so the behavior is testable. - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. + - Synchronize state shared with test goroutines: state written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - Bound work and persisted state on long-lived data and hot paths: do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. From 0a6ffa9e20c9b4e741678922f7ae2d03be4a4298 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Thu, 3 Sep 2026 18:15:30 +0000 Subject: [PATCH 27/49] self-development: add API and CLI completeness checks --- AGENTS.md | 4 +++- self-development/agentconfig.yaml | 4 +++- self-development/kelos-api-reviewer.yaml | 4 ++++ self-development/kelos-reviewer.yaml | 4 ++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 848bada35..76422ddcf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,6 +20,7 @@ - **Keep auxiliary lease refresh failures from terminating healthy primary work.** For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. +- **Validate collection semantics in generated CRDs.** For slice fields with documented allowed values, use item-level kubebuilder validation and inspect `make update` output to confirm the enum appears under `items`. When duplicates have no meaning, enforce set or uniqueness semantics so repeated values cannot duplicate work. Cover invalid and duplicate values in API validation tests. - **Revalidate preservation data during API conversion.** Treat annotations or other storage used to carry latest-version fields through older API versions as user-controlled input. Before restoring decoded data into the hub object, validate it against the destination field's full schema, including item and count bounds, patterns, and set uniqueness; do not assume syntactically valid data is schema-valid or that conversion output will be revalidated. Test syntactically valid but out-of-schema preservation values in addition to malformed encodings. - **Test API mutability through updates.** When making an API field mutable or changing transition validation, cover every newly allowed presence transition the contract promises, such as unset-to-set and set-to-unset, and at least one invalid `Update` that proves the remaining validation still applies. Create-only validation tests do not pin update behavior. - **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. @@ -38,12 +39,13 @@ - **Preserve keyboard operability in dynamic web controls.** Implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. +- **Keep CLI resource detail output aligned with configuration.** When adding or changing a user-visible resource field, update every applicable `get` or `describe` detail printer so configured values and behavior gates remain visible. Compare sibling source or provider branches for parallel fields, and test each added `label: value` line with a fixture value distinct from neighboring fields. - **Test every distinct behavior path.** When changed code has early-return or idempotent guards and a primary action, cover the primary action and every contractually distinct guard. For switch, table, or derivation logic, inventory and exercise every relevant source variant; use exact-set assertions when omissions matter. Arrange each case so it reaches the intended path and would fail if that path were removed or bypassed by an earlier condition. Tests must establish their own prerequisites or use explicit shared setup, include every runtime dependency, and remain executable independently. If production code lacks a seam, add one (interface, function field, fake client) so the behavior is testable. - **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Synchronize state shared with test goroutines.** State written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. -- **Bound work and persisted state on long-lived data and hot paths.** Do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. +- **Bound work and persisted state on long-lived data and hot paths.** Do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Page Kubernetes and other API list calls with limits and continuation tokens when cardinality is not contractually bounded. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. - **Use lease protection after rebasing published branches.** Rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. - **Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker.** If the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 8e5676887..cc3ff435a 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -44,6 +44,7 @@ spec: - Keep auxiliary lease refresh failures from terminating healthy primary work: for long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. + - Validate collection semantics in generated CRDs: for slice fields with documented allowed values, use item-level kubebuilder validation and inspect `make update` output to confirm the enum appears under `items`. When duplicates have no meaning, enforce set or uniqueness semantics so repeated values cannot duplicate work. Cover invalid and duplicate values in API validation tests. - Revalidate preservation data during API conversion: treat annotations or other storage used to carry latest-version fields through older API versions as user-controlled input. Before restoring decoded data into the hub object, validate it against the destination field's full schema, including item and count bounds, patterns, and set uniqueness; do not assume syntactically valid data is schema-valid or that conversion output will be revalidated. Test syntactically valid but out-of-schema preservation values in addition to malformed encodings. - Test API mutability through updates: when making an API field mutable or changing transition validation, cover every newly allowed presence transition the contract promises, such as unset-to-set and set-to-unset, and at least one invalid `Update` that proves the remaining validation still applies. Create-only validation tests do not pin update behavior. - Treat controller-generated Kubernetes names as durable identities: validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. @@ -59,12 +60,13 @@ spec: - Preserve keyboard operability in dynamic web controls: implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. + - Keep CLI resource detail output aligned with configuration: when adding or changing a user-visible resource field, update every applicable `get` or `describe` detail printer so configured values and behavior gates remain visible. Compare sibling source or provider branches for parallel fields, and test each added `label: value` line with a fixture value distinct from neighboring fields. - Test every distinct behavior path: when changed code has early-return or idempotent guards and a primary action, cover the primary action and every contractually distinct guard. For switch, table, or derivation logic, inventory and exercise every relevant source variant; use exact-set assertions when omissions matter. Arrange each case so it reaches the intended path and would fail if that path were removed or bypassed by an earlier condition. Tests must establish their own prerequisites or use explicit shared setup, include every runtime dependency, and remain executable independently. If production code lacks a seam, add one (interface, function field, fake client) so the behavior is testable. - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Synchronize state shared with test goroutines: state written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. - - Bound work and persisted state on long-lived data and hot paths: do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. + - Bound work and persisted state on long-lived data and hot paths: do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Page Kubernetes and other API list calls with limits and continuation tokens when cardinality is not contractually bounded. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. - Use lease protection after rebasing published branches: rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. - Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker: if the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. diff --git a/self-development/kelos-api-reviewer.yaml b/self-development/kelos-api-reviewer.yaml index dc9139e6b..7ee03d926 100644 --- a/self-development/kelos-api-reviewer.yaml +++ b/self-development/kelos-api-reviewer.yaml @@ -206,6 +206,10 @@ spec: **CRD and validation**: - Are kubebuilder validation markers correct and complete? - Are enum validations up to date with all valid values? + - For list fields with documented allowed values, does the generated CRD + place the enum under `items`, and are duplicates rejected when they + have no semantic meaning? Verify the generated YAML, not only the Go + markers. - Do enum docstrings match the `+kubebuilder:validation:Enum` marker? Flag when the godoc invites a value (e.g. "empty matches both") that the marker would then reject — either extend the enum to include diff --git a/self-development/kelos-reviewer.yaml b/self-development/kelos-reviewer.yaml index 02cfcfbbe..ebd8c104d 100644 --- a/self-development/kelos-reviewer.yaml +++ b/self-development/kelos-reviewer.yaml @@ -191,6 +191,10 @@ spec: only the bare value — these often pass vacuously when the value also appears in the resource's `Name` or other fixture context. Require the full `"label: value"` substring (or a regex). P2. + - When a PR adds or changes a user-visible resource field, check every + applicable CLI detail printer for the configured value. Compare + sibling source or provider branches and require a full `label: value` + assertion for each field. P2/P3 depending on operator impact. **Project conventions**: - Logging: messages start with capital letters, do not end with punctuation From 478d8446cb161f7d6e5ebb11fc1f6bb3e8a1b4ef Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Fri, 4 Sep 2026 18:13:17 +0000 Subject: [PATCH 28/49] self-development: add skill and install safeguards --- AGENTS.md | 2 ++ self-development/agentconfig.yaml | 2 ++ self-development/kelos-reviewer.yaml | 11 +++++++++++ 3 files changed, 15 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 76422ddcf..dfdb78707 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,6 +12,7 @@ - **Preserve Unicode semantics in text processing.** When a limit is defined in characters, count Unicode code points rather than bytes. When enforcing byte limits or truncating UTF-8 text, cut only at valid code-point boundaries so user-visible output remains valid UTF-8. Cover affected limits with non-ASCII test cases. - **Never use `os.Getenv()` for secrets as Go `flag` defaults.** Go's `flag` package prints default values in usage/help output, which leaks secret values. Instead, use an empty default and read the env var after `flag.Parse()`. - **Fail fast on invalid configuration.** Do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing. Return an error or exit immediately instead of returning nil or empty values that mask the failure. +- **Verify required filesystem outputs.** Setup and install scripts must validate the contents their downstream consumers require, not only a zero exit status or the existence of an output directory. Fail when a required operation produces no usable artifacts. When moving or copying a directory's contents, preserve dot-prefixed entries unless the format explicitly excludes them. Cover success-with-empty-output and hidden-entry behavior with executable tests where those cases are supported. - **Protect credentials in configuration and transport.** Values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - **Verify downloaded artifacts fail closed.** When a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. - **Keep provider integrations isolated end to end.** When adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. @@ -30,6 +31,7 @@ - **Import versioned Kelos APIs with aliases.** Prefer aliasing the current versioned API import as `kelos` when a file uses only one API version. Use explicit aliases like `kelosv1alpha1` only when a file genuinely needs multiple versions. - **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. - **Keep documentation complete and internally consistent.** When changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. +- **Keep skill discovery metadata aligned with skill behavior.** A skill's frontmatter description is its discovery contract: include distinctive terms users are likely to request, explicitly exclude unrelated uses, and keep that scope consistent with the `SKILL.md` body. When narrowing discovery, preserve the terms needed to trigger every use case the skill still supports. - **Preserve prompt contracts when consolidating agent instructions.** Before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. - **Keep `docs/reference.md` user-facing.** Document API fields, configuration, validation, defaults, and observable operational behavior. Do not describe controller or runtime implementation details unless users need them to configure, operate, or troubleshoot Kelos. Prefer outcomes over mechanisms, and remove nonessential internal detail instead of moving it to a separate architecture document. - **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index cc3ff435a..762db449e 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -36,6 +36,7 @@ spec: - Preserve Unicode semantics in text processing: when a limit is defined in characters, count Unicode code points rather than bytes. When enforcing byte limits or truncating UTF-8 text, cut only at valid code-point boundaries so user-visible output remains valid UTF-8. Cover affected limits with non-ASCII test cases. - Never use `os.Getenv()` for secrets as Go `flag` defaults: Go's `flag` package prints default values in usage/help output, which leaks secret values; use an empty default and read the env var after `flag.Parse()` - Fail fast on invalid configuration: do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing; return an error or exit immediately + - Verify required filesystem outputs: setup and install scripts must validate the contents their downstream consumers require, not only a zero exit status or the existence of an output directory. Fail when a required operation produces no usable artifacts. When moving or copying a directory's contents, preserve dot-prefixed entries unless the format explicitly excludes them. Cover success-with-empty-output and hidden-entry behavior with executable tests where those cases are supported. - Protect credentials in configuration and transport: values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - Verify downloaded artifacts fail closed: when a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. - Keep provider integrations isolated end to end: when adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. @@ -52,6 +53,7 @@ spec: - Keep configurable ServiceAccount identities in lockstep: when a Helm value controls a ServiceAccount name, use it consistently in the workload, the rendered ServiceAccount, and every RBAC subject. Reject names reserved by other chart-managed accounts to avoid duplicate objects, and render-test a non-default valid name plus every reserved name. - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. - Keep documentation complete and internally consistent: when changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. + - Keep skill discovery metadata aligned with skill behavior: a skill's frontmatter description is its discovery contract. Include distinctive terms users are likely to request, explicitly exclude unrelated uses, and keep that scope consistent with the `SKILL.md` body. When narrowing discovery, preserve the terms needed to trigger every use case the skill still supports. - Preserve prompt contracts when consolidating agent instructions: before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - Preserve readable contrast across web UI themes: when changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. diff --git a/self-development/kelos-reviewer.yaml b/self-development/kelos-reviewer.yaml index ebd8c104d..7b82a96c1 100644 --- a/self-development/kelos-reviewer.yaml +++ b/self-development/kelos-reviewer.yaml @@ -168,6 +168,10 @@ spec: (e.g., unauthenticated requests, nil/empty returns that mask the failure)? Flag silent degradation as P1 — operators should see the error, not discover it later from confusing symptoms. + - For setup or install scripts with required outputs, does success prove + the expected artifacts exist rather than only that a command exited + zero or a directory exists? When directory contents are relocated, + are dot-prefixed entries preserved unless explicitly unsupported? **Tests**: - Are there tests for the new/changed behavior? @@ -195,6 +199,9 @@ spec: applicable CLI detail printer for the configured value. Compare sibling source or provider branches and require a full `label: value` assertion for each field. P2/P3 depending on operator impact. + - For executable setup or install scripts, require cases for a successful + command that produces no required artifacts and for hidden entries when + the output format supports them. P2/P3 depending on startup impact. **Project conventions**: - Logging: messages start with capital letters, do not end with punctuation @@ -229,6 +236,10 @@ spec: documenting a Workspace section). A reader of one CRD's reference page should be able to locate any cited field without already knowing the layout of the others. P2. + - For `SKILL.md` changes, treat the frontmatter description as the + discovery contract. Check that it retains distinctive terms for every + still-supported use, excludes unrelated uses, and agrees with the + body's usage and exclusion sections. P2/P3 depending on lost recall. **Documentation completeness**: - When a PR introduces or changes user-facing surface — a new or From 45fa289be68a7851e6632ef73bddc61b24e65a77 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sat, 5 Sep 2026 18:11:11 +0000 Subject: [PATCH 29/49] self-development: verify external contracts in review findings --- self-development/kelos-reviewer.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/self-development/kelos-reviewer.yaml b/self-development/kelos-reviewer.yaml index 7b82a96c1..17ff8bd0b 100644 --- a/self-development/kelos-reviewer.yaml +++ b/self-development/kelos-reviewer.yaml @@ -265,6 +265,13 @@ spec: ### 5. Write findings + Before reporting a defect that depends on an external API or CLI contract, + verify the exact endpoint, response fields, and accepted input forms against + official documentation or upstream implementation for the relevant version. + Do not assume list and detail endpoints return the same fields or that + different clients accept the same URL form. Link the supporting source in + the finding; omit claims whose external contract you cannot verify. + **Which issues to flag.** Flag an issue only if all of these apply: - It meaningfully impacts correctness, performance, security, or maintainability. - It is discrete and actionable — not a vague or compound issue. From d599c8b88f99db97e89c4126163918029ab02c60 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Mon, 7 Sep 2026 18:08:48 +0000 Subject: [PATCH 30/49] self-development: document shared source exclusion criteria --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index dfdb78707..5ef0fb7dd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,6 +5,7 @@ - **Keep changes minimal.** Do not refactor, reorganize, or 'improve' code beyond what was explicitly requested. - **For CI/release workflows**, always use existing Makefile targets rather than reimplementing build logic in YAML. - **Centralize behavior that must stay in lockstep.** When two code paths intentionally implement the same behavior and differ only by an input or action, share the implementation instead of copying it. Route repeated build and generation commands through one Makefile target so their flags cannot drift. Do not abstract incidental similarity. +- **Use shared criteria for new source exclusion filters.** Reuse typed matching criteria and their matcher in source-level `excludeFilters` instead of adding a separate `excludeX` field for each attribute. AND criteria within a rule; any matching exclusion rejects the event regardless of accepting filters. Preserve shipped positive-filter semantics. Validate new exclusion rules so empty criteria or attributes absent from an event cannot silently match everything. Test intended matches, missing attributes, empty values, and exclusion precedence across every supported trigger path. - **Better tests.** Always try to add or improve tests(including integration, e2e) when modifying code. - **Logging conventions.** Start log messages with capital letters and do not end with punctuation. - **Commit messages.** Do not include PR links in commit messages. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 762db449e..771d90434 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -27,6 +27,7 @@ spec: - `make test-integration` — run integration tests - `make build` — build binary - Centralize behavior that must stay in lockstep: when two code paths intentionally implement the same behavior and differ only by an input or action, share the implementation instead of copying it. Route repeated build and generation commands through one Makefile target so their flags cannot drift. Do not abstract incidental similarity. + - Use shared criteria for new source exclusion filters: reuse typed matching criteria and their matcher in source-level `excludeFilters` instead of adding a separate `excludeX` field for each attribute. AND criteria within a rule; any matching exclusion rejects the event regardless of accepting filters. Preserve shipped positive-filter semantics. Validate new exclusion rules so empty criteria or attributes absent from an event cannot silently match everything. Test intended matches, missing attributes, empty values, and exclusion precedence across every supported trigger path. - When polling for a process to finish in shell, do not use `pgrep -f 'CMD_NAME'`: the caller's own argv contains the pattern, so pgrep matches itself and the loop never exits (deadlocks the pod). Either capture the PID at launch (`cmd & PID=$!`) and poll with `kill -0 "$PID"` / `wait "$PID"`, or exclude self from the match (`pgrep -f 'CMD_NAME' | grep -vw $$`). - Always try to add or improve tests when modifying code - Logging conventions: start log messages with capital letters and do not end with punctuation From 66404d702179e5ec6d94a8c8146b6f476977e18c Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Tue, 8 Sep 2026 18:08:52 +0000 Subject: [PATCH 31/49] self-development: document stream backpressure and teardown --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 5ef0fb7dd..96652f62b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,6 +20,7 @@ - **Classify request failures by cause.** When mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases; return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result — propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - **Protect state-dependent transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - **Keep auxiliary lease refresh failures from terminating healthy primary work.** For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. +- **Keep stream control and teardown responsive under backpressure.** A blocked data consumer must not prevent a stream bridge from reading control or disconnect messages. Bound any queue used to decouple them and handle overflow explicitly. On disconnect, cancellation, or remote exit, interrupt blocked pipe and socket I/O and wait for bridge-owned workers to stop before returning. Exercise blocked consumers, control delivery, overflow, and teardown through executable bridge tests that assert worker termination. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. - **Validate collection semantics in generated CRDs.** For slice fields with documented allowed values, use item-level kubebuilder validation and inspect `make update` output to confirm the enum appears under `items`. When duplicates have no meaning, enforce set or uniqueness semantics so repeated values cannot duplicate work. Cover invalid and duplicate values in API validation tests. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 771d90434..fb843c6b5 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -44,6 +44,7 @@ spec: - Classify request failures by cause: when mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases, and return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result; propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - Protect state-dependent transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - Keep auxiliary lease refresh failures from terminating healthy primary work: for long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. + - Keep stream control and teardown responsive under backpressure: a blocked data consumer must not prevent a stream bridge from reading control or disconnect messages. Bound any queue used to decouple them and handle overflow explicitly. On disconnect, cancellation, or remote exit, interrupt blocked pipe and socket I/O and wait for bridge-owned workers to stop before returning. Exercise blocked consumers, control delivery, overflow, and teardown through executable bridge tests that assert worker termination. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. - Validate collection semantics in generated CRDs: for slice fields with documented allowed values, use item-level kubebuilder validation and inspect `make update` output to confirm the enum appears under `items`. When duplicates have no meaning, enforce set or uniqueness semantics so repeated values cannot duplicate work. Cover invalid and duplicate values in API validation tests. From 566667dbabae46c587165ebe03327bc18b748f2b Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Thu, 10 Sep 2026 18:08:46 +0000 Subject: [PATCH 32/49] self-development: clarify history cursor recovery --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 96652f62b..024fe613c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,7 +48,7 @@ - **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Synchronize state shared with test goroutines.** State written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. -- **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. +- **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve valid cursors, in-flight request state, and independently delivered current state. When reconnecting to replacement history or recovering from an expired cursor, clear stale cursors and cached pages so the next request starts at the first page. Test page loading during an active stream, duplicate requests, reconnects, recovery from expired cursors, live-only notifications, and viewport and ordering preservation. - **Bound work and persisted state on long-lived data and hot paths.** Do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Page Kubernetes and other API list calls with limits and continuation tokens when cardinality is not contractually bounded. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. - **Use lease protection after rebasing published branches.** Rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index fb843c6b5..36791bf3e 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -69,7 +69,7 @@ spec: - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Synchronize state shared with test goroutines: state written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve cursors, in-flight request state, and independently delivered current state. Test page loading during an active stream, duplicate requests, reconnects, live-only notifications, and viewport and ordering preservation. + - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve valid cursors, in-flight request state, and independently delivered current state. When reconnecting to replacement history or recovering from an expired cursor, clear stale cursors and cached pages so the next request starts at the first page. Test page loading during an active stream, duplicate requests, reconnects, recovery from expired cursors, live-only notifications, and viewport and ordering preservation. - Bound work and persisted state on long-lived data and hot paths: do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Page Kubernetes and other API list calls with limits and continuation tokens when cardinality is not contractually bounded. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. - Use lease protection after rebasing published branches: rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. From d3a1fd37756057139d4ff5f8e3268e59f547d2c5 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Fri, 11 Sep 2026 18:11:37 +0000 Subject: [PATCH 33/49] self-development: clarify UI resource lifecycle conventions --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 024fe613c..ff06b9afb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -39,7 +39,7 @@ - **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - **Preserve readable contrast across web UI themes.** When changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. - **Audit responsive rules when changing web controls.** When adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. -- **Preserve UI behavior when restructuring web views.** When moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. +- **Preserve UI behavior across view and resource lifecycles.** When moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. For UI features that own terminals or browser notifications, also audit page departure/restoration and Session deletion/recreation. Close resources whose Session identity, including UID, is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration or reopening. - **Preserve keyboard operability in dynamic web controls.** Implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 36791bf3e..df5ee67cf 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -60,7 +60,7 @@ spec: - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - Preserve readable contrast across web UI themes: when changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. - Audit responsive rules when changing web controls: when adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. - - Preserve UI behavior when restructuring web views: when moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. + - Preserve UI behavior across view and resource lifecycles: when moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. For UI features that own terminals or browser notifications, also audit page departure/restoration and Session deletion/recreation. Close resources whose Session identity, including UID, is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration or reopening. - Preserve keyboard operability in dynamic web controls: implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. From 7d04cd959cb9d696121fedb3d42953c33402af56 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Wed, 16 Sep 2026 18:08:27 +0000 Subject: [PATCH 34/49] self-development: require child identity checks when reusing resources --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ff06b9afb..bac7c14a4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,7 +26,7 @@ - **Validate collection semantics in generated CRDs.** For slice fields with documented allowed values, use item-level kubebuilder validation and inspect `make update` output to confirm the enum appears under `items`. When duplicates have no meaning, enforce set or uniqueness semantics so repeated values cannot duplicate work. Cover invalid and duplicate values in API validation tests. - **Revalidate preservation data during API conversion.** Treat annotations or other storage used to carry latest-version fields through older API versions as user-controlled input. Before restoring decoded data into the hub object, validate it against the destination field's full schema, including item and count bounds, patterns, and set uniqueness; do not assume syntactically valid data is schema-valid or that conversion output will be revalidated. Test syntactically valid but out-of-schema preservation values in addition to malformed encodings. - **Test API mutability through updates.** When making an API field mutable or changing transition validation, cover every newly allowed presence transition the contract promises, such as unset-to-set and set-to-unset, and at least one invalid `Update` that proves the remaining validation still applies. Create-only validation tests do not pin update behavior. -- **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. +- **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. On `AlreadyExists`, verify ownership and the intended child's identity (such as stage and index labels) before reusing a resource; reject collisions between different children of the same parent. Cover that collision through a reconciliation test. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - **Make component renames upgrade-safe.** Before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. - **Keep configurable ServiceAccount identities in lockstep.** When a Helm value controls a ServiceAccount name, use it consistently in the workload, the rendered ServiceAccount, and every RBAC subject. Reject names reserved by other chart-managed accounts to avoid duplicate objects, and render-test a non-default valid name plus every reserved name. - **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index df5ee67cf..a51309424 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -50,7 +50,7 @@ spec: - Validate collection semantics in generated CRDs: for slice fields with documented allowed values, use item-level kubebuilder validation and inspect `make update` output to confirm the enum appears under `items`. When duplicates have no meaning, enforce set or uniqueness semantics so repeated values cannot duplicate work. Cover invalid and duplicate values in API validation tests. - Revalidate preservation data during API conversion: treat annotations or other storage used to carry latest-version fields through older API versions as user-controlled input. Before restoring decoded data into the hub object, validate it against the destination field's full schema, including item and count bounds, patterns, and set uniqueness; do not assume syntactically valid data is schema-valid or that conversion output will be revalidated. Test syntactically valid but out-of-schema preservation values in addition to malformed encodings. - Test API mutability through updates: when making an API field mutable or changing transition validation, cover every newly allowed presence transition the contract promises, such as unset-to-set and set-to-unset, and at least one invalid `Update` that proves the remaining validation still applies. Create-only validation tests do not pin update behavior. - - Treat controller-generated Kubernetes names as durable identities: validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. + - Treat controller-generated Kubernetes names as durable identities: validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. On `AlreadyExists`, verify ownership and the intended child's identity (such as stage and index labels) before reusing a resource; reject collisions between different children of the same parent. Cover that collision through a reconciliation test. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - Make component renames upgrade-safe: before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. - Keep configurable ServiceAccount identities in lockstep: when a Helm value controls a ServiceAccount name, use it consistently in the workload, the rendered ServiceAccount, and every RBAC subject. Reject names reserved by other chart-managed accounts to avoid duplicate objects, and render-test a non-default valid name plus every reserved name. - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. From f7e5c53b2ae451edb49b1c48a3ddb747d05c017f Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sat, 19 Sep 2026 04:35:37 +0000 Subject: [PATCH 35/49] self-development: strengthen recent UI review conventions --- AGENTS.md | 6 +++--- self-development/agentconfig.yaml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index bac7c14a4..1fd3aaaa1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,7 @@ - **Verify downloaded artifacts fail closed.** When a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. - **Keep provider integrations isolated end to end.** When adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - **Classify request failures by cause.** When mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases; return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result — propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. -- **Protect state-dependent transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. +- **Protect state-dependent transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. During an asynchronous UI action, preserve the user's in-progress intent until the result is known instead of rerendering controls from stale persisted state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - **Keep auxiliary lease refresh failures from terminating healthy primary work.** For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. - **Keep stream control and teardown responsive under backpressure.** A blocked data consumer must not prevent a stream bridge from reading control or disconnect messages. Bound any queue used to decouple them and handle overflow explicitly. On disconnect, cancellation, or remote exit, interrupt blocked pipe and socket I/O and wait for bridge-owned workers to stop before returning. Exercise blocked consumers, control delivery, overflow, and teardown through executable bridge tests that assert worker termination. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. @@ -40,12 +40,12 @@ - **Preserve readable contrast across web UI themes.** When changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. - **Audit responsive rules when changing web controls.** When adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. - **Preserve UI behavior across view and resource lifecycles.** When moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. For UI features that own terminals or browser notifications, also audit page departure/restoration and Session deletion/recreation. Close resources whose Session identity, including UID, is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration or reopening. -- **Preserve keyboard operability in dynamic web controls.** Implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. +- **Preserve keyboard operability in dynamic web controls.** Implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, close them when focus leaves unless focus is intentionally trapped, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, focus-leaving, and focus restoration in browser-level tests. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - **Keep CLI resource detail output aligned with configuration.** When adding or changing a user-visible resource field, update every applicable `get` or `describe` detail printer so configured values and behavior gates remain visible. Compare sibling source or provider branches for parallel fields, and test each added `label: value` line with a fixture value distinct from neighboring fields. - **Test every distinct behavior path.** When changed code has early-return or idempotent guards and a primary action, cover the primary action and every contractually distinct guard. For switch, table, or derivation logic, inventory and exercise every relevant source variant; use exact-set assertions when omissions matter. Arrange each case so it reaches the intended path and would fail if that path were removed or bypassed by an earlier condition. Tests must establish their own prerequisites or use explicit shared setup, include every runtime dependency, and remain executable independently. If production code lacks a seam, add one (interface, function field, fake client) so the behavior is testable. -- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. +- **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, command shims, and browser test doubles must exercise changed response fields, preserve the relevant platform or protocol semantics, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, add a focused assertion for each behavior-critical declaration in the owning test and assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Synchronize state shared with test goroutines.** State written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve valid cursors, in-flight request state, and independently delivered current state. When reconnecting to replacement history or recovering from an expired cursor, clear stale cursors and cached pages so the next request starts at the first page. Test page loading during an active stream, duplicate requests, reconnects, recovery from expired cursors, live-only notifications, and viewport and ordering preservation. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index a51309424..7b5012dfc 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -42,7 +42,7 @@ spec: - Verify downloaded artifacts fail closed: when a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. - Keep provider integrations isolated end to end: when adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - Classify request failures by cause: when mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases, and return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result; propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - - Protect state-dependent transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. + - Protect state-dependent transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. During an asynchronous UI action, preserve the user's in-progress intent until the result is known instead of rerendering controls from stale persisted state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - Keep auxiliary lease refresh failures from terminating healthy primary work: for long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. - Keep stream control and teardown responsive under backpressure: a blocked data consumer must not prevent a stream bridge from reading control or disconnect messages. Bound any queue used to decouple them and handle overflow explicitly. On disconnect, cancellation, or remote exit, interrupt blocked pipe and socket I/O and wait for bridge-owned workers to stop before returning. Exercise blocked consumers, control delivery, overflow, and teardown through executable bridge tests that assert worker termination. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped @@ -61,12 +61,12 @@ spec: - Preserve readable contrast across web UI themes: when changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. - Audit responsive rules when changing web controls: when adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. - Preserve UI behavior across view and resource lifecycles: when moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. For UI features that own terminals or browser notifications, also audit page departure/restoration and Session deletion/recreation. Close resources whose Session identity, including UID, is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration or reopening. - - Preserve keyboard operability in dynamic web controls: implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, and focus restoration in browser-level tests. + - Preserve keyboard operability in dynamic web controls: implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, close them when focus leaves unless focus is intentionally trapped, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, focus-leaving, and focus restoration in browser-level tests. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. - Keep CLI resource detail output aligned with configuration: when adding or changing a user-visible resource field, update every applicable `get` or `describe` detail printer so configured values and behavior gates remain visible. Compare sibling source or provider branches for parallel fields, and test each added `label: value` line with a fixture value distinct from neighboring fields. - Test every distinct behavior path: when changed code has early-return or idempotent guards and a primary action, cover the primary action and every contractually distinct guard. For switch, table, or derivation logic, inventory and exercise every relevant source variant; use exact-set assertions when omissions matter. Arrange each case so it reaches the intended path and would fail if that path were removed or bypassed by an earlier condition. Tests must establish their own prerequisites or use explicit shared setup, include every runtime dependency, and remain executable independently. If production code lacks a seam, add one (interface, function field, fake client) so the behavior is testable. - - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, and command shims must exercise changed response fields, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. + - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, command shims, and browser test doubles must exercise changed response fields, preserve the relevant platform or protocol semantics, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, add a focused assertion for each behavior-critical declaration in the owning test and assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Synchronize state shared with test goroutines: state written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve valid cursors, in-flight request state, and independently delivered current state. When reconnecting to replacement history or recovering from an expired cursor, clear stale cursors and cached pages so the next request starts at the first page. Test page loading during an active stream, duplicate requests, reconnects, recovery from expired cursors, live-only notifications, and viewport and ordering preservation. From a98001f4ff70be309a8d184ab8012d28f95df5a1 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Tue, 29 Sep 2026 18:12:10 +0000 Subject: [PATCH 36/49] self-development: keep auxiliary failures from breaking primary work Broaden the lease-refresh rule in AGENTS.md and the Kelos development AgentConfig to cover every best-effort enhancement: a failed added lookup, optional-view inspection, or heartbeat degrades only that enhancement with a logged, visible diagnostic and a later retry, instead of aborting startup or discarding a primary result. Invalid configuration and required writes remain fail-fast. Add the matching check to the Kelos reviewer so it weighs this case against the existing fail-fast check. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- self-development/kelos-reviewer.yaml | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1fd3aaaa1..41544daf4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,7 +19,7 @@ - **Keep provider integrations isolated end to end.** When adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - **Classify request failures by cause.** When mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases; return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result — propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - **Protect state-dependent transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. During an asynchronous UI action, preserve the user's in-progress intent until the result is known instead of rerendering controls from stale persisted state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. -- **Keep auxiliary lease refresh failures from terminating healthy primary work.** For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. +- **Keep auxiliary failures from breaking primary work.** When a best-effort enhancement fails, such as an added status lookup or a Git inspection behind an optional view, degrade only that enhancement: keep the primary result, log the failure, report it where the enhancement is shown, and retry on a later request or refresh. Do not let it abort startup or discard a primary result that would otherwise succeed. For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery; log and retry within that window. Invalid configuration and writes the primary path requires remain fail-fast, including at startup. Test the auxiliary failure and assert that primary work continues, and test eventual lease expiry. - **Keep stream control and teardown responsive under backpressure.** A blocked data consumer must not prevent a stream bridge from reading control or disconnect messages. Bound any queue used to decouple them and handle overflow explicitly. On disconnect, cancellation, or remote exit, interrupt blocked pipe and socket I/O and wait for bridge-owned workers to stop before returning. Exercise blocked consumers, control delivery, overflow, and teardown through executable bridge tests that assert worker termination. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 7b5012dfc..997ba441d 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -43,7 +43,7 @@ spec: - Keep provider integrations isolated end to end: when adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - Classify request failures by cause: when mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases, and return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result; propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. - Protect state-dependent transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. During an asynchronous UI action, preserve the user's in-progress intent until the result is known instead of rerendering controls from stale persisted state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - - Keep auxiliary lease refresh failures from terminating healthy primary work: for long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery. Log and retry the refresh within that window, while keeping startup and other required writes fail-fast. Test transient refresh failures as well as eventual lease expiry. + - Keep auxiliary failures from breaking primary work: when a best-effort enhancement fails, such as an added status lookup or a Git inspection behind an optional view, degrade only that enhancement: keep the primary result, log the failure, report it where the enhancement is shown, and retry on a later request or refresh. Do not let it abort startup or discard a primary result that would otherwise succeed. For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery; log and retry within that window. Invalid configuration and writes the primary path requires remain fail-fast, including at startup. Test the auxiliary failure and assert that primary work continues, and test eventual lease expiry. - Keep stream control and teardown responsive under backpressure: a blocked data consumer must not prevent a stream bridge from reading control or disconnect messages. Bound any queue used to decouple them and handle overflow explicitly. On disconnect, cancellation, or remote exit, interrupt blocked pipe and socket I/O and wait for bridge-owned workers to stop before returning. Exercise blocked consumers, control delivery, overflow, and teardown through executable bridge tests that assert worker termination. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. diff --git a/self-development/kelos-reviewer.yaml b/self-development/kelos-reviewer.yaml index 17ff8bd0b..a1bd87979 100644 --- a/self-development/kelos-reviewer.yaml +++ b/self-development/kelos-reviewer.yaml @@ -168,6 +168,12 @@ spec: (e.g., unauthenticated requests, nil/empty returns that mask the failure)? Flag silent degradation as P1 — operators should see the error, not discover it later from confusing symptoms. + - Conversely, can a failure in a best-effort enhancement (an added + lookup, a Git inspection behind an optional view, a heartbeat or + lease refresh) abort startup, tear down a healthy connection, or + discard a primary result that would otherwise succeed? Require + degrading only that enhancement with a logged, visible diagnostic + and a later retry. P2. - For setup or install scripts with required outputs, does success prove the expected artifacts exist rather than only that a command exited zero or a directory exists? When directory contents are relocated, From c514d0d8fa3072994e4b04efe79f1e73fcf012b8 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Wed, 30 Sep 2026 18:09:16 +0000 Subject: [PATCH 37/49] self-development: guarantee waits and lifecycle gates can finish Reviews on three Session runtime and controller changes found waits and lifecycle gates with no guaranteed exit: an interaction's ready channel left open so interrupts stalled until timeout, forced interruption that waited unboundedly on a provider ignoring cancellation, a non-deadline interrupt error that left a draining Session interrupting, a recreated RoleBinding that revoked the access a drain needed, and background or housekeeping tasks that kept a Session active and blocked idle and update drains. Require waiters to be released on every exit path, bounded waits with escalation for external work, gates only on signals that terminate, and tests for failed, canceled, and never-finishing work. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 41544daf4..cc07b79e0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,6 +21,7 @@ - **Protect state-dependent transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. During an asynchronous UI action, preserve the user's in-progress intent until the result is known instead of rerendering controls from stale persisted state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - **Keep auxiliary failures from breaking primary work.** When a best-effort enhancement fails, such as an added status lookup or a Git inspection behind an optional view, degrade only that enhancement: keep the primary result, log the failure, report it where the enhancement is shown, and retry on a later request or refresh. Do not let it abort startup or discard a primary result that would otherwise succeed. For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery; log and retry within that window. Invalid configuration and writes the primary path requires remain fail-fast, including at startup. Test the auxiliary failure and assert that primary work continues, and test eventual lease expiry. - **Keep stream control and teardown responsive under backpressure.** A blocked data consumer must not prevent a stream bridge from reading control or disconnect messages. Bound any queue used to decouple them and handle overflow explicitly. On disconnect, cancellation, or remote exit, interrupt blocked pipe and socket I/O and wait for bridge-owned workers to stop before returning. Exercise blocked consumers, control delivery, overflow, and teardown through executable bridge tests that assert worker termination. +- **Guarantee that waits and lifecycle gates can finish.** Release every channel, pending flag, or waiter an operation owns on every exit path, including errors, cancellation, and completion delivered through another code path. Bound waits on external work that may ignore cancellation, and escalate when the bound expires, such as by restarting the provider. Gate idle, drain, or interrupt transitions only on signals that reach a terminal state: exclude housekeeping work that never finishes, and when user-started work can run indefinitely, apply its gate only to the transitions that must wait for it and document the effect. Keep the access a pending drain or handoff needs until it finishes. Test that failed, canceled, and never-finishing work still releases waiters and lets each transition advance. - **Keep API surfaces minimal.** When adding new API fields, types, or CRD changes, include only what is immediately needed. Do not add speculative fields — API is hard to change once shipped. Start with the minimum viable API and extend in follow-up PRs. - **API changes must preserve backward compatibility for existing manifests.** Existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array, string ↔ object) on an existing field; do not add `MinLength`, `Required`, or other tightening validation to a field that previously accepted absence/empty values; when replacing a field, mark the old one `+deprecated` and keep it functional rather than removing it. When the schema must change, sweep `examples/`, `self-development/`, and any in-tree YAMLs that use the old form and update them in the same PR. - **Validate collection semantics in generated CRDs.** For slice fields with documented allowed values, use item-level kubebuilder validation and inspect `make update` output to confirm the enum appears under `items`. When duplicates have no meaning, enforce set or uniqueness semantics so repeated values cannot duplicate work. Cover invalid and duplicate values in API validation tests. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 997ba441d..75e45e3aa 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -45,6 +45,7 @@ spec: - Protect state-dependent transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. During an asynchronous UI action, preserve the user's in-progress intent until the result is known instead of rerendering controls from stale persisted state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - Keep auxiliary failures from breaking primary work: when a best-effort enhancement fails, such as an added status lookup or a Git inspection behind an optional view, degrade only that enhancement: keep the primary result, log the failure, report it where the enhancement is shown, and retry on a later request or refresh. Do not let it abort startup or discard a primary result that would otherwise succeed. For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery; log and retry within that window. Invalid configuration and writes the primary path requires remain fail-fast, including at startup. Test the auxiliary failure and assert that primary work continues, and test eventual lease expiry. - Keep stream control and teardown responsive under backpressure: a blocked data consumer must not prevent a stream bridge from reading control or disconnect messages. Bound any queue used to decouple them and handle overflow explicitly. On disconnect, cancellation, or remote exit, interrupt blocked pipe and socket I/O and wait for bridge-owned workers to stop before returning. Exercise blocked consumers, control delivery, overflow, and teardown through executable bridge tests that assert worker termination. + - Guarantee that waits and lifecycle gates can finish: release every channel, pending flag, or waiter an operation owns on every exit path, including errors, cancellation, and completion delivered through another code path. Bound waits on external work that may ignore cancellation, and escalate when the bound expires, such as by restarting the provider. Gate idle, drain, or interrupt transitions only on signals that reach a terminal state: exclude housekeeping work that never finishes, and when user-started work can run indefinitely, apply its gate only to the transitions that must wait for it and document the effect. Keep the access a pending drain or handoff needs until it finishes. Test that failed, canceled, and never-finishing work still releases waiters and lets each transition advance. - Keep API surfaces minimal: when adding new API fields, types, or CRD changes, include only what is immediately needed; do not add speculative fields — API is hard to change once shipped - API changes must preserve backward compatibility for existing manifests: existing in-cluster resources must continue to apply after a CRD update. Do not change a field's kind (scalar ↔ array) on an existing field; do not add `MinLength`/`Required` to a field that previously accepted absence/empty values; replace by deprecating (mark `+deprecated`, keep functional) rather than removing. When the schema must change, sweep `examples/` and `self-development/` for YAMLs using the old form and update them in the same PR. - Validate collection semantics in generated CRDs: for slice fields with documented allowed values, use item-level kubebuilder validation and inspect `make update` output to confirm the enum appears under `items`. When duplicates have no meaning, enforce set or uniqueness semantics so repeated values cannot duplicate work. Cover invalid and duplicate values in API validation tests. From 2ba18052a0ccbce29fad9a3e90d621e711436a6a Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Thu, 1 Oct 2026 18:13:15 +0000 Subject: [PATCH 38/49] self-development: clear runtime state across Session suspension Reviews of the Console background-task indicator found live runtime state that outlived the runtime it described: the background-task count stayed visible after the Session was suspended, and resuming restarted the elapsed-time timer for a turn from the stopped runtime before fresh runtime events arrived. Extend the UI lifecycle rule in AGENTS.md and the Kelos development AgentConfig to cover live runtime activity such as progress indicators and Session suspension/resume. Runtime-derived state must be cleared in current and cached views once the runtime is no longer valid, and tests must cover resume before fresh runtime events arrive. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index cc07b79e0..bdb2ca15b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -40,7 +40,7 @@ - **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - **Preserve readable contrast across web UI themes.** When changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. - **Audit responsive rules when changing web controls.** When adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. -- **Preserve UI behavior across view and resource lifecycles.** When moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. For UI features that own terminals or browser notifications, also audit page departure/restoration and Session deletion/recreation. Close resources whose Session identity, including UID, is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration or reopening. +- **Preserve UI behavior across view and resource lifecycles.** When moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. For UI features that own terminals, browser notifications, or live runtime activity such as progress indicators, also audit page departure/restoration, Session suspension/resume, and Session deletion/recreation. Close resources and clear runtime-derived state in current and cached views when the Session identity, including UID, or its runtime is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration, reopening, or resume before fresh runtime events arrive. - **Preserve keyboard operability in dynamic web controls.** Implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, close them when focus leaves unless focus is intentionally trapped, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, focus-leaving, and focus restoration in browser-level tests. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 75e45e3aa..6344e51ac 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -61,7 +61,7 @@ spec: - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - Preserve readable contrast across web UI themes: when changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. - Audit responsive rules when changing web controls: when adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. - - Preserve UI behavior across view and resource lifecycles: when moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. For UI features that own terminals or browser notifications, also audit page departure/restoration and Session deletion/recreation. Close resources whose Session identity, including UID, is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration or reopening. + - Preserve UI behavior across view and resource lifecycles: when moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. For UI features that own terminals, browser notifications, or live runtime activity such as progress indicators, also audit page departure/restoration, Session suspension/resume, and Session deletion/recreation. Close resources and clear runtime-derived state in current and cached views when the Session identity, including UID, or its runtime is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration, reopening, or resume before fresh runtime events arrive. - Preserve keyboard operability in dynamic web controls: implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, close them when focus leaves unless focus is intentionally trapped, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, focus-leaving, and focus restoration in browser-level tests. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. From b1a1a31777c9de19cc4778fae649d4c438548426 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Fri, 2 Oct 2026 18:09:22 +0000 Subject: [PATCH 39/49] self-development: grant only the access a component uses Reviews on three merged PRs found RBAC permissions or credentials that reached beyond the code that used them: cluster-wide Secret list/watch where named gets sufficed, an unused TaskSpawner RBAC marker on the Task controller, a GitHub token inherited by the whole integration-test step, and a privileged ServiceAccount token shared with an OAuth2 Proxy sidecar. Add a least-privilege rule to the project conventions and the Kelos development AgentConfig, and a matching security check to the reviewer prompt, which had raised the sidecar token only as an optional suggestion and missed the CI token. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + self-development/kelos-reviewer.yaml | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index bdb2ca15b..17a8de419 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,6 +15,7 @@ - **Fail fast on invalid configuration.** Do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing. Return an error or exit immediately instead of returning nil or empty values that mask the failure. - **Verify required filesystem outputs.** Setup and install scripts must validate the contents their downstream consumers require, not only a zero exit status or the existence of an output directory. Fail when a required operation produces no usable artifacts. When moving or copying a directory's contents, preserve dot-prefixed entries unless the format explicitly excludes them. Cover success-with-empty-output and hidden-entry behavior with executable tests where those cases are supported. - **Protect credentials in configuration and transport.** Values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. +- **Grant only the access a component uses.** Scope RBAC verbs, resources, and namespaces to the API calls the code actually makes. For example, request `get` on named Secrets rather than cluster-wide `list`/`watch`, and do not add RBAC markers for resources a controller never reads. Expose credentials, including ServiceAccount tokens and token environment variables, only to the container, process, or CI step that uses them. Sidecars and steps that run pull-request code must not inherit them. - **Verify downloaded artifacts fail closed.** When a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. - **Keep provider integrations isolated end to end.** When adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - **Classify request failures by cause.** When mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases; return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result — propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 6344e51ac..53dd74716 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -39,6 +39,7 @@ spec: - Fail fast on invalid configuration: do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing; return an error or exit immediately - Verify required filesystem outputs: setup and install scripts must validate the contents their downstream consumers require, not only a zero exit status or the existence of an output directory. Fail when a required operation produces no usable artifacts. When moving or copying a directory's contents, preserve dot-prefixed entries unless the format explicitly excludes them. Cover success-with-empty-output and hidden-entry behavior with executable tests where those cases are supported. - Protect credentials in configuration and transport: values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. + - Grant only the access a component uses: scope RBAC verbs, resources, and namespaces to the API calls the code actually makes. For example, request `get` on named Secrets rather than cluster-wide `list`/`watch`, and do not add RBAC markers for resources a controller never reads. Expose credentials, including ServiceAccount tokens and token environment variables, only to the container, process, or CI step that uses them. Sidecars and steps that run pull-request code must not inherit them. - Verify downloaded artifacts fail closed: when a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. - Keep provider integrations isolated end to end: when adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - Classify request failures by cause: when mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases, and return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result; propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. diff --git a/self-development/kelos-reviewer.yaml b/self-development/kelos-reviewer.yaml index a1bd87979..d1a8f1e07 100644 --- a/self-development/kelos-reviewer.yaml +++ b/self-development/kelos-reviewer.yaml @@ -229,6 +229,12 @@ spec: default value — Go prints flag defaults in `--help` output, leaking the secret. Require an empty default and reading the env var after `flag.Parse()`. P1. + - Flag RBAC rules, ServiceAccount token mounts, and credential + environment variables that reach beyond the code that uses them: + verbs or resources the code never calls, cluster-wide Secret + `list`/`watch` where named `get` suffices, or a token inherited by a + sidecar or by a CI step that runs pull-request code. P1 when a + credential or Secret is exposed; P2 otherwise. **Documentation accuracy**: - Docs, READMEs, and code comments must describe what the code From 82eb82863bb3628242ff6d1ce1222c476dfcf9fa Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sat, 3 Oct 2026 18:07:36 +0000 Subject: [PATCH 40/49] self-development: recompute geometry-derived UI state on every change Two independent reviews of a Console change found a scroll-position control whose visibility refreshed only from scroll events. It was measured before a scheduled bottom anchor ran, so it flashed visible, and late image loads moved the bottom without a scroll event, so it stayed hidden. An earlier review found the same gap when a hidden view was revealed, which is the only case the existing rule named. Generalize the rule to recompute derived state after deferred programmatic scrolls and late content growth, and to test those changes. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 17a8de419..11ab6de0d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -41,7 +41,7 @@ - **Honor reduced-motion preferences in web UI.** Do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - **Preserve readable contrast across web UI themes.** When changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. - **Audit responsive rules when changing web controls.** When adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. -- **Preserve UI behavior across view and resource lifecycles.** When moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. For UI features that own terminals, browser notifications, or live runtime activity such as progress indicators, also audit page departure/restoration, Session suspension/resume, and Session deletion/recreation. Close resources and clear runtime-derived state in current and cached views when the Session identity, including UID, or its runtime is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration, reopening, or resume before fresh runtime events arrive. +- **Preserve UI behavior across view and resource lifecycles.** When moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, and do not overwrite derived state from hidden layout geometry. Recompute geometry-derived state whenever the underlying geometry changes, not only on scroll events: when the view becomes visible, after a deferred programmatic scroll runs rather than when it is scheduled, and when late-loading content such as images resizes the container. Test the affected scroll interaction, hide/reveal transition, and these geometry changes through executable UI behavior. For UI features that own terminals, browser notifications, or live runtime activity such as progress indicators, also audit page departure/restoration, Session suspension/resume, and Session deletion/recreation. Close resources and clear runtime-derived state in current and cached views when the Session identity, including UID, or its runtime is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration, reopening, or resume before fresh runtime events arrive. - **Preserve keyboard operability in dynamic web controls.** Implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, close them when focus leaves unless focus is intentionally trapped, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, focus-leaving, and focus restoration in browser-level tests. - **Do not use Gomega's global `Expect()` inside `Eventually` polling blocks.** Gomega does not retry on `Expect` failures — a transient API error short-circuits the poller and fails the test on the first blip instead of retrying. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - **CLI error messages must name the resource.** When a CLI command fails for a named resource (task, spawner, workspace, etc.), include the resource name in the returned error so operators piping or batching invocations get an actionable signal. `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 53dd74716..9f8720a64 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -62,7 +62,7 @@ spec: - Honor reduced-motion preferences in web UI: do not add continuous or non-essential animation without a `prefers-reduced-motion` fallback. Keep the static state distinguishable when animation is disabled; prefer gating animation under `@media (prefers-reduced-motion: no-preference)`. - Preserve readable contrast across web UI themes: when changing colors, theme tokens, or control styling, verify text, interactive labels, and focus indicators in every supported theme against the applicable WCAG AA contrast requirement. Do not assume a light-theme color remains legible on a dark background. - Audit responsive rules when changing web controls: when adding or renaming a form field or control, include it in every applicable selector group and breakpoint override. In particular, preserve mobile font sizing that prevents focus zoom, touch-target sizing, and alignment with adjacent controls. When a breakpoint hides or replaces a control, keep every supported action available through an equivalent touch-operable path and test that path. - - Preserve UI behavior across view and resource lifecycles: when moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, do not overwrite derived state from hidden layout geometry, and recompute it when the view becomes visible. Test the affected scroll interaction and hide/reveal transition through executable UI behavior. For UI features that own terminals, browser notifications, or live runtime activity such as progress indicators, also audit page departure/restoration, Session suspension/resume, and Session deletion/recreation. Close resources and clear runtime-derived state in current and cached views when the Session identity, including UID, or its runtime is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration, reopening, or resume before fresh runtime events arrive. + - Preserve UI behavior across view and resource lifecycles: when moving scroll ownership, hiding or revealing a view, or replacing a DOM container, inventory every event listener and geometry- or visibility-derived state tied to the affected elements. Bind behavior to the element that actually scrolls, and do not overwrite derived state from hidden layout geometry. Recompute geometry-derived state whenever the underlying geometry changes, not only on scroll events: when the view becomes visible, after a deferred programmatic scroll runs rather than when it is scheduled, and when late-loading content such as images resizes the container. Test the affected scroll interaction, hide/reveal transition, and these geometry changes through executable UI behavior. For UI features that own terminals, browser notifications, or live runtime activity such as progress indicators, also audit page departure/restoration, Session suspension/resume, and Session deletion/recreation. Close resources and clear runtime-derived state in current and cached views when the Session identity, including UID, or its runtime is no longer valid, and leave views and controls usable after teardown. Test cleanup and restoration, reopening, or resume before fresh runtime events arrive. - Preserve keyboard operability in dynamic web controls: implement the expected keyboard interaction model for custom widgets. When tabs, menu items, or similar controls use roving `tabindex`, add Arrow-key and Home/End navigation so every item remains reachable. For menus, dialogs, or actions that close or rerender their trigger, keep a clearly visible `:focus-visible` indicator, dismiss overlays through expected keyboard navigation such as Escape and Tab, close them when focus leaves unless focus is intentionally trapped, and restore focus to the current replacement trigger after success and failure. Exercise keyboard activation, navigation, dismissal, focus-leaving, and focus restoration in browser-level tests. - Do not use Gomega's global `Expect()` inside `Eventually` polling blocks: Gomega does not retry on `Expect` failures, so a transient API error short-circuits the poller. In e2e `WaitFor*` helpers, either inline the call and return a zero-value on error, or use the `Eventually(func(g Gomega) { ... })` form so failed assertions are caught and retried. - CLI error messages must name the resource: when a CLI command fails for a named resource (task, spawner, workspace, etc.), include the name in the returned error so operators piping or batching invocations get an actionable signal — `fmt.Errorf("task %s failed", name)`, not `errors.New("task failed")`. From ab3a33fbc412cf7cc4c0561bd922d6a695d1f4fe Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Wed, 7 Oct 2026 18:10:45 +0000 Subject: [PATCH 41/49] self-development: document residual security risk of access changes Kelos reviews on three recent PRs found that docs for new access or authentication features omitted risks operators must manage: a Session agent could switch its own ServiceAccount, a compromised Console could create RoleBindings granting permissions it holds, and removing a user from a GitHub organization or team left existing Console logins active. Each author fixed the gap by documenting it. Require docs for changes that grant permissions, authenticate users, or accept a trust boundary to state what a compromised component or permitted identity can reach and how existing access is revoked. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 11ab6de0d..3c9cbe4dc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,7 +33,7 @@ - **Keep configurable ServiceAccount identities in lockstep.** When a Helm value controls a ServiceAccount name, use it consistently in the workload, the rendered ServiceAccount, and every RBAC subject. Reject names reserved by other chart-managed accounts to avoid duplicate objects, and render-test a non-default valid name plus every reserved name. - **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. - **Import versioned Kelos APIs with aliases.** Prefer aliasing the current versioned API import as `kelos` when a file uses only one API version. Use explicit aliases like `kelosv1alpha1` only when a file genuinely needs multiple versions. -- **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. +- **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. For changes that grant permissions, authenticate users, or accept a trust boundary, also document the residual risk operators must manage: what a compromised component or permitted identity can reach, including access it can grant to others, and whether removing upstream access ends existing sessions and how to revoke them. - **Keep documentation complete and internally consistent.** When changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. - **Keep skill discovery metadata aligned with skill behavior.** A skill's frontmatter description is its discovery contract: include distinctive terms users are likely to request, explicitly exclude unrelated uses, and keep that scope consistent with the `SKILL.md` body. When narrowing discovery, preserve the terms needed to trigger every use case the skill still supports. - **Preserve prompt contracts when consolidating agent instructions.** Before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 9f8720a64..801e7d2c9 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -55,7 +55,7 @@ spec: - Treat controller-generated Kubernetes names as durable identities: validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. On `AlreadyExists`, verify ownership and the intended child's identity (such as stage and index labels) before reusing a resource; reject collisions between different children of the same parent. Cover that collision through a reconciliation test. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - Make component renames upgrade-safe: before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. - Keep configurable ServiceAccount identities in lockstep: when a Helm value controls a ServiceAccount name, use it consistently in the workload, the rendered ServiceAccount, and every RBAC subject. Reject names reserved by other chart-managed accounts to avoid duplicate objects, and render-test a non-default valid name plus every reserved name. - - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. + - Docs must match implementation, not aspiration: describe only what the code actually does; do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Verify the code enforces a contract before documenting it. For changes that grant permissions, authenticate users, or accept a trust boundary, also document the residual risk operators must manage: what a compromised component or permitted identity can reach, including access it can grant to others, and whether removing upstream access ends existing sessions and how to revoke them. - Keep documentation complete and internally consistent: when changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. - Keep skill discovery metadata aligned with skill behavior: a skill's frontmatter description is its discovery contract. Include distinctive terms users are likely to request, explicitly exclude unrelated uses, and keep that scope consistent with the `SKILL.md` body. When narrowing discovery, preserve the terms needed to trigger every use case the skill still supports. - Preserve prompt contracts when consolidating agent instructions: before removing or shortening a TaskSpawner or SessionSpawner prompt because behavior is shared, map every removed requirement to a concrete instruction source that the spawned agent actually loads, such as a referenced AgentConfig, repository `AGENTS.md`, or skill. Retain requirements with no loaded owner, and update prompt-contract tests and every README section that claims the behavior in the same change. From a97837dfee2fd8e5afb9261c6a8b29c76e1fdd2d Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Thu, 8 Oct 2026 18:07:13 +0000 Subject: [PATCH 42/49] self-development: preserve latest-only fields through older-version conversion Two PRs added v1alpha2-only fields whose values a v1alpha1 read-modify-write could drop. One preserved the field only on TaskSpawner templates and missed the Task itself, where immutable-spec validation then rejects the write-back. The other left new Task status fields unpreserved. Extend the latest-version API rule to require preserving such fields on every kind that carries them and testing the round trip for each kind. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 3c9cbe4dc..7d8e7b634 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,7 +31,7 @@ - **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. On `AlreadyExists`, verify ownership and the intended child's identity (such as stage and index labels) before reusing a resource; reject collisions between different children of the same parent. Cover that collision through a reconciliation test. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - **Make component renames upgrade-safe.** Before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. - **Keep configurable ServiceAccount identities in lockstep.** When a Helm value controls a ServiceAccount name, use it consistently in the workload, the rendered ServiceAccount, and every RBAC subject. Reject names reserved by other chart-managed accounts to avoid duplicate objects, and render-test a non-default valid name plus every reserved name. -- **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. +- **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. Preserve each latest-only field through older-version conversion on every kind that carries it, including spec, status, and embedded templates, so a read-modify-write through an older version cannot drop it. Test the round trip for each kind. - **Import versioned Kelos APIs with aliases.** Prefer aliasing the current versioned API import as `kelos` when a file uses only one API version. Use explicit aliases like `kelosv1alpha1` only when a file genuinely needs multiple versions. - **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. For changes that grant permissions, authenticate users, or accept a trust boundary, also document the residual risk operators must manage: what a compromised component or permitted identity can reach, including access it can grant to others, and whether removing upstream access ends existing sessions and how to revoke them. - **Keep documentation complete and internally consistent.** When changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. From c819a661ea07869613f0760f3a5b2dcf24c206bf Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Fri, 16 Oct 2026 18:07:42 +0000 Subject: [PATCH 43/49] self-development: bound every field of size-limited messages and objects Reviews found unbounded fields that could push a size-limited payload over its limit: a workspace.changes event over the Console bridge's 8 MiB line limit, and per-user Session annotations that grow until the annotation limit rejects every write. The bounded-work rule covered only CR status aggregation. It now covers any message or object with a fixed size limit, requires counting every variable-length field's encoded size in one budget, and requires pruning or capping entries that accumulate. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 7d8e7b634..debaed379 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,7 +51,7 @@ - **Synchronize state shared with test goroutines.** State written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve valid cursors, in-flight request state, and independently delivered current state. When reconnecting to replacement history or recovering from an expired cursor, clear stale cursors and cached pages so the next request starts at the first page. Test page loading during an active stream, duplicate requests, reconnects, recovery from expired cursors, live-only notifications, and viewport and ordering preservation. -- **Bound work and persisted state on long-lived data and hot paths.** Do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Page Kubernetes and other API list calls with limits and continuation tokens when cardinality is not contractually bounded. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. +- **Bound work and persisted state on long-lived data and hot paths.** Do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Page Kubernetes and other API list calls with limits and continuation tokens when cardinality is not contractually bounded. Keep each message or object with a fixed size limit, such as a streamed event line, CR status, or a resource's annotations, within that limit at the maximum cardinality of every variable-length field it carries: count each field's encoded size in one shared budget, prune or cap entries that accumulate per user or per child, and store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. - **Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests.** When asserting a `label: value` line is emitted, match against the full `"label: value"` string (or a regex), not the bare value — bare values often collide with the resource's `Name` or other surrounding context in the fixture and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a per-line substring check still passes when lines are dropped, duplicated, or reordered. - **Use lease protection after rebasing published branches.** Rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. - **Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker.** If the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 801e7d2c9..156a9a01e 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -72,7 +72,7 @@ spec: - Synchronize state shared with test goroutines: state written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve valid cursors, in-flight request state, and independently delivered current state. When reconnecting to replacement history or recovering from an expired cursor, clear stale cursors and cached pages so the next request starts at the first page. Test page loading during an active stream, duplicate requests, reconnects, recovery from expired cursors, live-only notifications, and viewport and ordering preservation. - - Bound work and persisted state on long-lived data and hot paths: do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Page Kubernetes and other API list calls with limits and continuation tokens when cardinality is not contractually bounded. Keep CR status aggregation bounded at the API's maximum child cardinality and payload sizes; store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. + - Bound work and persisted state on long-lived data and hot paths: do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Page Kubernetes and other API list calls with limits and continuation tokens when cardinality is not contractually bounded. Keep each message or object with a fixed size limit, such as a streamed event line, CR status, or a resource's annotations, within that limit at the maximum cardinality of every variable-length field it carries: count each field's encoded size in one shared budget, prune or cap entries that accumulate per user or per child, and store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. - Avoid vacuous substring assertions in printer/formatter and stream-forwarding tests: when asserting a `label: value` line is emitted, match the full `"label: value"` string (or a regex), not the bare value — bare values frequently collide with the fixture's `Name` or surrounding context and pass even when the line is missing. When asserting that a writer or pipe forwards its input, compare the full normalized output for byte equality, not `strings.Contains` per line — a substring check still passes when lines are dropped, duplicated, or reordered. - Use lease protection after rebasing published branches: rebasing rewrites commit IDs, so push an already-published rebased branch with `git push --force-with-lease`, never `--force`. A normal push will be rejected as non-fast-forward, while an unleased force-push can overwrite concurrent remote work. - Keep CRD enum docstrings consistent with the `+kubebuilder:validation:Enum` marker: if the godoc says "empty matches both", either include `""` in the enum list so `field: ""` is accepted, or rephrase to "Omit to match both" so no one writes the explicit empty form. A docstring that invites a value the API server then rejects is a worse contract than either alternative. From 963bf25490c9fe09e5a3c391c3a5394b1f91ad21 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Mon, 19 Oct 2026 18:09:54 +0000 Subject: [PATCH 44/49] self-development: retry only failures that can clear on their own Reviews and a production defect found components that retried failures that could not succeed until an input changed. Reconcilers returned errors for an unsupported spec, a permanently invalid Task, and a foreign Service, so they requeued with backoff indefinitely. The Slack reporter re-posted a permanently rejected reply on every 30-second cycle. The Console re-sent read acknowledgments rejected with 403 on every poll. The existing rule covered only propagating transient errors so senders retry. Add the converse: report a permanent failure once where operators see it and stop retrying until the input changes. Reconcilers return without an error and re-enqueue from a watch. A referenced object that does not exist yet remains a wait with a fixed requeue, transient failures stay retryable, and tests cover both kinds. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index debaed379..fff30f6c1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,6 +19,7 @@ - **Verify downloaded artifacts fail closed.** When a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. - **Keep provider integrations isolated end to end.** When adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - **Classify request failures by cause.** When mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases; return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result — propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. +- **Retry only failures that can clear on their own.** When an operation cannot succeed until its spec, configuration, or permissions change, such as an unsupported spec or a permanent API rejection (for example a `400` or `403` response, or Slack's `cannot_reply_to_message`), report the failure once where operators see it, such as the affected resource's status or a Warning event, and stop retrying until that input changes. In reconcilers, return without an error so the object is not requeued with backoff, and let a watch on that input re-enqueue it. A referenced object that does not exist yet is not such a failure; requeue after a fixed delay without an error. Keep conflicts, rate limits, server errors, and network failures retryable. Test that a permanent failure is not attempted again and that a transient failure still is. - **Protect state-dependent transitions from stale work.** After asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. During an asynchronous UI action, preserve the user's in-progress intent until the result is known instead of rerendering controls from stale persisted state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - **Keep auxiliary failures from breaking primary work.** When a best-effort enhancement fails, such as an added status lookup or a Git inspection behind an optional view, degrade only that enhancement: keep the primary result, log the failure, report it where the enhancement is shown, and retry on a later request or refresh. Do not let it abort startup or discard a primary result that would otherwise succeed. For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery; log and retry within that window. Invalid configuration and writes the primary path requires remain fail-fast, including at startup. Test the auxiliary failure and assert that primary work continues, and test eventual lease expiry. - **Keep stream control and teardown responsive under backpressure.** A blocked data consumer must not prevent a stream bridge from reading control or disconnect messages. Bound any queue used to decouple them and handle overflow explicitly. On disconnect, cancellation, or remote exit, interrupt blocked pipe and socket I/O and wait for bridge-owned workers to stop before returning. Exercise blocked consumers, control delivery, overflow, and teardown through executable bridge tests that assert worker termination. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 156a9a01e..103de9249 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -43,6 +43,7 @@ spec: - Verify downloaded artifacts fail closed: when a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. - Keep provider integrations isolated end to end: when adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - Classify request failures by cause: when mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases, and return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result; propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. + - Retry only failures that can clear on their own: when an operation cannot succeed until its spec, configuration, or permissions change, such as an unsupported spec or a permanent API rejection (for example a `400` or `403` response, or Slack's `cannot_reply_to_message`), report the failure once where operators see it, such as the affected resource's status or a Warning event, and stop retrying until that input changes. In reconcilers, return without an error so the object is not requeued with backoff, and let a watch on that input re-enqueue it. A referenced object that does not exist yet is not such a failure; requeue after a fixed delay without an error. Keep conflicts, rate limits, server errors, and network failures retryable. Test that a permanent failure is not attempted again and that a transient failure still is. - Protect state-dependent transitions from stale work: after asynchronous work completes, re-read current state instead of rendering or restoring a captured pre-request snapshot, and verify the operation still belongs to the current resource, Session, dialog, or request generation before mutating shared state. During an asynchronous UI action, preserve the user's in-progress intent until the result is known instead of rerendering controls from stale persisted state. When lifecycle signals can disagree or lag, inspect terminal and failure evidence before returning a transient or retry decision. Derive completion, interruption, and control behavior from the exact active operation's identity and kind, not provider-global state. Coordinate startup, handoff, and destructive lifecycle transitions so stale notifications or newly accepted work cannot act on a different operation. - Keep auxiliary failures from breaking primary work: when a best-effort enhancement fails, such as an added status lookup or a Git inspection behind an optional view, degrade only that enhancement: keep the primary result, log the failure, report it where the enhancement is shown, and retry on a later request or refresh. Do not let it abort startup or discard a primary result that would otherwise succeed. For long-lived connections, a heartbeat or lease write that only extends secondary liveness state must not tear down the primary data path on a single transient control-plane failure when the lease window permits recovery; log and retry within that window. Invalid configuration and writes the primary path requires remain fail-fast, including at startup. Test the auxiliary failure and assert that primary work continues, and test eventual lease expiry. - Keep stream control and teardown responsive under backpressure: a blocked data consumer must not prevent a stream bridge from reading control or disconnect messages. Bound any queue used to decouple them and handle overflow explicitly. On disconnect, cancellation, or remote exit, interrupt blocked pipe and socket I/O and wait for bridge-owned workers to stop before returning. Exercise blocked consumers, control delivery, overflow, and teardown through executable bridge tests that assert worker termination. From 5cee024a69316448610838294d3b1ebc1fd6acbb Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Sat, 24 Oct 2026 18:09:55 +0000 Subject: [PATCH 45/49] self-development: keep test outcomes independent of real time Reviews found tests whose result depended on the real clock. One compared a hard-coded date with time.Now, so it passed only after that date. Others waited for asynchronous work with fixed sleeps, which fail on a slow runner and can pass before the behavior under test happens. Authors replaced them with clock-relative fixtures, Consistently, or a deadline-bounded poll. An agent-authored change shipped with the sleeps, and the Kelos reviewer approved both that change and the hard-coded date. Require clock-relative fixtures or an injected clock and condition-based waits, and add the check to the reviewer at P2. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 1 + self-development/agentconfig.yaml | 1 + self-development/kelos-reviewer.yaml | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index fff30f6c1..c22d31c90 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -50,6 +50,7 @@ - **Test every distinct behavior path.** When changed code has early-return or idempotent guards and a primary action, cover the primary action and every contractually distinct guard. For switch, table, or derivation logic, inventory and exercise every relevant source variant; use exact-set assertions when omissions matter. Arrange each case so it reaches the intended path and would fail if that path were removed or bypassed by an earlier condition. Tests must establish their own prerequisites or use explicit shared setup, include every runtime dependency, and remain executable independently. If production code lacks a seam, add one (interface, function field, fake client) so the behavior is testable. - **Test behavior through the layer that owns it.** Do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, command shims, and browser test doubles must exercise changed response fields, preserve the relevant platform or protocol semantics, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, add a focused assertion for each behavior-critical declaration in the owning test and assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - **Synchronize state shared with test goroutines.** State written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. +- **Keep test outcomes independent of real time.** When code under test compares timestamps with the current clock, derive fixtures from `time.Now()` or inject a clock instead of hard-coding dates, so the result does not depend on the date the test runs. Wait for asynchronous effects with `Eventually`, `Consistently`, or a deadline-bounded poll on an observable condition instead of a fixed sleep: a fixed sleep fails on a slow runner and can pass before the behavior under test has happened. - **Keep shared feature contracts consistent across clients.** When the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - **Separate historical replay from live event handling.** When a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve valid cursors, in-flight request state, and independently delivered current state. When reconnecting to replacement history or recovering from an expired cursor, clear stale cursors and cached pages so the next request starts at the first page. Test page loading during an active stream, duplicate requests, reconnects, recovery from expired cursors, live-only notifications, and viewport and ordering preservation. - **Bound work and persisted state on long-lived data and hot paths.** Do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Page Kubernetes and other API list calls with limits and continuation tokens when cardinality is not contractually bounded. Keep each message or object with a fixed size limit, such as a streamed event line, CR status, or a resource's annotations, within that limit at the maximum cardinality of every variable-length field it carries: count each field's encoded size in one shared budget, prune or cap entries that accumulate per user or per child, and store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 103de9249..834306bc4 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -71,6 +71,7 @@ spec: - Test every distinct behavior path: when changed code has early-return or idempotent guards and a primary action, cover the primary action and every contractually distinct guard. For switch, table, or derivation logic, inventory and exercise every relevant source variant; use exact-set assertions when omissions matter. Arrange each case so it reaches the intended path and would fail if that path were removed or bypassed by an earlier condition. Tests must establish their own prerequisites or use explicit shared setup, include every runtime dependency, and remain executable independently. If production code lacks a seam, add one (interface, function field, fake client) so the behavior is testable. - Test behavior through the layer that owns it: do not rely only on helper tests or source-fragment assertions when behavior depends on controller scheduling, rendered configuration, or stateful UI interactions. Exercise the behavior through `Reconcile`, rendered output, or executable UI tests, and cover non-default configuration branches that change the result. For state-dependent decision logic, cover each relevant terminal, transient, and unknown class plus combinations where independently updated status sources lag one another. Assert the primary effect directly, such as verifying that a process exited or that exact identifiers reached a fake, instead of relying only on logs, journals, or status that can succeed independently. Fakes, fixtures, command shims, and browser test doubles must exercise changed response fields, preserve the relevant platform or protocol semantics, validate the complete request shape, reject unexpected calls, and expose call counts when batching or request reduction is part of the contract. Populate production identity fields used as keys, and use multi-element fixtures when behavior depends on collection membership or aggregation; empty identities and singleton-only cases can let keyed or set-based logic pass vacuously. Tests that start goroutines or child processes must make blocking operations cancellation-aware and verify cleanup before returning so background work cannot leak after a passing assertion. When static-source assertions are appropriate, such as for CSS rules, add a focused assertion for each behavior-critical declaration in the owning test and assert behavior-relevant declarations and the absence of conflicting declarations instead of matching an exact serialized or minified block. - Synchronize state shared with test goroutines: state written by an `httptest` handler or another test goroutine and read by the test must use a mutex, atomics, or channels. Do not rely on an HTTP request completing as synchronization for unrelated shared variables; keep these tests clean under the race detector. + - Keep test outcomes independent of real time: when code under test compares timestamps with the current clock, derive fixtures from `time.Now()` or inject a clock instead of hard-coding dates, so the result does not depend on the date the test runs. Wait for asynchronous effects with `Eventually`, `Consistently`, or a deadline-bounded poll on an observable condition instead of a fixed sleep: a fixed sleep fails on a slow runner and can pass before the behavior under test has happened. - Keep shared feature contracts consistent across clients: when the web UI, TUI, and plain CLI expose the same feature, apply the same limits, empty-input rules, failure semantics, and discoverability. Test each surface's contract instead of assuming shared backend validation makes the clients equivalent. - Separate historical replay from live event handling: when a UI or CLI paginates or replays retained events while receiving live events, keep replay rendering and state separate from the live stream and its live-only side effects. Preserve valid cursors, in-flight request state, and independently delivered current state. When reconnecting to replacement history or recovering from an expired cursor, clear stale cursors and cached pages so the next request starts at the first page. Test page loading during an active stream, duplicate requests, reconnects, recovery from expired cursors, live-only notifications, and viewport and ordering preservation. - Bound work and persisted state on long-lived data and hot paths: do not read unbounded logs, history, or streams into memory; use server-side limits or incremental processing. Page Kubernetes and other API list calls with limits and continuation tokens when cardinality is not contractually bounded. Keep each message or object with a fixed size limit, such as a streamed event line, CR status, or a resource's annotations, within that limit at the maximum cardinality of every variable-length field it carries: count each field's encoded size in one shared budget, prune or cap entries that accumulate per user or per child, and store references or explicitly truncated summaries instead of copying every child output into the parent object. Before nested matching, restrict candidates to the resource kinds that can contribute. Coalesce high-frequency UI work such as scroll and resize geometry to animation frames, and skip unchanged DOM updates. Test the bound, truncation, or throttling behavior with realistic input sizes. diff --git a/self-development/kelos-reviewer.yaml b/self-development/kelos-reviewer.yaml index d1a8f1e07..d3a976c58 100644 --- a/self-development/kelos-reviewer.yaml +++ b/self-development/kelos-reviewer.yaml @@ -189,6 +189,11 @@ spec: retry on `Expect` failures, so a transient API error short-circuits the poller. Either inline the call and return a zero value on error, or use the `Eventually(func(g Gomega) { ... })` form. P2. + - Flag tests whose outcome depends on real time: hard-coded dates that + the code under test compares with the current clock, or a fixed sleep + used to wait for asynchronous work. Require clock-relative fixtures + or an injected clock, and `Eventually`, `Consistently`, or a + deadline-bounded poll on an observable condition. P2. - When a handler has both early-return guards and a primary action (post a message, create a resource, emit a metric), flag tests that cover only the no-op branches and leave the happy path untested. From eb9bb3558dded4f52d5942f44e1ecd8156dab9bf Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Mon, 26 Oct 2026 18:07:58 +0000 Subject: [PATCH 46/49] self-development: allow documented drops of latest-only fields The maintainer kept new AgentConfig skill files out of v1alpha1 instead of preserving them through conversion, and the merged change states the drop in the reference. Several other v1alpha2-only webhook filter criteria are already documented as not preserved across a v1alpha1 round trip. Keep preservation as the default, but when the maintainer decides not to carry a field through older versions, skip the preservation and document the drop in the field's reference entry. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index c22d31c90..76ca4827f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,7 +32,7 @@ - **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. On `AlreadyExists`, verify ownership and the intended child's identity (such as stage and index labels) before reusing a resource; reject collisions between different children of the same parent. Cover that collision through a reconciliation test. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - **Make component renames upgrade-safe.** Before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. - **Keep configurable ServiceAccount identities in lockstep.** When a Helm value controls a ServiceAccount name, use it consistently in the workload, the rendered ServiceAccount, and every RBAC subject. Reject names reserved by other chart-managed accounts to avoid duplicate objects, and render-test a non-default valid name plus every reserved name. -- **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. Preserve each latest-only field through older-version conversion on every kind that carries it, including spec, status, and embedded templates, so a read-modify-write through an older version cannot drop it. Test the round trip for each kind. +- **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. Preserve each latest-only field through older-version conversion on every kind that carries it, including spec, status, and embedded templates, so a read-modify-write through an older version cannot drop it. Test the round trip for each kind. When the maintainer decides not to carry a field through older versions, skip its preservation and state in the field's `docs/reference.md` entry that reads through an older version omit the field and writes through one remove it. - **Import versioned Kelos APIs with aliases.** Prefer aliasing the current versioned API import as `kelos` when a file uses only one API version. Use explicit aliases like `kelosv1alpha1` only when a file genuinely needs multiple versions. - **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. For changes that grant permissions, authenticate users, or accept a trust boundary, also document the residual risk operators must manage: what a compromised component or permitted identity can reach, including access it can grant to others, and whether removing upstream access ends existing sessions and how to revoke them. - **Keep documentation complete and internally consistent.** When changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. From e1cfd968da0d9be6ed48ac03da37a880e92aee2a Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Tue, 27 Oct 2026 18:09:45 +0000 Subject: [PATCH 47/49] self-development: scope delegated user actions and Secret reads Reviews of two Console changes found RBAC broader than the operation, and the maintainer narrowed the grant both times instead of documenting it. One gave the Console ServiceAccount cluster-wide get on Secrets so it could read context-source Secrets. The fix removed the grant and has operators grant get on named Secrets in the TaskSpawner namespace. The other required and granted users general TaskSpawner patch so the Console could toggle one field. The fix checks dedicated suspend and resume authorization subresources, as Session lifecycle actions already do, and keeps patch only on the ServiceAccount. The rule's Secret example and the reviewer check named only cluster-wide list/watch, and the rule said nothing about which permission to require from users when a server acts for them. The Kelos reviewer reported the Secret grant only as a P2 README wording issue because the ServiceAccount already had cluster-wide pods/exec. Cover cluster-wide Secret get and action-scoped user authorization, and keep the reviewer from lowering priority because of other broad access. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- self-development/agentconfig.yaml | 2 +- self-development/kelos-reviewer.yaml | 10 ++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 76ca4827f..8b52e26b3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,7 +15,7 @@ - **Fail fast on invalid configuration.** Do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing. Return an error or exit immediately instead of returning nil or empty values that mask the failure. - **Verify required filesystem outputs.** Setup and install scripts must validate the contents their downstream consumers require, not only a zero exit status or the existence of an output directory. Fail when a required operation produces no usable artifacts. When moving or copying a directory's contents, preserve dot-prefixed entries unless the format explicitly excludes them. Cover success-with-empty-output and hidden-entry behavior with executable tests where those cases are supported. - **Protect credentials in configuration and transport.** Values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. -- **Grant only the access a component uses.** Scope RBAC verbs, resources, and namespaces to the API calls the code actually makes. For example, request `get` on named Secrets rather than cluster-wide `list`/`watch`, and do not add RBAC markers for resources a controller never reads. Expose credentials, including ServiceAccount tokens and token environment variables, only to the container, process, or CI step that uses them. Sidecars and steps that run pull-request code must not inherit them. +- **Grant only the access a component uses.** Scope RBAC verbs, resources, and namespaces to the API calls the code actually makes. For example, request `get` on named Secrets in the namespaces that use them rather than cluster-wide Secret access, and do not add RBAC markers for resources a controller never reads. When a server acts for a user, authorize and grant the user only the requested action, such as the `sessions/suspend` authorization subresource, rather than a general verb like `patch` that also permits unrelated edits; keep the broader write on the server's ServiceAccount. Expose credentials, including ServiceAccount tokens and token environment variables, only to the container, process, or CI step that uses them. Sidecars and steps that run pull-request code must not inherit them. - **Verify downloaded artifacts fail closed.** When a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. - **Keep provider integrations isolated end to end.** When adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - **Classify request failures by cause.** When mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases; return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result — propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. diff --git a/self-development/agentconfig.yaml b/self-development/agentconfig.yaml index 834306bc4..2d736c165 100644 --- a/self-development/agentconfig.yaml +++ b/self-development/agentconfig.yaml @@ -39,7 +39,7 @@ spec: - Fail fast on invalid configuration: do not silently fall back to degraded behavior (e.g., unauthenticated requests) when configuration or credentials are invalid or missing; return an error or exit immediately - Verify required filesystem outputs: setup and install scripts must validate the contents their downstream consumers require, not only a zero exit status or the existence of an output directory. Fail when a required operation produces no usable artifacts. When moving or copying a directory's contents, preserve dot-prefixed entries unless the format explicitly excludes them. Cover success-with-empty-output and hidden-entry behavior with executable tests where those cases are supported. - Protect credentials in configuration and transport: values that may contain tokens, authorization headers, or other credentials must come from Kubernetes Secret references, not plaintext Helm values or rendered manifests. Require HTTPS for configurable endpoints that receive credentials unless the API has an explicit insecure opt-in. - - Grant only the access a component uses: scope RBAC verbs, resources, and namespaces to the API calls the code actually makes. For example, request `get` on named Secrets rather than cluster-wide `list`/`watch`, and do not add RBAC markers for resources a controller never reads. Expose credentials, including ServiceAccount tokens and token environment variables, only to the container, process, or CI step that uses them. Sidecars and steps that run pull-request code must not inherit them. + - Grant only the access a component uses: scope RBAC verbs, resources, and namespaces to the API calls the code actually makes. For example, request `get` on named Secrets in the namespaces that use them rather than cluster-wide Secret access, and do not add RBAC markers for resources a controller never reads. When a server acts for a user, authorize and grant the user only the requested action, such as the `sessions/suspend` authorization subresource, rather than a general verb like `patch` that also permits unrelated edits; keep the broader write on the server's ServiceAccount. Expose credentials, including ServiceAccount tokens and token environment variables, only to the container, process, or CI step that uses them. Sidecars and steps that run pull-request code must not inherit them. - Verify downloaded artifacts fail closed: when a build downloads an artifact and a checksum list, require exactly one checksum entry for the selected filename and fail if it is absent or ambiguous. Do not use options such as `--ignore-missing` that can let an unverified artifact install successfully. - Keep provider integrations isolated end to end: when adding a provider alongside an existing one, route credential resolvers, Secret keys, environment variables, API and CLI URL forms, reporters, and watch predicates by explicit provider identity. Do not infer the provider from unrelated keys or let provider-specific controllers process another provider's work. Test every provider path and mixed-provider coexistence, including supported self-hosted URL forms. - Classify request failures by cause: when mapping errors to HTTP status, reserve `4xx` for client-correctable input and confirmed not-found cases, and return `5xx` for lookup, RBAC, storage, and transport failures. A `List`/`Get` error is not an empty result; propagate it so webhook senders can retry. Use typed or sentinel errors such as `errors.Is` and `apierrors.IsNotFound` instead of matching message text, because wrapped server failures must not become client errors. diff --git a/self-development/kelos-reviewer.yaml b/self-development/kelos-reviewer.yaml index d3a976c58..cc08a63e2 100644 --- a/self-development/kelos-reviewer.yaml +++ b/self-development/kelos-reviewer.yaml @@ -236,10 +236,12 @@ spec: `flag.Parse()`. P1. - Flag RBAC rules, ServiceAccount token mounts, and credential environment variables that reach beyond the code that uses them: - verbs or resources the code never calls, cluster-wide Secret - `list`/`watch` where named `get` suffices, or a token inherited by a - sidecar or by a CI step that runs pull-request code. P1 when a - credential or Secret is exposed; P2 otherwise. + verbs or resources the code never calls, cluster-wide Secret access + (including `get`) where named `get` in the using namespace suffices, + or a token inherited by a sidecar or by a CI step that runs + pull-request code. P1 when a credential or Secret is exposed; P2 + otherwise. Do not lower the priority because the component already + holds other broad access. **Documentation accuracy**: - Docs, READMEs, and code comments must describe what the code From f9baa35722b10c35383e7cbbdedfc514d0cde224 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Thu, 29 Oct 2026 18:13:36 +0000 Subject: [PATCH 48/49] self-development: stop preserving new fields through v1alpha1 On a second API change in four days, the maintainer said not to care about v1alpha1 compatibility. Both Kelos reviewers had rated the missing v1alpha1 round-trip preservation of a new PodOverrides field as P2. The maintainer's own new webhook filter criterion was also documented as not preserved instead of getting an annotation. The rule still made preservation the default, and the API reviewer's checklist only asked whether conversion functions were updated. Make the rule match the maintainer's direction: do not add older-version preservation for latest-only fields, and state any drop in the field's reference entry. Tell the API reviewer not to request that preservation and to check for the documented drop instead. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 2 +- self-development/kelos-api-reviewer.yaml | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 8b52e26b3..c6356af97 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,7 +32,7 @@ - **Treat controller-generated Kubernetes names as durable identities.** Validate derived names against every generated resource kind's exact name grammar, then account for every downstream suffix and label limit, including Pod, PVC, and ControllerRevision names, rather than checking only the source resource or the 63-character resource-name limit. On `AlreadyExists`, verify ownership and the intended child's identity (such as stage and index labels) before reusing a resource; reject collisions between different children of the same parent. Cover that collision through a reconciliation test. Do not rename existing controller-owned resources just to satisfy a bound unless the change includes a collision-safe migration that preserves ownership and persistent data. - **Make component renames upgrade-safe.** Before renaming installed Kubernetes objects, Helm value paths, or persisted client keys, inventory the identifiers used by existing deployments. Preserve or explicitly migrate stored state, clean up superseded resources when installation does not prune, keep uninstall cleanup for legacy names, and reject unsupported old configuration with an actionable error. Test an upgrade from prior configuration and state in addition to a fresh install. - **Keep configurable ServiceAccount identities in lockstep.** When a Helm value controls a ServiceAccount name, use it consistently in the workload, the rendered ServiceAccount, and every RBAC subject. Reject names reserved by other chart-managed accounts to avoid duplicate objects, and render-test a non-default valid name plus every reserved name. -- **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. Preserve each latest-only field through older-version conversion on every kind that carries it, including spec, status, and embedded templates, so a read-modify-write through an older version cannot drop it. Test the round trip for each kind. When the maintainer decides not to carry a field through older versions, skip its preservation and state in the field's `docs/reference.md` entry that reads through an older version omit the field and writes through one remove it. +- **Maintain API changes only in the latest Kelos API version.** Add new CRD fields, validation, enum values, constants, and user-facing API behavior only to the latest served/storage API version. Older served versions are compatibility surfaces: keep them served, convertible, and backward-compatible, but do not add new capabilities there unless required to preserve existing manifests or implement conversion/migration compatibility. Do not add older-version preservation, such as a new `kelos.dev/v1alpha2-*` annotation and its round-trip tests, for a latest-only field. When an older version drops the field, state in the field's `docs/reference.md` entry that reads through that version omit it and writes through it remove it. - **Import versioned Kelos APIs with aliases.** Prefer aliasing the current versioned API import as `kelos` when a file uses only one API version. Use explicit aliases like `kelosv1alpha1` only when a file genuinely needs multiple versions. - **Docs must match implementation, not aspiration.** When writing or updating docs, READMEs, or comments, describe only what the code actually does. Do not document unimplemented behavior, overstate guarantees, or describe security checks (e.g., HMAC validation) that aren't enforced. Before describing a contract ("X is filtered", "Y is validated"), verify the code enforces it — partial enforcement should be documented as partial. For changes that grant permissions, authenticate users, or accept a trust boundary, also document the residual risk operators must manage: what a compromised component or permitted identity can reach, including access it can grant to others, and whether removing upstream access ends existing sessions and how to revoke them. - **Keep documentation complete and internally consistent.** When changing documented behavior, search for every related overview, per-component section, trigger table, setup example, and command/flag reference, and update all affected mentions together. Do not remove documentation for still-supported behavior when replacing a nearby section. diff --git a/self-development/kelos-api-reviewer.yaml b/self-development/kelos-api-reviewer.yaml index 7ee03d926..b66e517ce 100644 --- a/self-development/kelos-api-reviewer.yaml +++ b/self-development/kelos-api-reviewer.yaml @@ -258,6 +258,11 @@ spec: **Defaulting and conversion**: - Are defaults set via kubebuilder markers or webhook defaulting? - If this is a multi-version API, are conversion functions updated? + - Do not ask to carry a new latest-only field through an older served + version, for example with a `kelos.dev/v1alpha2-*` annotation. Kelos + does not add that preservation for new fields. Instead, check that + the field's `docs/reference.md` entry says reads through the older + version omit it and writes through it remove it. ### 5. Publish the review From 543453baf34399de57b991ced26b35e681d594a5 Mon Sep 17 00:00:00 2001 From: Gunju Kim Date: Fri, 30 Oct 2026 18:17:23 +0000 Subject: [PATCH 49/49] self-development: sync base-agent with kanon-repo screenshot rule gjkim42/kanon-repo added a rule to instructions/AGENTS.md that PRs with UI changes include labeled before and after screenshots. Copy it into the checked-in base-agent AgentConfig so its agentsMD matches upstream again. Co-Authored-By: Claude Opus 5.5 --- self-development/base-agent.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/self-development/base-agent.yaml b/self-development/base-agent.yaml index eb2bde96a..686ba274b 100644 --- a/self-development/base-agent.yaml +++ b/self-development/base-agent.yaml @@ -18,6 +18,7 @@ spec: - Use `gh` CLI for GitHub operations (PRs, issues, repos, etc.) instead of MCP OSS tools. - Do not prefix PR titles with `[codex]`. - When addressing PR feedback, treat reviews as findings to verify, not instructions to execute blindly. First identify the current PR head, review state, author, timestamp, thread resolution, and whether the comment still applies to the current diff. Fix only feedback that is valid, current, actionable, and supported by the code; explicitly triage stale, resolved, superseded, speculative, preference-only, or non-actionable feedback instead of changing code for it. + - When a PR includes UI changes, include clearly labeled before and after screenshots in the PR description. Use recordings when needed to show interaction changes, and capture the same view and state so reviewers can compare them directly. - Make patches as simple as possible: solve the requested problem directly, keep changes narrowly scoped, follow existing patterns, and avoid opportunistic refactors, dependency churn, or formatting-only edits unless they are necessary for the task. Do not add defensive handling for unreasonable or highly unlikely cases unless there is a concrete requirement, observed failure, or codebase contract that justifies it. - Treat CI-only and test-environment flakes as test or infrastructure problems unless there is concrete evidence of a product/runtime bug. Do not change product behavior just to mask CI timing, ordering, caching, or environment issues; fix the harness, readiness checks, isolation, or diagnostics instead. - Add a regression test only when it pins a failure mode the shipped code must hold against — a defect present on the base branch (diff against `git merge-base HEAD origin/main`, falling back to develop) that this change fixes. Do not add regression tests for bugs introduced and then fixed within the same branch or session; that code never shipped, so the test guards nothing and just locks in transient work-in-progress. If a transient mid-session failure exposes a genuine edge case the final code must handle, cover it as ordinary edge-case test, not a regression test. Always write normal tests for the intended behavior of new or changed code.