Repository navigation
feat(config): Operate · Configuration + Feature flags (Batch 4 / T403+T404, PR-14) - #33
Merged
Merged
Conversation
…k (CAS) Implements T403 config-entries slice (Batch 4): - src/api/config.ts: listConfig/getConfig/writeConfig/updateConfig/deleteConfig/publishConfig/rollbackConfig, CONFIG_URL, ConfigEntry type (codegen-derived) - src/lib/configValidation.ts: validateConfigKey/validateConfigValue/isRedactedPlaceholder (sensitive write guard) - src/stores/useConfigStore.ts: Pinia setup store (id: config.entries), CAS, mutating flag, sensitive guard, race-safe mutations - src/stores/index.ts: C2 append point for useFlagsStore - src/components/ConfirmDialog.vue: config-local copy (candidate for @gocell/core promotion) - src/components/ConfigEditDrawer.vue: create/edit form with stage warning strip, sensitive blank-on-edit - src/views/ConfigView.vue: page-head + table + three states + publish/rollback/delete confirm dialogs - 133 tests, 97.92% stmt / 85.96% fn / 87.86% branch coverage Degraded decisions: no version-history endpoint (BR-008 pending) → rollback manual version input; no draft state in contract → stage semantics via subtitle; sensitive redaction guard prevents "******" write-back. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d-key registry
Implements the flags slice (T404) for @gocell/config:
- src/api/flags.ts: FLAGS_URL, FeatureFlag type, listFlags/getFlag/createFlag/updateFlag/toggleFlag/evaluateFlag/deleteFlag (CAS, percent-encode, 409 re-throw)
- src/flags/registry.ts: FLAG_KEYS as const + FlagKey literal union (Hard-档 AI-robust — unknown literals are compile-time errors)
- src/composables/useFlag.ts: useFlag(key: FlagKey) → readonly ComputedRef<boolean> consuming useFlagsStore cache; fail-safe false when key not found
- src/stores/useFlagsStore.ts: defineStore('config.flags') — state/filteredFlags/isEnabled; fetchList/loadMore; create/update/toggle/remove (re-throw, CAS)
- src/components/RolloutSlider.vue: native <input type="range"> 0–100, aria-valuenow/min/max, clamp, keyboard-operable
- src/components/FlagFormModal.vue: ModalShell-based create/edit (key, description, enabled, rolloutPercentage, type read-only display, variant coming-soon placeholder)
- src/views/FlagsView.vue: v1-flag cards, v1-switch, v1-flag-bar rollout progress (CSS var data-driven), kill switch danger confirm, delete confirm, <Can> guards, 3-state (loading/empty/error)
- Appended useFlagsStore to src/stores/index.ts and flag exports to src/index.ts
- README updated with flags domain section
Degradation decisions:
- variant config UI not implemented; type field read-only + placeholder (BR-007 pending: flag variant schema)
- useFlag uses list cache, not /evaluate (admin endpoint, not suitable for high-frequency)
Tests: 252 pass (119 new), coverage 97.25% stmts, typecheck clean, lint 0 issues
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g cell Expose @gocell/config to apps/web; declare exports (./stores ./composables ./views/config ./views/flags); mirror access vitest/coverage config. Refs #15
…atch 4 PR-14)
- apps/web: register /config (PDP read:config) and /flags (PDP read:flag) as
AppShellLayout children; lazy-load @gocell/config view subpaths.
- core i18n: add config.entries.* and flags.{list,form}.* blocks (zh-CN + en-US).
- router spec: assert /config and /flags resolve nested with auth + PDP meta.
Refs #15
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- BR-006: audit row hash chain + actor/result metadata (audit page degraded until exposed; verifyChain ready). - BR-007: feature flag variant definition + evaluate value (bool ships now; variant placeholder). - BR-008: config version-history list endpoint (rollback uses manual version input until then). Refs #15
…ot Cx1-Cx2 F1: sensitive aria-label now bound via :aria-label + t() (was bare string literal) F2: toggle wrapped in <Can write/flag>; aria-label moved to <input>; @change replaced with onToggleChange — enabled→disabled path: preventDefault + askKill (checkbox reverted visually); disabled→enabled path: immediate store.toggle call F3: registry.spec FlagKey assignment removes `as FlagKey` cast — direct assignment from Object.values(FLAG_KEYS) is the real compile-time check F4: FlagFormModal + ConfigEditDrawer focus-visible outline restored to 2px solid var(--accent) + offset 2px (was outline:none) F5+F9: RolloutSlider — hardcoded id removed, replaced with useId() fallback; ariaLabel prop removed; accessible name comes from parent <label :for> association; FlagFormModal wires rolloutSliderId via useId() F6: FlagsView border-radius 4px → var(--r-sm), 999px → var(--r-pill) F7: useFlagsStore.isEnabled O(n)→O(1) via flagMap computed (Map keyed by flag.key) F8: useFlagsStore.flags + useConfigStore.entries changed to shallowRef F10: flags.list.confirm.cancel added to en-US + zh-CN; FlagsView kill/delete dialogs now pass cancel-key="flags.list.confirm.cancel" explicitly F11: api/config.ts + api/flags.ts use HttpConfigListV1Response / HttpConfigFlagsListV1Response directly; local ConfigListPage / FlagListPage interfaces removed F12: ConfirmDialog gains default <slot /> between message and error; rollback version input moved from standalone v-if div into rollback ConfirmDialog slot (input now inside modal panel, accessible within focus trap) F13: useFlag / FLAG_KEYS / FlagKey removed from @gocell/config root index.ts; exposed only via @gocell/config/composables F14: tsconfig.base.json paths: @gocell/config sub-paths added (composables, stores, views/config, views/flags) matching package.json#exports Tests: 259 passing (config), 957 passing (workspace) Coverage: 97% stmts / 87.74% branches (≥80% threshold met) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…itle - FlagsView: a failed *enable* toggle no longer opens the kill-switch confirm dialog (which prompted "disable this flag?" after an enable error). It now reverts the optimistic native checkbox flip and surfaces the error inline via a dedicated toggleErrorKey alert. Adds a regression test. - i18n: zh-CN flags.list.title was the English placeholder "Feature flags"; translated to "功能开关" to match nav.flags and the零硬编中英文 rule. config 260 tests + core i18n 29 tests pass; typecheck + lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Owner
Author
六维度 Review(包边界 / Vue 模板 / TS / 包架构 / a11y / 性能 / 设计一致性)整体质量积极:包边界无违规(无裸 axios、无横向业务 cell import、HTTP 经 已修复(commit 82c74dc)
评估后不改(登记说明 + 理由)
结论:✅ 可合并(reviewer 结论本身为「不 block」)。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Batch 4 / T403 + T404 — the Operate · Configuration and Feature flags pages, second of the two PRs for #15. Builds the
@gocell/configcell (config entries + flags +useFlag).Configuration (T403) —
config.entriesstoreapi/config.ts— list/get/write/update(CAS)/publish/rollback/delete against/api/v1/config/. CAS viaexpectedVersion.views/ConfigView.vue+ConfigEditDrawer.vue+ConfirmDialog.vue— versioned table, stage→publish flow, rollback, sensitive-value redaction guard.lib/configValidation.ts— key/value validation +isRedactedPlaceholderwrite-back guard.Feature flags (T404) —
config.flagsstore +useFlagapi/flags.ts— list/get/create/update(PUT, full)/toggle(kill switch)/evaluate/delete against/api/v1/flags/.views/FlagsView.vue+FlagFormModal.vue+RolloutSlider.vue— flag cards, bool toggle, rollout %, kill switch (danger confirm).composables/useFlag.ts+flags/registry.ts—FLAG_KEYS as const→FlagKeyliteral union.useFlag(key: FlagKey)makes a hardcoded flag name a compile error (AI-robust Hard: violation is inexpressible). Reverse self-check via@ts-expect-error(unknown key rejected, known key passes). Reads the flags-list cache (not per-flag/evaluate); missing key →false(fail-safe).Backend gaps (frontend-first per #15 — graceful degradation, BRs filed)
variant(create/update/evaluate carry no variant value;typeis a free string)typeread-only; explicit "variant configuration coming soon" placeholder (no internal IDs surfaced to users)Packages touched
@gocell/config(new cell): api / stores / composables / lib / views / components / README / exports.@gocell/core: i18nconfig.entries.*+flags.{list,form}.*blocks (zh-CN + en-US).apps/web:/config(PDPread:config) +/flags(PDPread:flag) routes +@gocell/configdependency.packages/contracts/: not modified — consumed read-only.Test plan
pnpm -F @gocell/config test:coverage— 252 tests pass, 97.25% statements (≥80%)pnpm -w typecheck— clean (incl.useFlag@ts-expect-errorreverse self-check + en-USsatisfies MessageSchema)pnpm -w lint— 0 issuespnpm -w test --run— 62 files / 950 tests pass (incl. new/config+/flagsrouter assertions)Refs #15