@starci/skills 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INDEX.md +74 -0
- package/INDEX.vi.md +75 -0
- package/README.md +44 -0
- package/README.vi.md +43 -0
- package/SKILL.md +135 -0
- package/SKILL.vi.md +128 -0
- package/alias/INDEX.md +104 -0
- package/alias/INDEX.vi.md +104 -0
- package/alias/alias.json +297 -0
- package/bin/starci-skills.mjs +219 -0
- package/knowledge/INDEX.md +22 -0
- package/knowledge/INDEX.vi.md +22 -0
- package/knowledge/grammars/starci/DNA.md +146 -0
- package/knowledge/grammars/starci/DNA.vi.md +146 -0
- package/knowledge/grammars/starci/INDEX.md +25 -0
- package/knowledge/grammars/starci/INDEX.vi.md +25 -0
- package/knowledge/grammars/starci/family.md +50 -0
- package/knowledge/grammars/starci/family.vi.md +50 -0
- package/knowledge/grammars/starci/idioms.md +120 -0
- package/knowledge/grammars/starci/idioms.vi.md +121 -0
- package/knowledge/grammars/starci/playbook.md +36 -0
- package/knowledge/grammars/starci/playbook.vi.md +35 -0
- package/knowledge/patterns/be/INDEX.md +29 -0
- package/knowledge/patterns/be/INDEX.vi.md +29 -0
- package/knowledge/patterns/be/comment.md +80 -0
- package/knowledge/patterns/be/comment.vi.md +80 -0
- package/knowledge/patterns/be/error.md +86 -0
- package/knowledge/patterns/be/error.vi.md +86 -0
- package/knowledge/patterns/be/folder.md +87 -0
- package/knowledge/patterns/be/folder.vi.md +86 -0
- package/knowledge/patterns/be/function.md +80 -0
- package/knowledge/patterns/be/function.vi.md +80 -0
- package/knowledge/patterns/be/imports.md +79 -0
- package/knowledge/patterns/be/imports.vi.md +79 -0
- package/knowledge/patterns/be/naming.md +87 -0
- package/knowledge/patterns/be/naming.vi.md +87 -0
- package/knowledge/patterns/be/test.md +79 -0
- package/knowledge/patterns/be/test.vi.md +79 -0
- package/knowledge/patterns/be/typing.md +73 -0
- package/knowledge/patterns/be/typing.vi.md +73 -0
- package/knowledge/patterns/fe/INDEX.md +29 -0
- package/knowledge/patterns/fe/INDEX.vi.md +29 -0
- package/knowledge/patterns/fe/comment.md +70 -0
- package/knowledge/patterns/fe/comment.vi.md +70 -0
- package/knowledge/patterns/fe/error.md +61 -0
- package/knowledge/patterns/fe/error.vi.md +61 -0
- package/knowledge/patterns/fe/folder.md +98 -0
- package/knowledge/patterns/fe/folder.vi.md +95 -0
- package/knowledge/patterns/fe/function.md +70 -0
- package/knowledge/patterns/fe/function.vi.md +70 -0
- package/knowledge/patterns/fe/imports.md +87 -0
- package/knowledge/patterns/fe/imports.vi.md +87 -0
- package/knowledge/patterns/fe/naming.md +77 -0
- package/knowledge/patterns/fe/naming.vi.md +77 -0
- package/knowledge/patterns/fe/test.md +73 -0
- package/knowledge/patterns/fe/test.vi.md +73 -0
- package/knowledge/patterns/fe/typing.md +67 -0
- package/knowledge/patterns/fe/typing.vi.md +67 -0
- package/knowledge/ui/INDEX.md +101 -0
- package/knowledge/ui/INDEX.vi.md +101 -0
- package/knowledge/ui/composition/INDEX.md +60 -0
- package/knowledge/ui/composition/INDEX.vi.md +63 -0
- package/knowledge/ui/composition/accent.md +73 -0
- package/knowledge/ui/composition/accent.vi.md +74 -0
- package/knowledge/ui/composition/action.md +55 -0
- package/knowledge/ui/composition/action.vi.md +56 -0
- package/knowledge/ui/composition/coverage.md +37 -0
- package/knowledge/ui/composition/coverage.vi.md +37 -0
- package/knowledge/ui/composition/cta.md +79 -0
- package/knowledge/ui/composition/cta.vi.md +79 -0
- package/knowledge/ui/composition/feedback.md +55 -0
- package/knowledge/ui/composition/feedback.vi.md +56 -0
- package/knowledge/ui/composition/hierarchy.md +83 -0
- package/knowledge/ui/composition/hierarchy.vi.md +82 -0
- package/knowledge/ui/composition/layout.md +91 -0
- package/knowledge/ui/composition/layout.vi.md +93 -0
- package/knowledge/ui/composition/responsive.md +67 -0
- package/knowledge/ui/composition/responsive.vi.md +67 -0
- package/knowledge/ui/composition/state.md +105 -0
- package/knowledge/ui/composition/state.vi.md +106 -0
- package/knowledge/ui/presentation/INDEX.md +94 -0
- package/knowledge/ui/presentation/INDEX.vi.md +69 -0
- package/knowledge/ui/presentation/boundary.md +170 -0
- package/knowledge/ui/presentation/boundary.vi.md +169 -0
- package/knowledge/ui/presentation/font.md +155 -0
- package/knowledge/ui/presentation/font.vi.md +156 -0
- package/knowledge/ui/presentation/gap.md +181 -0
- package/knowledge/ui/presentation/gap.vi.md +181 -0
- package/knowledge/ui/presentation/margin.md +168 -0
- package/knowledge/ui/presentation/margin.vi.md +171 -0
- package/knowledge/ui/presentation/measure.md +174 -0
- package/knowledge/ui/presentation/measure.vi.md +178 -0
- package/knowledge/ui/presentation/overflow.md +142 -0
- package/knowledge/ui/presentation/overflow.vi.md +142 -0
- package/knowledge/ui/presentation/padding.md +293 -0
- package/knowledge/ui/presentation/padding.vi.md +292 -0
- package/knowledge/ui/presentation/surface.md +166 -0
- package/knowledge/ui/presentation/surface.vi.md +166 -0
- package/knowledge/ui/presentation/text-flow.md +123 -0
- package/knowledge/ui/presentation/text-flow.vi.md +123 -0
- package/knowledge/ui/presentation/tone.md +114 -0
- package/knowledge/ui/presentation/tone.vi.md +113 -0
- package/knowledge/ui/proof/INDEX.md +55 -0
- package/knowledge/ui/proof/INDEX.vi.md +55 -0
- package/knowledge/ui/proof/accessibility.md +67 -0
- package/knowledge/ui/proof/accessibility.vi.md +68 -0
- package/knowledge/ui/proof/contrast.md +58 -0
- package/knowledge/ui/proof/contrast.vi.md +57 -0
- package/knowledge/ui/proof/focus.md +76 -0
- package/knowledge/ui/proof/focus.vi.md +76 -0
- package/knowledge/ui/proof/motion.md +63 -0
- package/knowledge/ui/proof/motion.vi.md +64 -0
- package/knowledge/ui/proof/render-truth.md +63 -0
- package/knowledge/ui/proof/render-truth.vi.md +63 -0
- package/operators/INDEX.md +199 -0
- package/operators/INDEX.vi.md +199 -0
- package/operators/architecture-decide/errors.json +115 -0
- package/operators/architecture-decide/operator.json +20 -0
- package/operators/architecture-decide/operator.md +133 -0
- package/operators/architecture-decide/operator.vi.md +130 -0
- package/operators/architecture-decide/self-test.mjs +221 -0
- package/operators/architecture-decide/validate.mjs +112 -0
- package/operators/backend-source-apply/errors.json +66 -0
- package/operators/backend-source-apply/operator.json +20 -0
- package/operators/backend-source-apply/operator.md +189 -0
- package/operators/backend-source-apply/operator.vi.md +185 -0
- package/operators/backend-source-apply/self-test.mjs +257 -0
- package/operators/backend-source-apply/validate.mjs +240 -0
- package/operators/business-decide/errors.json +78 -0
- package/operators/business-decide/operator.json +20 -0
- package/operators/business-decide/operator.md +176 -0
- package/operators/business-decide/operator.vi.md +174 -0
- package/operators/business-decide/self-test.mjs +225 -0
- package/operators/business-decide/validate.mjs +277 -0
- package/operators/content-generate/errors.json +106 -0
- package/operators/content-generate/operator.json +21 -0
- package/operators/content-generate/operator.md +155 -0
- package/operators/content-generate/operator.vi.md +155 -0
- package/operators/content-generate/self-test.mjs +288 -0
- package/operators/content-generate/validate.mjs +196 -0
- package/operators/errors.json +178 -0
- package/operators/frontend-direction-decide/errors.json +135 -0
- package/operators/frontend-direction-decide/operator.json +21 -0
- package/operators/frontend-direction-decide/operator.md +167 -0
- package/operators/frontend-direction-decide/operator.vi.md +167 -0
- package/operators/frontend-direction-decide/self-test.mjs +226 -0
- package/operators/frontend-direction-decide/validate.mjs +146 -0
- package/operators/frontend-presentation-resolve/errors.json +42 -0
- package/operators/frontend-presentation-resolve/operator.json +19 -0
- package/operators/frontend-presentation-resolve/operator.md +142 -0
- package/operators/frontend-presentation-resolve/operator.vi.md +140 -0
- package/operators/frontend-presentation-resolve/self-test.mjs +163 -0
- package/operators/frontend-presentation-resolve/validate.mjs +143 -0
- package/operators/frontend-source-apply/errors.json +30 -0
- package/operators/frontend-source-apply/operator.json +20 -0
- package/operators/frontend-source-apply/operator.md +127 -0
- package/operators/frontend-source-apply/operator.vi.md +126 -0
- package/operators/frontend-source-apply/self-test.mjs +214 -0
- package/operators/frontend-source-apply/validate.mjs +133 -0
- package/operators/frontend-surface-audit/errors.json +5 -0
- package/operators/frontend-surface-audit/operator.json +22 -0
- package/operators/frontend-surface-audit/operator.md +121 -0
- package/operators/frontend-surface-audit/operator.vi.md +121 -0
- package/operators/frontend-surface-audit/self-test.mjs +185 -0
- package/operators/frontend-surface-audit/validate.mjs +127 -0
- package/operators/git-publish/errors.json +54 -0
- package/operators/git-publish/operator.json +20 -0
- package/operators/git-publish/operator.md +180 -0
- package/operators/git-publish/operator.vi.md +178 -0
- package/operators/git-publish/self-test.mjs +177 -0
- package/operators/git-publish/validate.mjs +106 -0
- package/operators/platform-operate/errors.json +90 -0
- package/operators/platform-operate/operator.json +22 -0
- package/operators/platform-operate/operator.md +162 -0
- package/operators/platform-operate/operator.vi.md +160 -0
- package/operators/platform-operate/self-test.mjs +202 -0
- package/operators/platform-operate/validate.mjs +193 -0
- package/operators/quality-verify/errors.json +54 -0
- package/operators/quality-verify/operator.json +20 -0
- package/operators/quality-verify/operator.md +185 -0
- package/operators/quality-verify/operator.vi.md +177 -0
- package/operators/quality-verify/self-test.mjs +210 -0
- package/operators/quality-verify/validate.mjs +197 -0
- package/operators/release-deploy/errors.json +158 -0
- package/operators/release-deploy/operator.json +23 -0
- package/operators/release-deploy/operator.md +183 -0
- package/operators/release-deploy/operator.vi.md +181 -0
- package/operators/release-deploy/self-test.mjs +235 -0
- package/operators/release-deploy/validate.mjs +129 -0
- package/operators/uat-verify/errors.json +78 -0
- package/operators/uat-verify/operator.json +25 -0
- package/operators/uat-verify/operator.md +162 -0
- package/operators/uat-verify/operator.vi.md +161 -0
- package/operators/uat-verify/self-test.mjs +270 -0
- package/operators/uat-verify/validate.mjs +202 -0
- package/operators/workspace-bind/errors.json +90 -0
- package/operators/workspace-bind/operator.json +21 -0
- package/operators/workspace-bind/operator.md +148 -0
- package/operators/workspace-bind/operator.vi.md +148 -0
- package/operators/workspace-bind/self-test.mjs +205 -0
- package/operators/workspace-bind/validate.mjs +152 -0
- package/package.json +50 -0
- package/readiness/initialization/workspaces/commit-policy.json +91 -0
- package/readiness/initialization/workspaces/config.schema.json +35 -0
- package/readiness/initialization/workspaces/device-state.schema.json +54 -0
- package/readiness/initialization/workspaces/local-route.schema.json +206 -0
- package/readiness/initialization/workspaces/portable-route.schema.json +200 -0
- package/resources/INDEX.md +96 -0
- package/resources/INDEX.vi.md +99 -0
- package/resources/agents/profiles/claude.json +131 -0
- package/resources/agents/profiles/openai.json +131 -0
- package/resources/orchestrator.json +71 -0
- package/resources/tools.json +85 -0
- package/routing.json +272 -0
- package/scripts/alias-registry.mjs +31 -0
- package/scripts/device-state.mjs +497 -0
- package/scripts/device-state.spec.mjs +18 -0
- package/scripts/errors-registry.mjs +68 -0
- package/scripts/generate-alias-doc.mjs +66 -0
- package/scripts/generate-grammar-dna.mjs +387 -0
- package/scripts/generate-operators-index.mjs +95 -0
- package/scripts/generate-presentation-owned.mjs +681 -0
- package/scripts/install-cli.spec.mjs +74 -0
- package/scripts/json-schema.mjs +94 -0
- package/scripts/operator-md.mjs +96 -0
- package/scripts/run-operator-self-tests.mjs +36 -0
- package/scripts/validate-alias.mjs +165 -0
- package/scripts/validate-defaults.mjs +72 -0
- package/scripts/validate-knowledge-citations.mjs +90 -0
- package/scripts/validate-operator.mjs +125 -0
- package/scripts/validate-request.mjs +80 -0
- package/scripts/validate-resources.mjs +117 -0
- package/scripts/validate-response.mjs +141 -0
- package/scripts/validate-routing.mjs +91 -0
- package/scripts/validate-step.mjs +50 -0
- package/scripts/validate-templates.mjs +226 -0
- package/scripts/validate-templates.spec.mjs +144 -0
- package/scripts/validate-workflows.mjs +106 -0
- package/scripts/workspace-portable.mjs +389 -0
- package/scripts/workspace-portable.spec.mjs +246 -0
- package/templates/README.md +47 -0
- package/templates/README.vi.md +45 -0
- package/templates/changes.example.md +27 -0
- package/templates/grammars.template.md +34 -0
- package/templates/kinds/architecture-decision.contract.json +14 -0
- package/templates/kinds/architecture-decision.skeleton.md +52 -0
- package/templates/kinds/backend-source-application.contract.json +10 -0
- package/templates/kinds/backend-source-application.skeleton.md +34 -0
- package/templates/kinds/business-promise-authority.contract.json +13 -0
- package/templates/kinds/business-promise-authority.skeleton.md +67 -0
- package/templates/kinds/capture.schema.json +33 -0
- package/templates/kinds/changes.contract.json +10 -0
- package/templates/kinds/changes.skeleton.md +26 -0
- package/templates/kinds/checks.schema.json +44 -0
- package/templates/kinds/claims.schema.json +165 -0
- package/templates/kinds/conformance.schema.json +15 -0
- package/templates/kinds/content-brief.contract.json +12 -0
- package/templates/kinds/content-brief.skeleton.md +45 -0
- package/templates/kinds/content-generation-receipt.contract.json +11 -0
- package/templates/kinds/content-generation-receipt.skeleton.md +40 -0
- package/templates/kinds/content-review.contract.json +11 -0
- package/templates/kinds/content-review.skeleton.md +45 -0
- package/templates/kinds/contract.schema.json +28 -0
- package/templates/kinds/coverage-matrix.schema.json +86 -0
- package/templates/kinds/coverage.schema.json +62 -0
- package/templates/kinds/current-state.schema.json +40 -0
- package/templates/kinds/delta.schema.json +95 -0
- package/templates/kinds/e2e.schema.json +63 -0
- package/templates/kinds/frontend-direction-decision.contract.json +93 -0
- package/templates/kinds/frontend-direction-decision.skeleton.md +68 -0
- package/templates/kinds/frontend-presentation-resolution.contract.json +11 -0
- package/templates/kinds/frontend-presentation-resolution.skeleton.md +32 -0
- package/templates/kinds/frontend-source-application.contract.json +10 -0
- package/templates/kinds/frontend-source-application.skeleton.md +30 -0
- package/templates/kinds/frontend-surface-audit.contract.json +52 -0
- package/templates/kinds/frontend-surface-audit.skeleton.md +32 -0
- package/templates/kinds/gate-result.schema.json +64 -0
- package/templates/kinds/git-publication.contract.json +82 -0
- package/templates/kinds/git-publication.skeleton.md +61 -0
- package/templates/kinds/independent-critique.contract.json +9 -0
- package/templates/kinds/independent-critique.skeleton.md +28 -0
- package/templates/kinds/inventory.schema.json +35 -0
- package/templates/kinds/model.schema.json +99 -0
- package/templates/kinds/mutations.schema.json +65 -0
- package/templates/kinds/platform-operation-receipt.contract.json +76 -0
- package/templates/kinds/platform-operation-receipt.skeleton.md +54 -0
- package/templates/kinds/probes.schema.json +130 -0
- package/templates/kinds/proof.schema.json +17 -0
- package/templates/kinds/quality-verification.contract.json +87 -0
- package/templates/kinds/quality-verification.skeleton.md +58 -0
- package/templates/kinds/release-deployment.contract.json +85 -0
- package/templates/kinds/release-deployment.skeleton.md +67 -0
- package/templates/kinds/route.schema.json +293 -0
- package/templates/kinds/stack-model.schema.json +90 -0
- package/templates/kinds/uat-capture.schema.json +35 -0
- package/templates/kinds/uat-flow-verification.contract.json +11 -0
- package/templates/kinds/uat-flow-verification.skeleton.md +47 -0
- package/templates/kinds/uat-snapshot.schema.json +96 -0
- package/templates/kinds/uat-verdicts.schema.json +41 -0
- package/templates/kinds/ui-coverage.schema.json +77 -0
- package/templates/kinds/verdicts.schema.json +39 -0
- package/templates/kinds/workspace-route-binding.contract.json +12 -0
- package/templates/kinds/workspace-route-binding.skeleton.md +60 -0
- package/templates/kinds/writes.schema.json +37 -0
- package/templates/operator.template.md +73 -0
- package/templates/patterns.template.md +31 -0
- package/templates/step/request.schema.json +42 -0
- package/templates/step/response.schema.json +144 -0
- package/templates/ui-composition.template.md +36 -0
- package/templates/ui-presentation.template.md +57 -0
- package/templates/ui-proof.template.md +34 -0
- package/workflows/README.md +37 -0
- package/workflows/README.vi.md +37 -0
- package/workflows/backend-feature.json +59 -0
- package/workflows/content-unit.json +19 -0
- package/workflows/frontend-new-surface.json +81 -0
- package/workflows/frontend-reconstruct.json +67 -0
- package/workflows/frontend-refine.json +67 -0
- package/workflows/frontend-with-uat.json +78 -0
- package/workflows/full-feature.json +104 -0
- package/workflows/release.json +29 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# StarCi Core — idioms
|
|
2
|
+
|
|
3
|
+
An idiom is a composition StarCi reaches for again: a recurring way of putting published Grammar
|
|
4
|
+
renderers together that the owner has already chosen more than once. This file is taste, not law.
|
|
5
|
+
Universal law lives in [knowledge/ui](../../ui/INDEX.md); what each renderer is and owns lives in
|
|
6
|
+
[DNA](DNA.md), generated from the package. Nothing here re-narrates renderer anatomy, and nothing
|
|
7
|
+
here tells anyone to go looking for inspiration: a direction is composed from these idioms plus the
|
|
8
|
+
business shape, and a reference, when the request supplies one, is read through them.
|
|
9
|
+
|
|
10
|
+
Promotion has one bar: at least two occurrences in the evidence blocks — starci
|
|
11
|
+
`src/components/blocks/dashboard/*` and `src/components/blocks/commerce/ProSubscriptionBlock`, and
|
|
12
|
+
nivo `apps/app/src/components/blocks/auth/AuthenticationPanel`. A composition seen once is recorded
|
|
13
|
+
at the bottom and never composed from. Where the owner has already written down the intent, that
|
|
14
|
+
sentence is quoted from the block's own `classNames.ts`, because it is the owner's word on why the
|
|
15
|
+
shape exists. Every composition cell names only renderers and props the package publishes.
|
|
16
|
+
|
|
17
|
+
## Joined bands in one flush card
|
|
18
|
+
|
|
19
|
+
One card, no inner cards: the body is flush, and horizontal rules alone divide it into bands.
|
|
20
|
+
|
|
21
|
+
| Use when | Grammar composition | Evidence |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| A dashboard section holds two or more kinds of content that belong to one heading | `SurfaceCard label composition="joined"` around one app `flex flex-col` body whose children are the bands; the card owns the outer boundary and clipping, the app owns `border-t border-separator` between bands | 10 dashboard blocks: `ChangelogList`, `ContinueLearning`, `DailyQuest`, `FeedExplorer`, `JobReadinessWidget`, `OverviewContributions`, `StreakStrip`, `TrendingContents`, `WeeklyChallengeCard`, `WeeklyGoals`. Owner: "Stack full-bleed bands inside one bounded dashboard surface", "Separate stacked dashboard bands without inventing extra vertical space" (`blocks/dashboard/classNames.ts`) |
|
|
24
|
+
| A band is a list of peers | An app `ul`/`div` with `m-0 list-none p-0 divide-y divide-separator`; each row `px-4 pt-3 pb-3 last:pb-4`, so the inset is constant and only the outer bottom edge grows | `ChangelogList`, `DailyQuest` tasks, `JobReadinessWidget` pillars, `WeeklyChallengeCard` finishers, `MyCoursesProgress`, `RecommendedCourses`, commerce benefit grid. Owner: "One divided row inside a flush dashboard list: px-4 always; pb-4 only on the bottom edge" (`blocks/dashboard/classNames.ts`) |
|
|
25
|
+
| An explanatory page section carries prose, art, and a list | Same card, the three as successive bands rather than three cards | `ProSubscriptionBlock` benefits card: intro band, journey band, benefit grid inside one `SurfaceCard label composition={"joined"}` |
|
|
26
|
+
|
|
27
|
+
## A neutral band opens the card with its summary
|
|
28
|
+
|
|
29
|
+
The card's one-line answer sits first, on the secondary surface, and is separated from the evidence
|
|
30
|
+
below it — a summary, never a verdict.
|
|
31
|
+
|
|
32
|
+
| Use when | Grammar composition | Evidence |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| A card states a measure before it shows the evidence for it | App band `bg-surface-secondary text-foreground px-4 pt-4 pb-3` as the first child of the joined card, then `border-t border-separator` | `JobReadinessWidget` headline, `WeeklyGoals` summary, `WeeklyChallengeCard` countdown, `ProSubscriptionBlock` benefits intro |
|
|
35
|
+
| The band sits between two separators rather than at the top | The same treatment at `px-4 py-3` | `DailyQuest` reward band. Owner: "Neutral band between separators: px-4 always; p-3 on both separator sides vertically" (`blocks/dashboard/classNames.ts`) |
|
|
36
|
+
| The summary is a proven outcome, not a pending promise | Only then does the band take a state colour; an unclaimed promise stays neutral | Owner: "Keep an unclaimed promise neutral; only a proven claimed outcome receives success" (`DailyQuest/classNames.ts`); "Present weekly progress as a neutral summary rather than a state outcome" (`WeeklyGoals/classNames.ts`); [TRUTH-1..4](../../ui/proof/render-truth.md) |
|
|
37
|
+
|
|
38
|
+
## The card's one action closes the bottom band
|
|
39
|
+
|
|
40
|
+
A card has at most one onward action, and it lives in its own band against the card's bottom edge —
|
|
41
|
+
never floating inside the content.
|
|
42
|
+
|
|
43
|
+
| Use when | Grammar composition | Evidence |
|
|
44
|
+
| --- | --- | --- |
|
|
45
|
+
| A card offers one way onward | App band `border-t border-separator px-4 pb-4 pt-3` as the last child, holding one `Button` or `TextAction` | `JobReadinessWidget`, `StreakStrip`, `WeeklyChallengeCard`, `WeeklyGoals`. Owner: "Keep a card's lone action separated at the bottom edge" (`blocks/dashboard/classNames.ts`) |
|
|
46
|
+
| The action is a purchase and must survive a narrow viewport | The same band with `grid grid-cols-1 gap-2`, the `Button variant="primary"` stretched to the band width, and an optional `Text size="xs" tone="muted"` under it | `ProSubscriptionBlock`. Owner: "Full-bleed divider and inset action content for the purchase boundary" (`ProSubscriptionBlock/classNames.ts`) |
|
|
47
|
+
|
|
48
|
+
## Generated art is a band, not a card
|
|
49
|
+
|
|
50
|
+
Generated imagery earns a band inside the surface it belongs to. It never becomes a second card, and
|
|
51
|
+
it never gets an inset frame that letterboxes it.
|
|
52
|
+
|
|
53
|
+
| Use when | Grammar composition | Evidence |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| The asset is the point of the section | Its own full-bleed band inside the joined card: `border-t border-separator` above, no inset, and an image at `block h-auto w-full` so the generated ratio survives | `ProSubscriptionBlock` journey band. Owner: "The generated journey is its own edge-to-edge joined band; the SurfaceCard owns outer clipping" and "Preserve the generated asset ratio without an inset frame or letterbox" (`ProSubscriptionBlock/classNames.ts`) |
|
|
56
|
+
| The asset is decorative reward or discovery art | `MediaFrame aspect="landscape" fit="contain" treatment="plain"` inside an app band on `bg-accent-soft`, or an accent slab with the art anchored to its trailing edge and `alt=""`/`aria-hidden` | `TrendingContents` media panel — owner: "Keep generated discovery media prominent without becoming a separate card"; `DailyQuest` hero — owner: "The generated quest illustration stays decorative on the hero's trailing edge" |
|
|
57
|
+
|
|
58
|
+
## Title and one supporting line
|
|
59
|
+
|
|
60
|
+
Two lines, tightly stacked: what the thing is called, then one muted line that qualifies it. It is
|
|
61
|
+
the most repeated relationship in the whole surface set.
|
|
62
|
+
|
|
63
|
+
| Use when | Grammar composition | Evidence |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| Inside a row or cell, where the title is not document structure | `Text size="sm" weight="semibold"` then `Text size="xs" tone="muted"`, stacked at [GAP-1](../../ui/presentation/gap.md) | Dashboard rows and the commerce benefit rows. Owner: "Title and explanation stack for one outcome" (`ProSubscriptionBlock/classNames.ts`) |
|
|
66
|
+
| The title is the surface's own name | `Heading` then `Text size="sm" tone="muted"` | nivo `AuthenticationPanel` header and its notice tree. Owner: "Title and subtitle stay visually coupled" (`AuthenticationPanel/classNames.ts`) |
|
|
67
|
+
| The qualifier belongs above the title | `Text size="xs" tone="muted"` first, then `Text size="md" weight="semibold"` | `ContinueLearning`. Owner: "Keep the supporting kind close to the destination title it qualifies" |
|
|
68
|
+
| Grammar should own the pair | `SurfaceCopyGroup` — the one renderer published for this rhythm | `ProSubscriptionBlock` purchase status |
|
|
69
|
+
|
|
70
|
+
Counted once per container across the evidence blocks, this pair occurs eight times, seven of them
|
|
71
|
+
hand-built with an app `div`; the inventory and the proposal to give `SurfaceCopyGroup` typed slots
|
|
72
|
+
are in [audits/1.0.1/proposals/copy-group-composite.md](../../../audits/1.0.1/proposals/copy-group-composite.md).
|
|
73
|
+
|
|
74
|
+
## Pending is the same tree, resting
|
|
75
|
+
|
|
76
|
+
A loading surface is the finished surface with its content at rest — same card, same bands, same row
|
|
77
|
+
count. It is never a spinner and never a different tree.
|
|
78
|
+
|
|
79
|
+
| Use when | Grammar composition | Evidence |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| The block is waiting for its data | `SurfaceCard state={loading ? "pending" : "neutral"}`, a fixed-length array of resting items in place of the real ones, and `isSkeleton` on every `Text` and `Heading` inside | `ChangelogList`, `ContinueLearning`, `DailyQuest`, `JobReadinessWidget`, `OverviewContributions`, `StreakStrip`, `WeeklyChallengeCard`, `WeeklyGoals`, `ProSubscriptionBlock` |
|
|
82
|
+
| An action exists but must not fire yet | The same tree with `Button isDisabled` rather than a removed button, so the layout does not move when data lands | `ProSubscriptionBlock` purchase action |
|
|
83
|
+
| The block has no data at all, or the read failed | A different tree: `SurfaceCard composition="single"` around one `EmptyNotice` with `message`, optional `actionLabel`, and `iconSource` — empty and failed are states, not a resting variant | `ContinueLearning`, `DailyQuest`, `TrendingContents`; [STATE-1](../../ui/composition/state.md) |
|
|
84
|
+
|
|
85
|
+
## One highlighted card
|
|
86
|
+
|
|
87
|
+
At most one card in a view wears `isHighlight`, and it is the one thing the reader is meant to do
|
|
88
|
+
next. Highlight is a claim about the task, not decoration.
|
|
89
|
+
|
|
90
|
+
| Use when | Grammar composition | Evidence |
|
|
91
|
+
| --- | --- | --- |
|
|
92
|
+
| Peers are shown together but one is the intended next step | `SurfaceCard isHighlight={true}` on that one card only; peers keep the same props otherwise, so the difference reads as rank rather than as a different component | `ContinueLearning`, where only the first resume item is highlighted — owner: "The first resumable item is the focal task; later items remain useful but quieter" |
|
|
93
|
+
| The view exists to close one decision | The decision card is the highlighted one and sits in the rail of `PrimaryRailLayout`; everything explanatory stays unhighlighted in `primary` | `ProSubscriptionBlock` plan rail |
|
|
94
|
+
|
|
95
|
+
## Single-column form stack
|
|
96
|
+
|
|
97
|
+
Fields, then the status sentence, then one primary submit — one column, one submit, in that order.
|
|
98
|
+
|
|
99
|
+
| Use when | Grammar composition | Evidence |
|
|
100
|
+
| --- | --- | --- |
|
|
101
|
+
| A step asks for credentials or a code | An app `form` around `flex flex-col gap-4`: `Input` per field with `label`, `placeholder`, `hint`, `errorMessage`, `isError`, `isDisabled`; then the status `Text size="sm" tone="muted" live` (`assertive` for a refusal, `polite` otherwise); then one `Button variant="primary" type="submit" isPending` | nivo `AuthenticationPanel`, both the `details` and `code` steps. Owner: "Credentials and their submit controls form one semantic unit" (`AuthenticationPanel/classNames.ts`) |
|
|
102
|
+
| Secondary ways onward exist | They follow the form as `TextAction size="sm"` in a wrapping row, never as a second `Button variant="primary"` | `AuthenticationPanel` resend/back row and its footer prompt. Owner: "Secondary text actions wrap cleanly instead of overflowing" |
|
|
103
|
+
|
|
104
|
+
## Seen once, not yet an idiom
|
|
105
|
+
|
|
106
|
+
Recorded so nobody re-derives them as if they were house style, and so a second occurrence can
|
|
107
|
+
promote them. A direction may not compose from this table; a request that needs one of these shapes
|
|
108
|
+
is a choice for the owner.
|
|
109
|
+
|
|
110
|
+
| Composition | Where | What a second occurrence would settle |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| Priced decision rail: `Heading level={2}` beside `Badge tone="accent"`, then price and period as one labelled fact, then the bottom action band | `ProSubscriptionBlock` plan | Whether the price/period pair is the house way to state a recurring charge |
|
|
113
|
+
| `SurfaceAccordionCard depth="top"` carrying secondary explanation with no card of its own around it | `ProSubscriptionBlock` disclosures | Whether disclosure is the house answer for content that must be present but not read |
|
|
114
|
+
| Orientation stack before the first band: breadcrumb, then `SectionHeader composition="context-intro" level={1}`, inside a `max-w-3xl` column | `ProSubscriptionBlock` | Whether a route opens with orientation rather than with its first surface |
|
|
115
|
+
| Provider shortcut above a labelled `Divider`, before the credential form | nivo `AuthenticationPanel` | Whether the shortcut always precedes the form |
|
|
116
|
+
| A leading `IconTile` beside a copy block | `ContinueLearning` | Whether identity art belongs beside copy or above it |
|
|
117
|
+
|
|
118
|
+
The owner's own proposal records one further shape — a muted supporting line followed by an action —
|
|
119
|
+
at two instances and declines to promote it ("Two instances are not a pattern yet; it stays
|
|
120
|
+
app-owned"). It stays out of this file until the owner says otherwise.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# StarCi Core — idiom
|
|
2
|
+
|
|
3
|
+
Idiom là một cách ghép mà StarCi quay lại dùng: một lối lắp các renderer Grammar đã công bố mà chủ sở
|
|
4
|
+
hữu đã chọn hơn một lần. File này là gu, không phải luật. Luật universal nằm ở
|
|
5
|
+
[knowledge/ui](../../ui/INDEX.vi.md); còn mỗi renderer là gì và sở hữu cái gì thì nằm ở
|
|
6
|
+
[DNA](DNA.vi.md), sinh ra từ package. Ở đây không kể lại giải phẫu renderer, và cũng không bảo ai đi
|
|
7
|
+
tìm cảm hứng ở đâu: một định hướng được ghép từ các idiom này cộng với hình dạng nghiệp vụ, còn tham
|
|
8
|
+
chiếu, khi yêu cầu có kèm, được đọc xuyên qua chúng.
|
|
9
|
+
|
|
10
|
+
Điều kiện để một cách ghép thành idiom chỉ có một: xuất hiện ít nhất hai lần trong các block bằng
|
|
11
|
+
chứng — starci `src/components/blocks/dashboard/*` và
|
|
12
|
+
`src/components/blocks/commerce/ProSubscriptionBlock`, cùng nivo
|
|
13
|
+
`apps/app/src/components/blocks/auth/AuthenticationPanel`. Cách ghép mới thấy một lần được ghi ở cuối
|
|
14
|
+
file và không bao giờ được đem ra ghép. Chỗ nào chủ sở hữu đã tự viết ra ý định thì câu đó được trích
|
|
15
|
+
nguyên từ `classNames.ts` của chính block, vì đó là lời của chủ sở hữu về lý do hình dạng ấy tồn tại.
|
|
16
|
+
Mọi ô composition chỉ gọi tên renderer và prop mà package thực sự công bố.
|
|
17
|
+
|
|
18
|
+
## Joined bands in one flush card
|
|
19
|
+
|
|
20
|
+
Một thẻ duy nhất, không thẻ lồng: thân thẻ phẳng, và chỉ có đường kẻ ngang chia nó thành các dải.
|
|
21
|
+
|
|
22
|
+
| Dùng khi | Cách ghép Grammar | Bằng chứng |
|
|
23
|
+
| --- | --- | --- |
|
|
24
|
+
| Một mục dashboard chứa từ hai loại nội dung trở lên nhưng cùng thuộc một tiêu đề | `SurfaceCard label composition="joined"` bọc một thân `flex flex-col` của app mà các con là các dải; thẻ sở hữu biên ngoài và việc cắt, app sở hữu `border-t border-separator` giữa các dải | 10 block dashboard: `ChangelogList`, `ContinueLearning`, `DailyQuest`, `FeedExplorer`, `JobReadinessWidget`, `OverviewContributions`, `StreakStrip`, `TrendingContents`, `WeeklyChallengeCard`, `WeeklyGoals`. Chủ sở hữu: "Stack full-bleed bands inside one bounded dashboard surface", "Separate stacked dashboard bands without inventing extra vertical space" (`blocks/dashboard/classNames.ts`) |
|
|
25
|
+
| Một dải là danh sách các phần tử ngang hàng | `ul`/`div` của app với `m-0 list-none p-0 divide-y divide-separator`; mỗi hàng `px-4 pt-3 pb-3 last:pb-4`, nên lề trong không đổi và chỉ mép dưới ngoài cùng mới nới ra | `ChangelogList`, dải task của `DailyQuest`, trụ đo của `JobReadinessWidget`, hàng finisher của `WeeklyChallengeCard`, `MyCoursesProgress`, `RecommendedCourses`, lưới quyền lợi bên commerce. Chủ sở hữu: "One divided row inside a flush dashboard list: px-4 always; pb-4 only on the bottom edge" (`blocks/dashboard/classNames.ts`) |
|
|
26
|
+
| Một mục giải thích mang cả chữ, tranh và danh sách | Vẫn một thẻ ấy, ba thứ thành ba dải nối nhau thay vì ba thẻ | Thẻ quyền lợi của `ProSubscriptionBlock`: dải mở đầu, dải tranh hành trình, lưới quyền lợi trong cùng một `SurfaceCard label composition={"joined"}` |
|
|
27
|
+
|
|
28
|
+
## A neutral band opens the card with its summary
|
|
29
|
+
|
|
30
|
+
Câu trả lời một dòng của thẻ nằm trước, trên nền phụ, và tách khỏi phần bằng chứng bên dưới — là bản
|
|
31
|
+
tóm tắt, không phải một phán quyết.
|
|
32
|
+
|
|
33
|
+
| Dùng khi | Cách ghép Grammar | Bằng chứng |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| Thẻ nêu một số đo trước khi trưng bằng chứng cho số đo ấy | Dải của app `bg-surface-secondary text-foreground px-4 pt-4 pb-3` làm con đầu tiên của thẻ joined, rồi `border-t border-separator` | Dải đầu của `JobReadinessWidget`, dải tóm tắt của `WeeklyGoals`, dải đếm ngược của `WeeklyChallengeCard`, dải mở đầu quyền lợi của `ProSubscriptionBlock` |
|
|
36
|
+
| Dải nằm giữa hai đường kẻ chứ không ở trên cùng | Vẫn cách xử lý ấy nhưng `px-4 py-3` | Dải phần thưởng của `DailyQuest`. Chủ sở hữu: "Neutral band between separators: px-4 always; p-3 on both separator sides vertically" (`blocks/dashboard/classNames.ts`) |
|
|
37
|
+
| Bản tóm tắt là kết quả đã chứng minh, không phải lời hứa còn treo | Chỉ khi đó dải mới nhận màu trạng thái; lời hứa chưa nhận vẫn trung tính | Chủ sở hữu: "Keep an unclaimed promise neutral; only a proven claimed outcome receives success" (`DailyQuest/classNames.ts`); "Present weekly progress as a neutral summary rather than a state outcome" (`WeeklyGoals/classNames.ts`); [TRUTH-1..4](../../ui/proof/render-truth.vi.md) |
|
|
38
|
+
|
|
39
|
+
## The card's one action closes the bottom band
|
|
40
|
+
|
|
41
|
+
Một thẻ có nhiều nhất một hành động đi tiếp, và nó nằm trong dải riêng sát mép dưới thẻ — không bao
|
|
42
|
+
giờ trôi nổi giữa nội dung.
|
|
43
|
+
|
|
44
|
+
| Dùng khi | Cách ghép Grammar | Bằng chứng |
|
|
45
|
+
| --- | --- | --- |
|
|
46
|
+
| Thẻ mở ra một lối đi tiếp | Dải của app `border-t border-separator px-4 pb-4 pt-3` làm con cuối cùng, chứa một `Button` hoặc `TextAction` | `JobReadinessWidget`, `StreakStrip`, `WeeklyChallengeCard`, `WeeklyGoals`. Chủ sở hữu: "Keep a card's lone action separated at the bottom edge" (`blocks/dashboard/classNames.ts`) |
|
|
47
|
+
| Hành động là mua và phải sống được trên màn hình hẹp | Vẫn dải ấy nhưng `grid grid-cols-1 gap-2`, `Button variant="primary"` kéo hết bề ngang dải, và tuỳ chọn một `Text size="xs" tone="muted"` bên dưới | `ProSubscriptionBlock`. Chủ sở hữu: "Full-bleed divider and inset action content for the purchase boundary" (`ProSubscriptionBlock/classNames.ts`) |
|
|
48
|
+
|
|
49
|
+
## Generated art is a band, not a card
|
|
50
|
+
|
|
51
|
+
Tranh sinh ra được cấp một dải bên trong chính mặt phẳng mà nó thuộc về. Nó không thành thẻ thứ hai,
|
|
52
|
+
và cũng không bị đóng khung thụt vào để rồi lộ viền đen hai bên.
|
|
53
|
+
|
|
54
|
+
| Dùng khi | Cách ghép Grammar | Bằng chứng |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| Bức tranh chính là điều mục ấy muốn nói | Một dải tràn viền riêng bên trong thẻ joined: `border-t border-separator` phía trên, không lề trong, ảnh để `block h-auto w-full` để tỉ lệ gốc còn nguyên | Dải hành trình của `ProSubscriptionBlock`. Chủ sở hữu: "The generated journey is its own edge-to-edge joined band; the SurfaceCard owns outer clipping" và "Preserve the generated asset ratio without an inset frame or letterbox" (`ProSubscriptionBlock/classNames.ts`) |
|
|
57
|
+
| Bức tranh là phần thưởng hoặc gợi mở mang tính trang trí | `MediaFrame aspect="landscape" fit="contain" treatment="plain"` đặt trong dải `bg-accent-soft` của app, hoặc một mảng accent với tranh neo ở mép sau và `alt=""`/`aria-hidden` | Khối tranh của `TrendingContents` — chủ sở hữu: "Keep generated discovery media prominent without becoming a separate card"; hero của `DailyQuest` — chủ sở hữu: "The generated quest illustration stays decorative on the hero's trailing edge" |
|
|
58
|
+
|
|
59
|
+
## Title and one supporting line
|
|
60
|
+
|
|
61
|
+
Hai dòng xếp sát nhau: tên của thứ đang nói, rồi một dòng mờ bổ nghĩa cho nó. Đây là quan hệ lặp lại
|
|
62
|
+
nhiều nhất trong toàn bộ tập mặt phẳng.
|
|
63
|
+
|
|
64
|
+
| Dùng khi | Cách ghép Grammar | Bằng chứng |
|
|
65
|
+
| --- | --- | --- |
|
|
66
|
+
| Ở trong một hàng hay một ô, khi tiêu đề không phải cấu trúc tài liệu | `Text size="sm" weight="semibold"` rồi `Text size="xs" tone="muted"`, xếp chồng ở [GAP-1](../../ui/presentation/gap.vi.md) | Các hàng dashboard và các hàng quyền lợi bên commerce. Chủ sở hữu: "Title and explanation stack for one outcome" (`ProSubscriptionBlock/classNames.ts`) |
|
|
67
|
+
| Tiêu đề là tên của chính mặt phẳng | `Heading` rồi `Text size="sm" tone="muted"` | Phần đầu và cây thông báo của nivo `AuthenticationPanel`. Chủ sở hữu: "Title and subtitle stay visually coupled" (`AuthenticationPanel/classNames.ts`) |
|
|
68
|
+
| Phần bổ nghĩa phải nằm trên tiêu đề | `Text size="xs" tone="muted"` trước, rồi `Text size="md" weight="semibold"` | `ContinueLearning`. Chủ sở hữu: "Keep the supporting kind close to the destination title it qualifies" |
|
|
69
|
+
| Muốn Grammar sở hữu cặp này | `SurfaceCopyGroup` — renderer duy nhất được công bố cho nhịp ấy | Trạng thái mua của `ProSubscriptionBlock` |
|
|
70
|
+
|
|
71
|
+
Đếm mỗi container một lần trên toàn bộ block bằng chứng, cặp này xuất hiện tám lần, bảy trong số đó
|
|
72
|
+
dựng tay bằng `div` của app; bản kiểm kê và đề xuất cấp slot có kiểu cho `SurfaceCopyGroup` nằm ở
|
|
73
|
+
[audits/1.0.1/proposals/copy-group-composite.md](../../../audits/1.0.1/proposals/copy-group-composite.md).
|
|
74
|
+
|
|
75
|
+
## Pending is the same tree, resting
|
|
76
|
+
|
|
77
|
+
Mặt phẳng đang tải chính là mặt phẳng hoàn chỉnh với nội dung nghỉ: cùng thẻ, cùng dải, cùng số hàng.
|
|
78
|
+
Nó không bao giờ là một vòng xoay, cũng không bao giờ là một cây khác.
|
|
79
|
+
|
|
80
|
+
| Dùng khi | Cách ghép Grammar | Bằng chứng |
|
|
81
|
+
| --- | --- | --- |
|
|
82
|
+
| Block đang đợi dữ liệu | `SurfaceCard state={loading ? "pending" : "neutral"}`, một mảng phần tử nghỉ có độ dài cố định thay cho dữ liệu thật, và `isSkeleton` trên mọi `Text` và `Heading` bên trong | `ChangelogList`, `ContinueLearning`, `DailyQuest`, `JobReadinessWidget`, `OverviewContributions`, `StreakStrip`, `WeeklyChallengeCard`, `WeeklyGoals`, `ProSubscriptionBlock` |
|
|
83
|
+
| Hành động đã có nhưng chưa được phép chạy | Vẫn cây ấy với `Button isDisabled` thay vì gỡ nút đi, để bố cục không nhảy khi dữ liệu về | Hành động mua của `ProSubscriptionBlock` |
|
|
84
|
+
| Block không có dữ liệu nào, hoặc lần đọc thất bại | Một cây khác hẳn: `SurfaceCard composition="single"` bọc một `EmptyNotice` với `message`, `actionLabel` tuỳ chọn và `iconSource` — rỗng và hỏng là state, không phải biến thể nghỉ | `ContinueLearning`, `DailyQuest`, `TrendingContents`; [STATE-1](../../ui/composition/state.vi.md) |
|
|
85
|
+
|
|
86
|
+
## One highlighted card
|
|
87
|
+
|
|
88
|
+
Trong một khung nhìn, nhiều nhất một thẻ mang `isHighlight`, và đó là việc người đọc nên làm tiếp
|
|
89
|
+
theo. Highlight là một khẳng định về nhiệm vụ, không phải trang trí.
|
|
90
|
+
|
|
91
|
+
| Dùng khi | Cách ghép Grammar | Bằng chứng |
|
|
92
|
+
| --- | --- | --- |
|
|
93
|
+
| Các phần tử ngang hàng cùng hiện nhưng một cái là bước kế tiếp có chủ đích | `SurfaceCard isHighlight={true}` chỉ trên đúng thẻ đó; các thẻ còn lại giữ nguyên mọi prop khác, để khác biệt đọc ra là thứ hạng chứ không phải một component khác | `ContinueLearning`, nơi chỉ mục học dở đầu tiên được làm nổi — chủ sở hữu: "The first resumable item is the focal task; later items remain useful but quieter" |
|
|
94
|
+
| Khung nhìn tồn tại để chốt một quyết định | Thẻ quyết định là thẻ được làm nổi và nằm ở rail của `PrimaryRailLayout`; mọi thứ giải thích ở lại `primary` và không làm nổi | Rail gói cước của `ProSubscriptionBlock` |
|
|
95
|
+
|
|
96
|
+
## Single-column form stack
|
|
97
|
+
|
|
98
|
+
Các ô nhập, rồi câu trạng thái, rồi một nút gửi chính — một cột, một nút gửi, đúng thứ tự ấy.
|
|
99
|
+
|
|
100
|
+
| Dùng khi | Cách ghép Grammar | Bằng chứng |
|
|
101
|
+
| --- | --- | --- |
|
|
102
|
+
| Một bước hỏi thông tin đăng nhập hoặc mã | `form` của app bọc `flex flex-col gap-4`: mỗi ô là `Input` với `label`, `placeholder`, `hint`, `errorMessage`, `isError`, `isDisabled`; rồi câu trạng thái `Text size="sm" tone="muted" live` (`assertive` khi là lời từ chối, còn lại `polite`); rồi một `Button variant="primary" type="submit" isPending` | nivo `AuthenticationPanel`, cả bước `details` lẫn bước `code`. Chủ sở hữu: "Credentials and their submit controls form one semantic unit" (`AuthenticationPanel/classNames.ts`) |
|
|
103
|
+
| Có những lối đi tiếp phụ | Chúng đứng sau form dưới dạng `TextAction size="sm"` trong một hàng biết xuống dòng, không bao giờ là một `Button variant="primary"` thứ hai | Hàng gửi lại/quay lại và dòng chân của `AuthenticationPanel`. Chủ sở hữu: "Secondary text actions wrap cleanly instead of overflowing" |
|
|
104
|
+
|
|
105
|
+
## Seen once, not yet an idiom
|
|
106
|
+
|
|
107
|
+
Ghi lại để không ai suy ngược chúng thành gu nhà, và để lần xuất hiện thứ hai có thể nâng chúng lên.
|
|
108
|
+
Một định hướng không được ghép từ bảng này; yêu cầu nào cần đúng những hình dạng ấy là một lựa chọn
|
|
109
|
+
dành cho chủ sở hữu.
|
|
110
|
+
|
|
111
|
+
| Cách ghép | Ở đâu | Lần thứ hai sẽ chốt điều gì |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| Rail quyết định có giá: `Heading level={2}` cạnh `Badge tone="accent"`, rồi giá và kỳ hạn thành một dữ kiện có nhãn, rồi dải hành động dưới đáy | Gói cước của `ProSubscriptionBlock` | Cặp giá/kỳ hạn có phải cách nhà này nêu một khoản thu định kỳ hay không |
|
|
114
|
+
| `SurfaceAccordionCard depth="top"` mang nội dung giải thích phụ mà không có thẻ nào bọc ngoài | Phần công bố của `ProSubscriptionBlock` | Gấp mở có phải câu trả lời của nhà cho nội dung buộc phải có mà không buộc phải đọc hay không |
|
|
115
|
+
| Khối định hướng trước dải đầu tiên: breadcrumb, rồi `SectionHeader composition="context-intro" level={1}`, trong một cột `max-w-3xl` | `ProSubscriptionBlock` | Một route có mở đầu bằng định hướng thay vì bằng mặt phẳng đầu tiên hay không |
|
|
116
|
+
| Lối tắt qua nhà cung cấp đặt trên một `Divider` có nhãn, trước form thông tin đăng nhập | nivo `AuthenticationPanel` | Lối tắt có luôn đứng trước form hay không |
|
|
117
|
+
| Một `IconTile` dẫn đầu đặt cạnh khối chữ | `ContinueLearning` | Tranh định danh thuộc về bên cạnh chữ hay bên trên chữ |
|
|
118
|
+
|
|
119
|
+
Chính đề xuất của chủ sở hữu còn ghi một hình dạng nữa — một dòng mờ bổ trợ rồi tới một hành động —
|
|
120
|
+
với hai lần xuất hiện, và từ chối nâng nó lên ("Two instances are not a pattern yet; it stays
|
|
121
|
+
app-owned"). Nó ở ngoài file này cho tới khi chủ sở hữu nói khác.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# StarCi Core — playbook
|
|
2
|
+
|
|
3
|
+
Which idioms a business shape asks for, and what a supplied reference is allowed to contribute to
|
|
4
|
+
that answer. The idioms themselves are in [Idioms](idioms.md); what the package publishes is in
|
|
5
|
+
[DNA](DNA.md). A shape not listed here is not forbidden — it is undecided, and an undecided shape is
|
|
6
|
+
a question for the owner, not a guess.
|
|
7
|
+
|
|
8
|
+
## Shapes
|
|
9
|
+
|
|
10
|
+
| Business shape | What a supplied reference may contribute | Idiom sequence |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| Purchase decision page — one offer, one price, one commitment (subscriptions) | Keep: the order of regions (what is read before the price), which single decision dominates, and what folds away instead of competing with it. Never crosses over: page layout, brand, palette, type scale, component anatomy, or any control the reference draws that this Grammar does not publish. | Explanatory column in `primary` of `PrimaryRailLayout`, decision card in `rail`: **Joined bands in one flush card** for the explanation → **A neutral band opens the card with its summary** for its intro line → **Generated art is a band, not a card** for the journey asset → **Title and one supporting line** for each included outcome → **One highlighted card** on the decision itself → **The card's one action closes the bottom band** for the purchase → **Pending is the same tree, resting** across the offer read. Secondary explanation folds away; whether it folds into a disclosure is a `DIRECTION_CHOICE_REQUIRED` (seen once). |
|
|
13
|
+
| Learner dashboard — many peer sections, one intended next step | Keep: which sections exist and their reading order, and which single section is the reader's next action. Never crosses over: grid geometry, card chrome, densities, or a section that would need a renderer this family does not publish. | Per section: **Joined bands in one flush card** → **A neutral band opens the card with its summary** where the section states a measure → **Title and one supporting line** inside every row → **The card's one action closes the bottom band** where a section leads somewhere → **Pending is the same tree, resting** for every read, with `EmptyNotice` for empty and failed. Across the page: **One highlighted card** exactly once, on the resume or next-step section. |
|
|
14
|
+
| Sign-in — one surface, several journeys, one submit per step | Keep: the order of the ways in (shortcut before form, or the reverse), which journeys share one surface, and where the way to the other journey sits. Never crosses over: provider branding, illustration, field chrome, or a social-login row this product does not have a provider for. | **Title and one supporting line** as the surface header (`Heading` + muted subtitle) → **Single-column form stack** per step, one `Button variant="primary" type="submit"` each → **Pending is the same tree, resting** through `isPending` and `isDisabled` rather than a replaced tree → the way onward as `TextAction`, last. Whether the provider shortcut precedes the form is seen once; carry the reference's order and say so. |
|
|
15
|
+
|
|
16
|
+
## How a direction reads this
|
|
17
|
+
|
|
18
|
+
1. **Business shape first.** Name the shape and the one outcome the surface exists to produce. The
|
|
19
|
+
shape selects the idiom sequence; nothing else does.
|
|
20
|
+
2. **Then the reference, if the request supplied one.** A reference contributes region order, which
|
|
21
|
+
decision dominates, and what folds away. It never contributes layout, brand, palette, or
|
|
22
|
+
component anatomy — those are already owned, by this family and by
|
|
23
|
+
[knowledge/ui](../../ui/INDEX.md). A reference that can only be honoured by breaking an idiom is
|
|
24
|
+
evidence about the reference, not about StarCi.
|
|
25
|
+
3. **Then the idioms**, composed in the order the shape's row gives, each one taken whole: an idiom
|
|
26
|
+
is a relationship, and half of it is a different shape.
|
|
27
|
+
4. **Then [DNA](DNA.md)**, to confirm every renderer and prop the composition names exists, and to
|
|
28
|
+
read the gaps in [Family and DNA](family.md) before promising a capability. A composition that
|
|
29
|
+
needs a recorded gap is not a direction yet.
|
|
30
|
+
|
|
31
|
+
Anything still open after those four steps is not resolved by preference: it stops with
|
|
32
|
+
`DIRECTION_CHOICE_REQUIRED` and goes to the owner. That includes any shape absent from the table
|
|
33
|
+
above, any composition that appears only in the idioms file's seen-once table, and any point where
|
|
34
|
+
the reference and an idiom disagree.
|
|
35
|
+
|
|
36
|
+
Owner ruling 2026-09-03 on images: a direction that leaves a region reading empty adds an image made to one claim of the promise, on its own judgement, and records why; it never waits to be asked and never decorates a region the copy and the Grammar objects already carry. `frontend.direction.decide` applies this in its `## Images` table.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# StarCi Core — playbook
|
|
2
|
+
|
|
3
|
+
Hình dạng nghiệp vụ nào đòi chuỗi idiom nào, và một tham chiếu được phép góp gì vào câu trả lời ấy.
|
|
4
|
+
Bản thân các idiom nằm ở [Idiom](idioms.vi.md); còn package công bố những gì thì nằm ở
|
|
5
|
+
[DNA](DNA.vi.md). Một hình dạng không có trong đây không phải là bị cấm — nó là chưa được quyết, mà
|
|
6
|
+
chưa được quyết thì hỏi chủ sở hữu, không đoán.
|
|
7
|
+
|
|
8
|
+
## Các hình dạng
|
|
9
|
+
|
|
10
|
+
| Hình dạng nghiệp vụ | Tham chiếu được góp gì | Chuỗi idiom |
|
|
11
|
+
| --- | --- | --- |
|
|
12
|
+
| Trang quyết định mua — một gói, một giá, một cam kết (thuê bao) | Giữ: thứ tự các vùng (cái gì được đọc trước giá), quyết định duy nhất nào chiếm ưu thế, và cái gì gấp lại thay vì tranh chỗ với nó. Không bao giờ theo sang: bố cục trang, thương hiệu, bảng màu, thang chữ, giải phẫu component, hay bất kỳ control nào tham chiếu vẽ mà Grammar này không công bố. | Cột giải thích ở `primary` của `PrimaryRailLayout`, thẻ quyết định ở `rail`: **Joined bands in one flush card** cho phần giải thích → **A neutral band opens the card with its summary** cho dòng mở đầu → **Generated art is a band, not a card** cho tranh hành trình → **Title and one supporting line** cho từng quyền lợi → **One highlighted card** cho chính thẻ quyết định → **The card's one action closes the bottom band** cho hành động mua → **Pending is the same tree, resting** suốt lần đọc gói. Nội dung phụ thì gấp lại; gấp bằng disclosure hay không là một `DIRECTION_CHOICE_REQUIRED` (mới thấy một lần). |
|
|
13
|
+
| Dashboard người học — nhiều mục ngang hàng, một bước kế tiếp có chủ đích | Giữ: có những mục nào và đọc theo thứ tự nào, và mục nào là việc kế tiếp của người đọc. Không bao giờ theo sang: hình học lưới, viền thẻ, mật độ, hay một mục đòi renderer mà family này không công bố. | Trong từng mục: **Joined bands in one flush card** → **A neutral band opens the card with its summary** khi mục nêu một số đo → **Title and one supporting line** trong mọi hàng → **The card's one action closes the bottom band** khi mục dẫn đi đâu đó → **Pending is the same tree, resting** cho mọi lần đọc, với `EmptyNotice` cho rỗng và hỏng. Trên toàn trang: **One highlighted card** đúng một lần, đặt ở mục học tiếp hoặc bước kế tiếp. |
|
|
14
|
+
| Đăng nhập — một mặt phẳng, nhiều hành trình, mỗi bước một nút gửi | Giữ: thứ tự các lối vào (lối tắt trước form hay ngược lại), những hành trình nào dùng chung một mặt phẳng, và lối sang hành trình kia nằm ở đâu. Không bao giờ theo sang: thương hiệu nhà cung cấp, tranh minh hoạ, viền ô nhập, hay một hàng đăng nhập mạng xã hội mà sản phẩm này không có nhà cung cấp. | **Title and one supporting line** làm phần đầu mặt phẳng (`Heading` cộng dòng phụ mờ) → **Single-column form stack** cho mỗi bước, mỗi bước đúng một `Button variant="primary" type="submit"` → **Pending is the same tree, resting** qua `isPending` và `isDisabled` chứ không thay cây khác → lối đi tiếp là `TextAction`, đặt cuối. Lối tắt có đứng trước form hay không thì mới thấy một lần; theo thứ tự của tham chiếu và nói rõ ra. |
|
|
15
|
+
|
|
16
|
+
## Một định hướng đọc file này thế nào
|
|
17
|
+
|
|
18
|
+
1. **Hình dạng nghiệp vụ trước.** Gọi tên hình dạng và kết quả duy nhất mà mặt phẳng ấy tồn tại để
|
|
19
|
+
tạo ra. Hình dạng chọn chuỗi idiom; không gì khác chọn thay.
|
|
20
|
+
2. **Rồi mới tới tham chiếu, nếu yêu cầu có kèm.** Tham chiếu góp thứ tự vùng, quyết định nào chiếm
|
|
21
|
+
ưu thế, và cái gì gấp lại. Nó không góp bố cục, thương hiệu, bảng màu hay giải phẫu component —
|
|
22
|
+
những thứ đó đã có chủ, là family này và [knowledge/ui](../../ui/INDEX.vi.md). Một tham chiếu chỉ
|
|
23
|
+
theo được bằng cách phá vỡ một idiom là bằng chứng về tham chiếu, không phải về StarCi.
|
|
24
|
+
3. **Rồi tới các idiom**, ghép theo đúng thứ tự dòng của hình dạng, và lấy trọn từng cái: idiom là
|
|
25
|
+
một quan hệ, lấy một nửa thì thành hình dạng khác.
|
|
26
|
+
4. **Rồi tới [DNA](DNA.vi.md)**, để xác nhận mọi renderer và prop mà cách ghép gọi tên đều tồn tại,
|
|
27
|
+
và để đọc bảng gap trong [Family và DNA](family.vi.md) trước khi hứa một năng lực. Cách ghép nào
|
|
28
|
+
cần tới một gap đã ghi thì chưa phải một định hướng.
|
|
29
|
+
|
|
30
|
+
Cái gì còn mở sau bốn bước ấy thì không giải bằng sở thích: nó dừng lại với
|
|
31
|
+
`DIRECTION_CHOICE_REQUIRED` và về tay chủ sở hữu. Trong đó có mọi hình dạng vắng mặt ở bảng trên, mọi
|
|
32
|
+
cách ghép chỉ nằm ở bảng "mới thấy một lần" của file idiom, và mọi điểm mà tham chiếu với idiom nói
|
|
33
|
+
ngược nhau.
|
|
34
|
+
|
|
35
|
+
Chốt của owner ngày 2026-09-03 về hình: hướng nào để lại một vùng đọc lên thấy trống thì tự thêm hình làm theo đúng một claim của lời hứa, tự xét và ghi lý do; không bao giờ chờ được yêu cầu và không bao giờ trang trí vùng mà chữ và Grammar object đã gánh được. `frontend.direction.decide` áp điều này trong bảng `## Images` của nó.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Backend code patterns
|
|
2
|
+
|
|
3
|
+
`knowledge/ui/` decides what the interface must be. `knowledge/patterns/be/` decides how the
|
|
4
|
+
NestJS code behind it is written: which root a file lives under, what a GraphQL unit's files are
|
|
5
|
+
called, how a handler is shaped, which alias an import uses, where a docblock sits relative to a
|
|
6
|
+
decorator, how an exception is declared and where it is mapped to HTTP and GraphQL, and where the
|
|
7
|
+
spec goes. Every rule below was extracted from `starci-academy-backend/src/` by opening files and
|
|
8
|
+
counting; each table cites its sources. Where the code is split, the file records the dominant
|
|
9
|
+
variant with its count instead of legislating. Lint rule names from `@starci/eslint-canon-be` are
|
|
10
|
+
quoted only where the code already follows them.
|
|
11
|
+
|
|
12
|
+
## Catalog
|
|
13
|
+
|
|
14
|
+
| Knowledge | What it decides | Rules |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| [Folder](folder.md) | `features/` versus `modules/`, the file set of one GraphQL unit, where exceptions, entities and tests live | BE-FOLDER-1 … BE-FOLDER-7 |
|
|
17
|
+
| [Naming](naming.md) | Kebab file suffixes, class role suffixes, exception identity, enums, constants, methods | BE-NAMING-1 … BE-NAMING-8 |
|
|
18
|
+
| [Function](function.md) | Handler `process`, message envelope, service and resolver `execute`, constructor injection, helpers | BE-FUNCTION-1 … BE-FUNCTION-8 |
|
|
19
|
+
| [Imports](imports.md) | `@modules`/`@features`/`@tests` aliases, brace style, order, layering direction, banned imports | BE-IMPORTS-1 … BE-IMPORTS-7 |
|
|
20
|
+
| [Comment](comment.md) | Docblock after decorators, responsibility over name, enum member docs, `//` prose, ASCII and `vn-ok` | BE-COMMENT-1 … BE-COMMENT-7 |
|
|
21
|
+
| [Typing](typing.md) | `interface` over `type`, named params, `readonly`, enums, no `any`, GraphQL classes | BE-TYPING-1 … BE-TYPING-7 |
|
|
22
|
+
| [Error](error.md) | `AbstractException` shape, metadata, wrapping, the HTTP filter, the GraphQL interceptor and `formatError` | BE-ERROR-1 … BE-ERROR-7 |
|
|
23
|
+
| [Test](test.md) | Colocated `.spec.ts`, lanes by suffix, direct construction with `as never`, what an assertion proves | BE-TEST-1 … BE-TEST-7 |
|
|
24
|
+
|
|
25
|
+
## Sources
|
|
26
|
+
|
|
27
|
+
`D:\Repositories\starci-academy-backend\src\` (4463 non-spec TypeScript files, 875 unit specs,
|
|
28
|
+
7 integration specs), `apps/core/src/app.module.ts` for filter registration, `tsconfig.json`,
|
|
29
|
+
`jest.config.ts`, `eslint.config.mjs`.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Mẫu mã nguồn backend
|
|
2
|
+
|
|
3
|
+
`knowledge/ui/` quyết định giao diện phải là gì. `knowledge/patterns/be/` quyết định mã NestJS
|
|
4
|
+
đứng sau giao diện ấy được viết ra sao: một tệp nằm dưới gốc nào, các tệp của một đơn vị GraphQL
|
|
5
|
+
tên gì, một handler có hình dạng nào, một import dùng alias nào, docblock đứng ở đâu so với
|
|
6
|
+
decorator, một exception được khai báo thế nào và được ánh xạ sang HTTP và GraphQL ở đâu, và spec
|
|
7
|
+
nằm chỗ nào. Mọi luật dưới đây được rút ra từ `starci-academy-backend/src/` bằng cách mở tệp và
|
|
8
|
+
đếm; mỗi bảng dẫn nguồn của nó. Nơi nào mã nguồn chia hai ngả, tệp ghi lại biến thể chiếm ưu thế
|
|
9
|
+
cùng con số thay vì áp đặt. Tên luật lint từ `@starci/eslint-canon-be` chỉ được dẫn ở nơi mã đã
|
|
10
|
+
tuân theo.
|
|
11
|
+
|
|
12
|
+
## Danh mục
|
|
13
|
+
|
|
14
|
+
| Tri thức | Quyết định điều gì | Luật |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| [Thư mục](folder.vi.md) | `features/` so với `modules/`, bộ tệp của một đơn vị GraphQL, nơi đặt exception, entity và kiểm thử | BE-FOLDER-1 … BE-FOLDER-7 |
|
|
17
|
+
| [Đặt tên](naming.vi.md) | Hậu tố tệp kebab, hậu tố vai trò của lớp, danh tính exception, enum, hằng, phương thức | BE-NAMING-1 … BE-NAMING-8 |
|
|
18
|
+
| [Hàm](function.vi.md) | `process` của handler, phong bì thông điệp, `execute` của service và resolver, tiêm qua constructor, helper | BE-FUNCTION-1 … BE-FUNCTION-8 |
|
|
19
|
+
| [Import](imports.vi.md) | Alias `@modules`/`@features`/`@tests`, kiểu ngoặc, thứ tự, chiều phân tầng, import bị cấm | BE-IMPORTS-1 … BE-IMPORTS-7 |
|
|
20
|
+
| [Chú thích](comment.vi.md) | Docblock sau decorator, trách nhiệm thay vì tên, chú thích thành viên enum, câu `//`, ASCII và `vn-ok` | BE-COMMENT-1 … BE-COMMENT-7 |
|
|
21
|
+
| [Kiểu](typing.vi.md) | `interface` thay `type`, tham số có tên, `readonly`, enum, không `any`, lớp GraphQL | BE-TYPING-1 … BE-TYPING-7 |
|
|
22
|
+
| [Lỗi](error.vi.md) | Hình dạng `AbstractException`, metadata, bọc lỗi, bộ lọc HTTP, interceptor GraphQL và `formatError` | BE-ERROR-1 … BE-ERROR-7 |
|
|
23
|
+
| [Kiểm thử](test.vi.md) | `.spec.ts` kề bên, làn theo hậu tố, khởi tạo trực tiếp với `as never`, điều một khẳng định chứng minh | BE-TEST-1 … BE-TEST-7 |
|
|
24
|
+
|
|
25
|
+
## Nguồn
|
|
26
|
+
|
|
27
|
+
`D:\Repositories\starci-academy-backend\src\` (4463 tệp TypeScript không phải spec, 875 spec đơn
|
|
28
|
+
vị, 7 spec tích hợp), `apps/core/src/app.module.ts` cho việc đăng ký bộ lọc, `tsconfig.json`,
|
|
29
|
+
`jest.config.ts`, `eslint.config.mjs`.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Comment
|
|
2
|
+
|
|
3
|
+
This file answers one question: given a backend declaration or statement, does it carry a
|
|
4
|
+
comment, where does it sit, and what does it say?
|
|
5
|
+
|
|
6
|
+
Sources: `features/api/core/graphql/mutations/courses/add-to-cart/*`,
|
|
7
|
+
`features/api/core/graphql/queries/courses/course/course.handler.ts`,
|
|
8
|
+
`modules/platform/exceptions/errors/abstract.ts`, `errors/courses/challenge-not-found.ts`,
|
|
9
|
+
`modules/platform/exceptions/filters/abstract-exception-http.filter.ts`,
|
|
10
|
+
`modules/databases/postgresql/primary/enums/locale.ts`,
|
|
11
|
+
`modules/api/apollo/server/monolithic/monolithic-apollo-server.module.ts`, `eslint.config.mjs`.
|
|
12
|
+
|
|
13
|
+
## BE-COMMENT-1 — Every export has a docblock, placed after the decorators
|
|
14
|
+
|
|
15
|
+
1676 of 2009 non-spec files under `features/api/core/graphql` contain a docblock; lint
|
|
16
|
+
`require-export-jsdoc` is at error. The block sits between the class decorators and `export class`.
|
|
17
|
+
|
|
18
|
+
| Case | When | Write |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| Case 1 | Resolver | `@Resolver()\n/**\n * GraphQL entry for adding a course to the current user's shopping cart.\n */\nexport class AddToCartResolver` |
|
|
21
|
+
| Case 2 | Handler | `@CommandHandler(AddToCartCommand)\n@Injectable()\n/**\n * Handler for the addToCart mutation.\n *\n * Idempotently places a \`(user, course)\` row into the cart: …\n */\nexport class AddToCartHandler` |
|
|
22
|
+
| Case 3 | Module | `@Module({ … })\n/** Isolated Nest registration for staging a course in the cart before checkout. */\nexport class AddToCartSingleMutationModule` |
|
|
23
|
+
| Case 4 | Input type | `@InputType({ description: "…" })\n/** Request for the addToCart mutation -- identifies the course to add to the caller's cart. */\nexport class AddToCartRequest` |
|
|
24
|
+
| Case 5 | Undecorated export | directly above: `/** CQRS command carrying the request/user context for the addToCart mutation. */\nexport class AddToCartCommand` |
|
|
25
|
+
| Case 6 | Interceptor | `@Injectable()\n/**\n * Interceptor that wraps resolver result in { data, message, success } and handles errors.\n *\n * @example\n * Use @GraphQLSuccessMessage("Done") on a resolver; …\n */` |
|
|
26
|
+
|
|
27
|
+
## BE-COMMENT-2 — The docblock states responsibility and invariant, not the name
|
|
28
|
+
|
|
29
|
+
| Case | When | Write |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| Case 1 | Why a field is nullable | `\`data\` … is marked \`nullable: true\` because the transform interceptor sets \`data = null\` on the error path -- a non-nullable field would crash GraphQL and mask the real error instead of surfacing it.` (`AddToCartResponse`) |
|
|
32
|
+
| Case 2 | Why a class exists | `Nest's default filter only maps subclasses of \`HttpException\` to their status code -- \`AbstractException\` extends plain \`Error\`, so without this filter every thrown \`AbstractException\` falls through to a generic 500.` (`AbstractExceptionHttpFilter`) |
|
|
33
|
+
| Case 3 | What must not happen | `Fails the request when the challenge id is unknown -- downstream must not grade a ghost challenge.` (`ChallengeNotFoundException`) |
|
|
34
|
+
| Case 4 | Who throws it | `Thrown by \`AiEntitlementService.consume\` when the user has no remaining allowance in one of the sliding windows, …` |
|
|
35
|
+
| Case 5 | Restated name | `/** Constructor. */` still appears in 27 files (`add-to-cart.handler.ts` among them); lint `no-restated-name-jsdoc` names it as debt, not as the pattern |
|
|
36
|
+
|
|
37
|
+
## BE-COMMENT-3 — Methods and fields
|
|
38
|
+
|
|
39
|
+
| Case | When | Write |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| Case 1 | Method | `/**\n * Processes the addToCart command.\n * @param command - The command carrying request + authenticated user.\n * @returns The cart row holding the course (created or already present).\n */` |
|
|
42
|
+
| Case 2 | Field | `/** Unique error code for identification */ readonly code: string`; `/** Id of the course the user wants to place in their cart. */ courseId: string` |
|
|
43
|
+
| Case 3 | Interface field with a cross-reference | `/** Enrollment id for the active course (user x course), injected by {@link GraphQLEnrollmentGuard} from the \`x-course-id\` header. … */ enrollmentId?: string` |
|
|
44
|
+
| Case 4 | Generic | `@template TParams - The parameters type.` (`ICQRSHandler`) |
|
|
45
|
+
|
|
46
|
+
## BE-COMMENT-4 — Enum members are each documented
|
|
47
|
+
|
|
48
|
+
| Case | When | Write |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| Case 1 | `Locale` | `/** Serve Vietnamese copy; missing Vi falls through to \`fallbackLocale\`. */ Vi = "vi", /** Serve English copy; also the usual \`defaultLocale\` / fallback. */ En = "en"` (lint `require-enum-member-jsdoc`) |
|
|
51
|
+
|
|
52
|
+
## BE-COMMENT-5 — Line comments are prose about why
|
|
53
|
+
|
|
54
|
+
9720 `//` lines exist in non-spec `src/`. They sit above the statement, in lowercase sentence
|
|
55
|
+
form, and give the consequence that motivates the code.
|
|
56
|
+
|
|
57
|
+
| Case | When | Write |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| Case 1 | A guard | `// reject unauthenticated callers up front -- cart rows are per-user` |
|
|
60
|
+
| Case 2 | A constraint | `// idempotent add: if the (user, course) row already exists, return it as-is --\n// creating a second one would violate UQ_cart_items_user_course` |
|
|
61
|
+
| Case 3 | A scope decision | `// a course the user already OWNS must not be carted -- real enrollment (paid)\n// only, so a trial/preview row (is_enrolled = false) still allows adding to cart` |
|
|
62
|
+
| Case 4 | A hand-off | `// hand off to the command handler which performs the DB work` |
|
|
63
|
+
| Case 5 | Error policy | `// any OTHER error (graphql-js's own execution errors …) is by definition an UNEXPECTED server bug, never the client's fault -- default it to 500 too` |
|
|
64
|
+
| Case 6 | The ` -- ` join | used in 1299 of the 9720 lines; a plain sentence is the majority form |
|
|
65
|
+
|
|
66
|
+
## BE-COMMENT-6 — Block comments for multi-paragraph reasoning inside a body
|
|
67
|
+
|
|
68
|
+
| Case | When | Write |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| Case 1 | `course.handler.ts` | `/*\n * EITHER IDENTIFIER ADDRESSES THE SAME OBJECT, because the synchronizer writes both.\n * \`MaterializeAndUploadService\` uploads … It used to refuse anything but a display id, … That reading was wrong about the bucket …\n */` — opens with a capitalised claim, then the evidence, then the corrected history |
|
|
71
|
+
| Case 2 | Config | the `eslint.config.mjs` blocks that record why a rule is at error, with the measured debt (`nợ=0 (296/296)`) |
|
|
72
|
+
|
|
73
|
+
## BE-COMMENT-7 — What a comment never contains
|
|
74
|
+
|
|
75
|
+
| Case | When | Write |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| Case 1 | Non-ASCII | lint `no-non-ascii-source`, `no-ai-symbol`, `no-emoji`; comments stay ASCII |
|
|
78
|
+
| Case 2 | Vietnamese | lint `no-vietnamese`; a Vietnamese string emitted to clients carries a reason: `[Locale.Vi]: "Thêm khóa học vào giỏ hàng thành công", // vn-ok: vi-locale string emitted to clients` (lint `require-vn-ok-reason`) |
|
|
79
|
+
| Case 3 | A suppression | `eslint-disable-next-line` (lint `no-line-suppression`); exceptions live in config with a comment |
|
|
80
|
+
| Case 4 | A restated name | see BE-COMMENT-2 Case 5 |
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Chú thích
|
|
2
|
+
|
|
3
|
+
Tệp này trả lời một câu hỏi: cho một khai báo hay câu lệnh backend, nó có mang chú thích không,
|
|
4
|
+
chú thích đứng ở đâu, và nói gì?
|
|
5
|
+
|
|
6
|
+
Nguồn: `features/api/core/graphql/mutations/courses/add-to-cart/*`,
|
|
7
|
+
`features/api/core/graphql/queries/courses/course/course.handler.ts`,
|
|
8
|
+
`modules/platform/exceptions/errors/abstract.ts`, `errors/courses/challenge-not-found.ts`,
|
|
9
|
+
`modules/platform/exceptions/filters/abstract-exception-http.filter.ts`,
|
|
10
|
+
`modules/databases/postgresql/primary/enums/locale.ts`,
|
|
11
|
+
`modules/api/apollo/server/monolithic/monolithic-apollo-server.module.ts`, `eslint.config.mjs`.
|
|
12
|
+
|
|
13
|
+
## BE-COMMENT-1 — Mọi export có docblock, đặt sau các decorator
|
|
14
|
+
|
|
15
|
+
1676 trên 2009 tệp không phải spec dưới `features/api/core/graphql` chứa docblock; lint
|
|
16
|
+
`require-export-jsdoc` ở mức error. Khối này nằm giữa các decorator của lớp và `export class`.
|
|
17
|
+
|
|
18
|
+
| Case | Dùng khi | Viết |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| Case 1 | Resolver | `@Resolver()\n/**\n * GraphQL entry for adding a course to the current user's shopping cart.\n */\nexport class AddToCartResolver` |
|
|
21
|
+
| Case 2 | Handler | `@CommandHandler(AddToCartCommand)\n@Injectable()\n/**\n * Handler for the addToCart mutation.\n *\n * Idempotently places a \`(user, course)\` row into the cart: …\n */\nexport class AddToCartHandler` |
|
|
22
|
+
| Case 3 | Module | `@Module({ … })\n/** Isolated Nest registration for staging a course in the cart before checkout. */\nexport class AddToCartSingleMutationModule` |
|
|
23
|
+
| Case 4 | Kiểu đầu vào | `@InputType({ description: "…" })\n/** Request for the addToCart mutation -- identifies the course to add to the caller's cart. */\nexport class AddToCartRequest` |
|
|
24
|
+
| Case 5 | Export không decorator | ngay phía trên: `/** CQRS command carrying the request/user context for the addToCart mutation. */\nexport class AddToCartCommand` |
|
|
25
|
+
| Case 6 | Interceptor | `@Injectable()\n/**\n * Interceptor that wraps resolver result in { data, message, success } and handles errors.\n *\n * @example\n * Use @GraphQLSuccessMessage("Done") on a resolver; …\n */` |
|
|
26
|
+
|
|
27
|
+
## BE-COMMENT-2 — Docblock nêu trách nhiệm và bất biến, không nêu tên
|
|
28
|
+
|
|
29
|
+
| Case | Dùng khi | Viết |
|
|
30
|
+
| --- | --- | --- |
|
|
31
|
+
| Case 1 | Vì sao một trường nullable | `\`data\` … is marked \`nullable: true\` because the transform interceptor sets \`data = null\` on the error path -- a non-nullable field would crash GraphQL and mask the real error instead of surfacing it.` (`AddToCartResponse`) |
|
|
32
|
+
| Case 2 | Vì sao một lớp tồn tại | `Nest's default filter only maps subclasses of \`HttpException\` to their status code -- \`AbstractException\` extends plain \`Error\`, so without this filter every thrown \`AbstractException\` falls through to a generic 500.` (`AbstractExceptionHttpFilter`) |
|
|
33
|
+
| Case 3 | Điều không được xảy ra | `Fails the request when the challenge id is unknown -- downstream must not grade a ghost challenge.` (`ChallengeNotFoundException`) |
|
|
34
|
+
| Case 4 | Ai ném nó | `Thrown by \`AiEntitlementService.consume\` when the user has no remaining allowance in one of the sliding windows, …` |
|
|
35
|
+
| Case 5 | Nhắc lại tên | `/** Constructor. */` vẫn xuất hiện ở 27 tệp (`add-to-cart.handler.ts` trong số đó); lint `no-restated-name-jsdoc` gọi tên nó là nợ, không phải mẫu |
|
|
36
|
+
|
|
37
|
+
## BE-COMMENT-3 — Phương thức và trường
|
|
38
|
+
|
|
39
|
+
| Case | Dùng khi | Viết |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| Case 1 | Phương thức | `/**\n * Processes the addToCart command.\n * @param command - The command carrying request + authenticated user.\n * @returns The cart row holding the course (created or already present).\n */` |
|
|
42
|
+
| Case 2 | Trường | `/** Unique error code for identification */ readonly code: string`; `/** Id of the course the user wants to place in their cart. */ courseId: string` |
|
|
43
|
+
| Case 3 | Trường interface có tham chiếu chéo | `/** Enrollment id for the active course (user x course), injected by {@link GraphQLEnrollmentGuard} from the \`x-course-id\` header. … */ enrollmentId?: string` |
|
|
44
|
+
| Case 4 | Generic | `@template TParams - The parameters type.` (`ICQRSHandler`) |
|
|
45
|
+
|
|
46
|
+
## BE-COMMENT-4 — Mỗi thành viên enum đều được chú thích
|
|
47
|
+
|
|
48
|
+
| Case | Dùng khi | Viết |
|
|
49
|
+
| --- | --- | --- |
|
|
50
|
+
| Case 1 | `Locale` | `/** Serve Vietnamese copy; missing Vi falls through to \`fallbackLocale\`. */ Vi = "vi", /** Serve English copy; also the usual \`defaultLocale\` / fallback. */ En = "en"` (lint `require-enum-member-jsdoc`) |
|
|
51
|
+
|
|
52
|
+
## BE-COMMENT-5 — Chú thích dòng là văn xuôi về lý do
|
|
53
|
+
|
|
54
|
+
9720 dòng `//` tồn tại trong `src/` không kể spec. Chúng đứng trên câu lệnh, viết thường theo dạng
|
|
55
|
+
câu, và đưa ra hệ quả thúc đẩy đoạn mã.
|
|
56
|
+
|
|
57
|
+
| Case | Dùng khi | Viết |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| Case 1 | Một chốt chặn | `// reject unauthenticated callers up front -- cart rows are per-user` |
|
|
60
|
+
| Case 2 | Một ràng buộc | `// idempotent add: if the (user, course) row already exists, return it as-is --\n// creating a second one would violate UQ_cart_items_user_course` |
|
|
61
|
+
| Case 3 | Một quyết định phạm vi | `// a course the user already OWNS must not be carted -- real enrollment (paid)\n// only, so a trial/preview row (is_enrolled = false) still allows adding to cart` |
|
|
62
|
+
| Case 4 | Một lượt chuyển giao | `// hand off to the command handler which performs the DB work` |
|
|
63
|
+
| Case 5 | Chính sách lỗi | `// any OTHER error (graphql-js's own execution errors …) is by definition an UNEXPECTED server bug, never the client's fault -- default it to 500 too` |
|
|
64
|
+
| Case 6 | Dấu nối ` -- ` | dùng ở 1299 trên 9720 dòng; câu trơn là dạng đa số |
|
|
65
|
+
|
|
66
|
+
## BE-COMMENT-6 — Chú thích khối cho lập luận nhiều đoạn bên trong thân
|
|
67
|
+
|
|
68
|
+
| Case | Dùng khi | Viết |
|
|
69
|
+
| --- | --- | --- |
|
|
70
|
+
| Case 1 | `course.handler.ts` | `/*\n * EITHER IDENTIFIER ADDRESSES THE SAME OBJECT, because the synchronizer writes both.\n * \`MaterializeAndUploadService\` uploads … It used to refuse anything but a display id, … That reading was wrong about the bucket …\n */` — mở bằng một khẳng định viết hoa, rồi bằng chứng, rồi lịch sử đã sửa |
|
|
71
|
+
| Case 2 | Cấu hình | các khối trong `eslint.config.mjs` ghi vì sao một luật ở mức error, kèm nợ đã đo (`nợ=0 (296/296)`) |
|
|
72
|
+
|
|
73
|
+
## BE-COMMENT-7 — Thứ một chú thích không bao giờ chứa
|
|
74
|
+
|
|
75
|
+
| Case | Dùng khi | Viết |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| Case 1 | Ký tự ngoài ASCII | lint `no-non-ascii-source`, `no-ai-symbol`, `no-emoji`; chú thích giữ ASCII |
|
|
78
|
+
| Case 2 | Tiếng Việt | lint `no-vietnamese`; một chuỗi tiếng Việt phát ra cho client mang theo lý do: `[Locale.Vi]: "Thêm khóa học vào giỏ hàng thành công", // vn-ok: vi-locale string emitted to clients` (lint `require-vn-ok-reason`) |
|
|
79
|
+
| Case 3 | Tắt luật | `eslint-disable-next-line` (lint `no-line-suppression`); ngoại lệ sống trong cấu hình kèm chú thích |
|
|
80
|
+
| Case 4 | Nhắc lại tên | xem BE-COMMENT-2 Trường hợp 5 |
|