@starci/hfs 4.7.44 → 4.7.46
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/CHANGELOG.md +9 -0
- package/lint/run.mjs +46 -10
- package/package.json +4 -1
- package/release/candidate-ci.mjs +7 -7
- package/release/push.mjs +3 -3
- package/runtime/config.example.yaml +9 -10
- package/runtime/engine/config.mjs +3 -3
- package/runtime/engine/release-config.mjs +5 -5
- package/runtime/knowledge/grammars/offset-pop/playbook.yaml +1 -1
- package/runtime/knowledge/grammars/starci/playbook.yaml +27 -3
- package/runtime/knowledge/hfs/canon-pins.yaml +14 -7
- package/runtime/knowledge/hfs/ci-standard.yaml +1 -1
- package/runtime/knowledge/hfs/fe-stack.yaml +12 -1
- package/runtime/knowledge/hfs/rules.yaml +41 -6
- package/runtime/knowledge/hfs/slots.yaml +5 -5
- package/runtime/knowledge/patterns/be/realtime.yaml +72 -12
- package/runtime/modules/kernel/command-policy.yaml +5 -5
- package/runtime/modules/kernel/failure-codes.yaml +42 -20
- package/runtime/modules/kernel/removed-vocabulary.yaml +33 -7
- package/runtime/packages/cli/src/catalog.generated.mjs +90 -37
- package/runtime/scripts/api/git/commit-tree.mjs +5 -0
- package/runtime/scripts/api/git/update-ref.mjs +8 -0
- package/runtime/scripts/api/git/write-tree.mjs +5 -0
- package/runtime/scripts/guards/command-policy.mjs +4 -4
- package/runtime/scripts/guards/push-scan.mjs +128 -0
- package/runtime/scripts/guards/release-push-gate.mjs +31 -29
- package/runtime/scripts/hfs/architecture/index.mjs +2 -1
- package/runtime/scripts/hfs/architecture/type-context.mjs +38 -23
- package/runtime/scripts/hfs/example-secrets.mjs +61 -8
- package/runtime/scripts/hfs/rules/deploy-workflow.mjs +5 -5
- package/runtime/scripts/hfs/runtime-rules/git-triggers.mjs +61 -0
- package/runtime/scripts/hfs/runtime-rules/hook-shape.mjs +4 -6
- package/runtime/scripts/hfs/typescript-programs.mjs +30 -3
- package/runtime/scripts/machine/home.mjs +2 -2
- package/templates/app/ci-workflows/github/workflows/ci.yml +2 -2
- package/templates/app/ci-workflows-lite/github/workflows/ci.yml +2 -2
- package/templates/app/hooks/husky/pre-push +3 -56
- package/templates/be/inbox/platform/inbox/inbox.port.ts.tpl +1 -3
- package/templates/be/patterns/idempotency/platform/inbox/inbox.port.ts.tpl +1 -3
- package/templates/be/patterns/realtime/realtime-config.mapper.ts.tpl +32 -0
- package/runtime/scripts/api/fs/write-json-file.mjs +0 -10
- package/runtime/scripts/api/git/merge-base-query.mjs +0 -5
- package/runtime/scripts/gates/commit-proof.mjs +0 -53
- package/runtime/scripts/guards/release-definition.mjs +0 -204
- package/runtime/scripts/guards/release-record.mjs +0 -58
- package/runtime/scripts/guards/release-suite-mode.mjs +0 -15
- package/runtime/scripts/hfs/runtime-rules/release-notes.mjs +0 -44
|
@@ -7,7 +7,7 @@ purpose: |-
|
|
|
7
7
|
(`transport/sse/<channel>.controller.ts`), a socket gateway (`<channel>.gateway.ts`) or a GraphQL subscription
|
|
8
8
|
(`<x>.subscription.ts`), that READS and PUSHES only. It never writes. The push is fed by a reactor: an event consumer in
|
|
9
9
|
`features/reactors/` receives the domain event and publishes to a topic of the `RealtimeHub` of `platform/realtime` (an app
|
|
10
|
-
with no event bus lets the domain service that commits the change publish
|
|
10
|
+
with no event bus lets the domain service that commits the change publish with the hub's commit ordering, BE-REALTIME-7); the door only subscribes the
|
|
11
11
|
client to a topic of that hub, scoped to the client's principal. SSE is the default door for one-way server-to-client push
|
|
12
12
|
(job progress, a status that changes, streamed tokens), in lite and in full (owner decision 2026-10-24): it is plain HTTP
|
|
13
13
|
behind the ingress, reconnects by itself and resumes from `Last-Event-ID`. The socket.io gateway stays for a channel that also
|
|
@@ -28,7 +28,8 @@ provenance:
|
|
|
28
28
|
- owner decision 2026-10-24 (the lite edition may serve SSE, and SSE is the default door for one-way push; ruling:R212/case-5, amended by owner:lite-graphql-subscription-door and ruling:R212/case-7 for the GraphQL subscription door)
|
|
29
29
|
limitations: |-
|
|
30
30
|
Static checks prove the door's role, that the hub is the only thing injected and that a subscribed topic is built from the
|
|
31
|
-
principal. They do not prove the
|
|
31
|
+
principal. They do not prove the publisher's membership read, that a non-Postgres publish follows the domain commit,
|
|
32
|
+
that the topic naming is collision free across channels, that an initial snapshot is consistent with
|
|
32
33
|
the stream, or the behaviour of the socket under reconnects; the three `realtime/*` scenarios prove delivery, isolation and
|
|
33
34
|
cleanup on the real stack. Nothing proves the ingress settings of an SSE channel (no compression, no buffering, HTTP/2, an
|
|
34
35
|
idle timeout above the heartbeat): they are reviewed against the stack files and a real stream. The memory hub is
|
|
@@ -127,8 +128,8 @@ rules:
|
|
|
127
128
|
needs is pushed by a reactor (or by the domain service that commits the change, in an app with no event bus); the socket
|
|
128
129
|
library and the pub/sub engine (`socket.io`, `@socket.io/postgres-adapter`, `@socket.io/redis-adapter`, `graphql-subscriptions`, `graphql-ws`, `pg` for LISTEN and `ioredis`) are imported only by `platform/realtime`,
|
|
129
130
|
and so is the heartbeat timer (`setInterval` is owned by `platform/realtime`, `infraOwners`). Because the door reads only the
|
|
130
|
-
hub, a snapshot cannot be sent on connect from the door: the client reads the current state through a query operation
|
|
131
|
-
(or again on every open), and the stream carries the changes.
|
|
131
|
+
hub, a snapshot cannot be sent on connect from the door: the client reads the current state through a query operation that
|
|
132
|
+
checks resource membership first (or again on every open), and the stream carries the changes (BE-REALTIME-3).
|
|
132
133
|
rationale: |-
|
|
133
134
|
A door that can only touch the hub cannot change business state, so the biggest risk of a long-lived unauthenticated-looking
|
|
134
135
|
connection, a write through it, is removed by type.
|
|
@@ -143,13 +144,26 @@ rules:
|
|
|
143
144
|
- id: BE-REALTIME-3
|
|
144
145
|
title: The topic is built from the principal
|
|
145
146
|
kind: mandatory
|
|
146
|
-
hfsRules: [R186, R41]
|
|
147
|
+
hfsRules: [R186, R41, R89, R204]
|
|
147
148
|
requirement: |-
|
|
148
149
|
Every `@SubscribeMessage`, `@Subscription` and `@Sse` handler takes `@CurrentPrincipal()` of `domain/identity` and builds the topic it
|
|
149
150
|
passes to `hub.subscribe` from the principal's id (plus a client-supplied id only inside that prefix) with the topic
|
|
150
|
-
builder of the owning domain contract, so one client cannot
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
builder of the owning domain contract, so one client cannot listen to another's channel. A typed realtime topic builder is
|
|
152
|
+
a policy: it lives in `<name>.policy.ts` of slot `be.domain`, is exported through the capability's `index.ts`, and returns
|
|
153
|
+
`RealtimeTopic<Frame>` with the principal-prefixed `name` and the frame's narrowing `accepts` predicate. The named frame
|
|
154
|
+
contract lives in `*.contracts.ts`. The builder and predicate are pure functions of their inputs, with no injection, database
|
|
155
|
+
read or membership lookup; `policy` keeps the logic-role coverage of R204. The publisher and every door import this one
|
|
156
|
+
builder from the owning domain contract, never a hand-written topic string.
|
|
157
|
+
|
|
158
|
+
Resource membership is enforced on the publish side. The domain service that owns the change, or the domain service called
|
|
159
|
+
by a reactor's command, reads the current entitled members and fans out to one principal-prefixed topic per member. A client-supplied room
|
|
160
|
+
or conversation id stays inside that principal's prefix: a non-member can open only its own idle topic, which no publisher
|
|
161
|
+
targets. A removed member's already-open stream stays open but receives no newly published frames from the next publish onward, because
|
|
162
|
+
the refreshed member list no longer targets it. Frames already delivered or retained in its own topic are not revoked;
|
|
163
|
+
replay remains subject to the hub's bounds. Snapshot and history queries independently check current membership before
|
|
164
|
+
returning rows; the door injects only the hub (BE-REALTIME-2), never a membership-reading service or guard.
|
|
165
|
+
|
|
166
|
+
The default-deny guards apply to the door like to any other; an SSE stream is never `@Public`. The
|
|
153
167
|
browser `EventSource` cannot send an `Authorization` header, so the front end reads the stream with `fetch` and the bearer
|
|
154
168
|
access token (an operation of modules/api/sse read by a useSubscribe<Channel>Swr hook); a token in the URL is refused because it ends up in logs. The `Last-Event-ID` header is client
|
|
155
169
|
input like any other: it is passed as `{ after }` to `hub.subscribe` and only picks where the replay resumes inside the
|
|
@@ -160,12 +174,26 @@ rules:
|
|
|
160
174
|
- id: case-1
|
|
161
175
|
when: Subscribing to one order's status
|
|
162
176
|
write: 'hub.subscribe(orderStatusTopic(principal.id, orderId)); the reactor publishes to orderStatusTopic(buyerId, orderId) of the order owner, so a different principal receives nothing.'
|
|
177
|
+
- id: case-2
|
|
178
|
+
when: A room or conversation has several members and membership can change
|
|
179
|
+
write: |-
|
|
180
|
+
The owning domain exports roomMessageTopic(memberId, roomId) from its room-message.policy.ts, returning
|
|
181
|
+
{ name: `room-message:${memberId}:${roomId}`, accepts: isRoomMessageFrame } with the declared return type RealtimeTopic<RoomMessageFrame>.
|
|
182
|
+
The SSE door calls hub.stream(roomMessageTopic(principal.id, roomId), { after: lastEventId }) and injects only the hub.
|
|
183
|
+
The publisher reads the current members and publishes a frame to roomMessageTopic(member.id, roomId) for each of them,
|
|
184
|
+
following the hub's commit ordering (BE-REALTIME-7). A non-member's topic receives nothing; removing a member leaves its
|
|
185
|
+
stream open and excludes it from subsequent fan-out. The snapshot query checks membership in its own read.
|
|
163
186
|
verification:
|
|
164
187
|
automated:
|
|
165
188
|
- BE_REALTIME_TOPIC_SCOPE
|
|
166
189
|
- BE_DEFAULT_DENY
|
|
190
|
+
- BE_SOURCE_FORM
|
|
191
|
+
- BE_PUBLIC_CONTRACT_FORM
|
|
192
|
+
- HFS_COVERAGE_SCOPE_DRIFT
|
|
167
193
|
manual:
|
|
168
|
-
- Review that the topic
|
|
194
|
+
- Review that the pure topic policy is exported by the owning domain and used by both the publisher and door.
|
|
195
|
+
- Prove the publisher targets exactly the current member list, including the next publish after removing a member; the non-member's stream receives no frame.
|
|
196
|
+
- Prove snapshot and history queries reject a caller whose membership is absent or removed.
|
|
169
197
|
relatedExamples: [realtime]
|
|
170
198
|
- id: BE-REALTIME-4
|
|
171
199
|
title: Three behaviours are proven on the test world
|
|
@@ -301,7 +329,7 @@ rules:
|
|
|
301
329
|
hfsRules: [R90, R266, R155]
|
|
302
330
|
requirement: |-
|
|
303
331
|
Both editions run N api replicas. `platform/realtime` keeps ONE port, `RealtimeHub` (`publish(topic, frame, within?): Promise<void>`
|
|
304
|
-
called by a reactor, or by the domain service that owns the state change
|
|
332
|
+
called by a reactor, or by the domain service that owns the state change with the commit ordering below,
|
|
305
333
|
`subscribe(topic, { after })` for GraphQL and socket doors, `stream(topic, { after })` for the SSE door), so a door never
|
|
306
334
|
changes between a single replica and N replicas: only the hub binding does. Three adapters implement the port:
|
|
307
335
|
`MemoryRealtimeHub` (non-scale: one process, tests and a single-replica dev run, replay from an in-process ring buffer),
|
|
@@ -318,8 +346,16 @@ rules:
|
|
|
318
346
|
replay reads the stream with XRANGE and resumes after the newest entry carrying the id; when the subscriber connection comes
|
|
319
347
|
back the hub reloads each followed topic after the minimum last stream id of its open subscriptions, with the same per-subscription
|
|
320
348
|
duplicate suppression and paused retry policy as Postgres; ioredis, every key and channel carries the
|
|
321
|
-
app prefix `REALTIME_REDIS_PREFIX`).
|
|
322
|
-
`
|
|
349
|
+
app prefix `REALTIME_REDIS_PREFIX`). Only the Postgres hub can publish inside the domain transaction: pass that
|
|
350
|
+
transaction as `within` when the domain write and realtime table use the same database. Redis and memory push immediately,
|
|
351
|
+
even if a caller passes `within`; they do not defer delivery until commit. With either hub the domain service awaits its
|
|
352
|
+
successful transaction before calling `hub.publish(topic, frame)` without `within`. A rollback therefore publishes nothing;
|
|
353
|
+
a publish failure after commit cannot roll back the domain write, and the client resyncs through its membership-checked
|
|
354
|
+
snapshot query. A service whose hub binding can vary uses this after-commit sequence unless it is bound to Postgres.
|
|
355
|
+
The app selects the hub with `REALTIME_HUB=memory|postgres|redis`, parsed once by
|
|
356
|
+
`parseRealtimeConfig` in `platform/realtime/realtime-config.mapper.ts` (the unit-tested mapping role of BE-NAMING-1),
|
|
357
|
+
with its unit spec in `realtime-config.mapper.spec.ts`, and validated at boot; unset, the parser defaults to `redis`
|
|
358
|
+
when `REALTIME_REDIS_URL` is declared (lite
|
|
323
359
|
and full), `postgres` when lite declares no Redis, and never `memory`, so a scaled deploy cannot fall back to the in-process
|
|
324
360
|
hub unseen. All three
|
|
325
361
|
adapters are legal in both editions; select `redis` when Redis is declared (lite with Redis, and full), `postgres` when it is
|
|
@@ -381,6 +417,18 @@ rules:
|
|
|
381
417
|
RealtimeModule.register({ isGlobal: true, ...withConnection(options.realtime, ORDER_ENTITY_MANAGER, options.database.url) })
|
|
382
418
|
// REALTIME_HUB=redis, the secret REALTIME_REDIS_URL, REALTIME_REDIS_DB (and REALTIME_REDIS_PREFIX when the server is shared);
|
|
383
419
|
// Redis 7 or later for the sharded socket.io adapter.
|
|
420
|
+
- id: case-9
|
|
421
|
+
when: A domain service changes state with the Redis or memory hub, or a selectable hub binding
|
|
422
|
+
write: |-
|
|
423
|
+
Collect the frame and entitled recipients while committing the change; publish only after the awaited transaction
|
|
424
|
+
resolves. No publish runs inside its callback, so a rollback sends no frame:
|
|
425
|
+
const push = await this.entityManager.transaction(async (tx) => {
|
|
426
|
+
// Read current membership and write the domain change through tx; return its topic/frame pairs.
|
|
427
|
+
return pendingPushes
|
|
428
|
+
})
|
|
429
|
+
for (const { topic, frame } of push) await this.hub.publish(topic, frame)
|
|
430
|
+
Passing tx to the Redis or memory hub does not make its immediate push transactional. Postgres alone may use
|
|
431
|
+
hub.publish(topic, frame, tx) inside that transaction (case-2).
|
|
384
432
|
- id: case-4
|
|
385
433
|
when: GraphQL subscription, non-scale
|
|
386
434
|
write: |-
|
|
@@ -449,8 +497,14 @@ rules:
|
|
|
449
497
|
manual:
|
|
450
498
|
- Confirm the ingress holds no sticky-session declaration for the api, and that polling is off in the socket.io client configuration.
|
|
451
499
|
- Confirm a Postgres hub connects through a session-mode URL and that the realtime tables are migrations of the app.
|
|
500
|
+
- Prove a domain rollback publishes nothing; with Redis or memory, verify publish follows the awaited commit and a publish failure leaves the committed change intact.
|
|
452
501
|
relatedExamples: [realtime]
|
|
453
502
|
files:
|
|
503
|
+
- path: src/modules/domain/<capability>/<name>.policy.ts
|
|
504
|
+
slot: be.domain
|
|
505
|
+
role: The pure typed topic builder and frame predicate of BE-REALTIME-3, exported by the domain contract for publisher and door.
|
|
506
|
+
spec: src/modules/domain/<capability>/<name>.policy.spec.ts
|
|
507
|
+
optional: true
|
|
454
508
|
- path: src/features/realtime/<channel>/index.ts
|
|
455
509
|
slot: be.feature.realtime
|
|
456
510
|
template: realtime/channel.index.ts.tpl
|
|
@@ -544,6 +598,12 @@ files:
|
|
|
544
598
|
slot: be.platform
|
|
545
599
|
role: RealtimeHub, the one port of the realtime kind (publish, subscribe, stream) and the typed topic; its three adapters are the files below.
|
|
546
600
|
optional: true
|
|
601
|
+
- path: src/modules/platform/realtime/realtime-config.mapper.ts
|
|
602
|
+
slot: be.platform
|
|
603
|
+
template: realtime/realtime-config.mapper.ts.tpl
|
|
604
|
+
role: parseRealtimeConfig maps EnvSource to the hub options at boot; hub-selection logic is a covered mapper role.
|
|
605
|
+
spec: src/modules/platform/realtime/realtime-config.mapper.spec.ts
|
|
606
|
+
optional: true
|
|
547
607
|
- path: src/modules/platform/realtime/memory-realtime-hub.service.ts
|
|
548
608
|
slot: be.platform
|
|
549
609
|
role: "MemoryRealtimeHub, the non-scale adapter: one process, tests and a single-replica dev run, replay from an in-process ring buffer."
|
|
@@ -80,7 +80,7 @@ op:
|
|
|
80
80
|
release:
|
|
81
81
|
verbs: [cut, publish]
|
|
82
82
|
scripts: [release:cut, release:publish]
|
|
83
|
-
use: starci release cut is owner-approved and
|
|
83
|
+
use: starci release cut is owner-approved and tags a commit already pushed; starci release publish publishes a finished package at once; starci release push sends main and is not owner-gated; starci release notes prints or writes a tag CHANGELOG section and is not owner-gated
|
|
84
84
|
# Programs that cannot change state, with any arguments.
|
|
85
85
|
read: [ls, dir, cat, type, head, tail, wc, uniq, cut, tr, diff, cmp, comm, rg, grep, egrep, fgrep, echo, printf, pwd, cd, pushd, popd, which, where, whoami, hostname,
|
|
86
86
|
stat, file, realpath, readlink, dirname, basename, date, true, false, test, '[', expr, sleep, seq, basename, md5sum, sha1sum, sha256sum, shasum, cksum, od, xxd, hexdump,
|
|
@@ -123,7 +123,7 @@ git:
|
|
|
123
123
|
add: {code: RIGHTS_GIT_COMMIT, use: starci git commit}
|
|
124
124
|
rm: {code: RIGHTS_GIT_COMMIT, use: starci git commit}
|
|
125
125
|
mv: {code: RIGHTS_GIT_COMMIT, use: starci git commit}
|
|
126
|
-
push: {code: RIGHTS_GIT_PUSH, use: 'starci git backup (refs/backup/* only); main and
|
|
126
|
+
push: {code: RIGHTS_GIT_PUSH, use: 'starci git backup (refs/backup/* only); main moves only through starci release push and a release tag through starci release cut'}
|
|
127
127
|
tag: {code: RIGHTS_GIT_TAG, use: starci release cut (the only tag maker; housekeeping marks are refs/backup/* or refs/salvage/*, never tags)}
|
|
128
128
|
merge: {code: RIGHTS_GIT_SYNC, use: starci git sync (merge main into the lane) or starci git land (coordinator)}
|
|
129
129
|
rebase: {code: RIGHTS_GIT_SYNC, use: starci git sync}
|
|
@@ -154,7 +154,7 @@ npm:
|
|
|
154
154
|
programs: [npm, pnpm, yarn, bun]
|
|
155
155
|
read: [view, v, info, show, ls, list, la, ll, outdated, explain, why, root, prefix, bin, help, search, whoami, ping, '-v', '--version', 'config get', 'config list', pack-dry]
|
|
156
156
|
deny:
|
|
157
|
-
publish: {code: RIGHTS_NPM_PUBLISH, use: starci release
|
|
157
|
+
publish: {code: RIGHTS_NPM_PUBLISH, use: starci release publish is the package-publication path and runs as soon as a package branch is done}
|
|
158
158
|
unpublish: {code: RIGHTS_NPM_PUBLISH, use: starci release publish is the only package-publication path; unpublishing is owner-only}
|
|
159
159
|
deprecate: {code: RIGHTS_NPM_PUBLISH, use: starci release publish is the only package-publication path; deprecation is owner-only}
|
|
160
160
|
dist-tag: {code: RIGHTS_NPM_PUBLISH, use: starci release publish is the only package-publication path; dist-tag mutation is owner-only}
|
|
@@ -165,8 +165,8 @@ npm:
|
|
|
165
165
|
add: {code: RIGHTS_RAW_TOOL, use: starci npm install adds exact-pinned dependencies and is limited to lead or owner}
|
|
166
166
|
update: {code: RIGHTS_RAW_TOOL, use: starci npm install is the owner-controlled dependency-change path using canon pins}
|
|
167
167
|
uninstall: {code: RIGHTS_RAW_TOOL, use: starci npm install is the owner-controlled dependency-change path using canon pins}
|
|
168
|
-
test: {code: RIGHTS_RAW_TOOL, use: starci runtime verify in the runtime (the check, package clean-test and scaffold e2e; the full proof is
|
|
169
|
-
t: {code: RIGHTS_RAW_TOOL, use: starci runtime verify in the runtime (the check, package clean-test and scaffold e2e; the full proof is
|
|
168
|
+
test: {code: RIGHTS_RAW_TOOL, use: starci runtime verify in the runtime (the check, package clean-test and scaffold e2e; the full proof is CI) or starci gate unit --root <app> in an app}
|
|
169
|
+
t: {code: RIGHTS_RAW_TOOL, use: starci runtime verify in the runtime (the check, package clean-test and scaffold e2e; the full proof is CI) or starci gate unit --root <app> in an app}
|
|
170
170
|
run: {code: RIGHTS_RAW_TOOL, use: 'starci runtime verify proves a runtime change; starci work layout-render serves the app for a layout capture; use starci release sync-runtime for package runtime copies and grammar preparation, or starci check run, starci lint run, starci typecheck run, starci app stack, or starci gate run for other scripts'}
|
|
171
171
|
run-script: {code: RIGHTS_RAW_TOOL, use: 'starci runtime verify proves a runtime change; use starci check run, starci lint run, starci typecheck run, starci app stack, or starci gate run for other scripts'}
|
|
172
172
|
start: {code: RIGHTS_RAW_TOOL, use: 'starci app stack'}
|
|
@@ -1770,13 +1770,13 @@ CI_STANDARD:
|
|
|
1770
1770
|
kind: check-finding
|
|
1771
1771
|
|
|
1772
1772
|
CI_TRIGGERS_RELEASE_ONLY:
|
|
1773
|
-
title: "Workflow trigger
|
|
1774
|
-
title_vi: "Workflow có trigger
|
|
1775
|
-
meaning_vi: "
|
|
1773
|
+
title: "Workflow trigger outside R221"
|
|
1774
|
+
title_vi: "Workflow có trigger ngoài R221"
|
|
1775
|
+
meaning_vi: "Workflow khai báo trigger ngoài R221: main được phép trong workflow runtime và ci.yml của app; các nhánh, sự kiện và bộ lọc khác phải theo R221."
|
|
1776
1776
|
causes_vi:
|
|
1777
|
-
- "
|
|
1778
|
-
- "
|
|
1779
|
-
nextStep_vi: "
|
|
1777
|
+
- "Khai báo nhánh, sự kiện hoặc bộ lọc không thuộc R221"
|
|
1778
|
+
- "Khai báo nhánh, sự kiện hoặc bộ lọc không thuộc R221"
|
|
1779
|
+
nextStep_vi: "Sửa on theo R221 rồi chạy starci app sync --write để tạo lại workflow ứng dụng."
|
|
1780
1780
|
owner: supervisor
|
|
1781
1781
|
kind: check-finding
|
|
1782
1782
|
|
|
@@ -2284,6 +2284,17 @@ FE_CONSOLE_CALL:
|
|
|
2284
2284
|
owner: op-retry
|
|
2285
2285
|
kind: check-finding
|
|
2286
2286
|
|
|
2287
|
+
FE_CONTROL_HEIGHT:
|
|
2288
|
+
title: "A control picks a larger size or mixes sizes in one row"
|
|
2289
|
+
title_vi: "Điều khiển chọn cỡ lớn hơn hoặc trộn nhiều cỡ trong một hàng"
|
|
2290
|
+
meaning_vi: "Điều khiển Grammar tại <file> truyền size lớn hơn chiều cao chuẩn 40px, hoặc đứng cạnh điều khiển khác cỡ trong cùng một hàng."
|
|
2291
|
+
causes_vi:
|
|
2292
|
+
- "Truyền size lg hoặc xl cho nút, công tắc, ô nhập ngoài khu hero của trang landing."
|
|
2293
|
+
- "Trộn size sm với cỡ mặc định trong cùng một thanh công cụ hoặc hàng."
|
|
2294
|
+
nextStep_vi: "Bỏ size để Grammar vẽ đúng một chiều cao điều khiển 40px (quy định control-height-40)."
|
|
2295
|
+
owner: op-retry
|
|
2296
|
+
kind: check-finding
|
|
2297
|
+
|
|
2287
2298
|
FE_COOKIE_ATTRIBUTES:
|
|
2288
2299
|
title: "A written cookie leaves its attributes to the defaults"
|
|
2289
2300
|
title_vi: "Cookie được ghi mà không nêu rõ thuộc tính"
|
|
@@ -2348,6 +2359,16 @@ FE_DESTRUCTIVE_CONFIRM_IN_DIALOG:
|
|
|
2348
2359
|
owner: op-retry
|
|
2349
2360
|
kind: check-finding
|
|
2350
2361
|
|
|
2362
|
+
FE_DIVIDER_FULL_BLEED:
|
|
2363
|
+
title: "Horizontal Grammar divider is inset by wrapper padding"
|
|
2364
|
+
title_vi: "Divider bị padding ngang của wrapper làm thụt vào"
|
|
2365
|
+
meaning_vi: "Divider ngang của Grammar phải dài full và sát viền surface."
|
|
2366
|
+
causes_vi:
|
|
2367
|
+
- "Wrapper chứa Divider có padding ngang"
|
|
2368
|
+
nextStep_vi: "Chuyển padding vào các band nội dung cạnh Divider."
|
|
2369
|
+
owner: "op-retry"
|
|
2370
|
+
kind: "check-finding"
|
|
2371
|
+
|
|
2351
2372
|
FE_EFFECT_CLEANUP:
|
|
2352
2373
|
title: "What an effect starts, its cleanup releases"
|
|
2353
2374
|
title_vi: "Effect khởi động thứ không được dọn"
|
|
@@ -3209,12 +3230,12 @@ HFS_APP_LEXICON:
|
|
|
3209
3230
|
kind: check-finding
|
|
3210
3231
|
|
|
3211
3232
|
HFS_APP_WORKFLOW_TRIGGER:
|
|
3212
|
-
title: "App
|
|
3233
|
+
title: "App workflow trigger outside R251"
|
|
3213
3234
|
title_vi: "Workflow ứng dụng có sự kiện khởi chạy không hợp lệ"
|
|
3214
|
-
meaning_vi: "Workflow
|
|
3235
|
+
meaning_vi: "Workflow ứng dụng khai báo trigger ngoài R251; ci.yml được chạy trên main và nhánh release candidate, các workflow khác giữ phạm vi trigger của R251."
|
|
3215
3236
|
causes_vi:
|
|
3216
|
-
- "
|
|
3217
|
-
nextStep_vi: "
|
|
3237
|
+
- "Khai báo trigger không thuộc R251"
|
|
3238
|
+
nextStep_vi: "Sửa trigger theo R251 và chạy starci app sync --write."
|
|
3218
3239
|
owner: op-retry
|
|
3219
3240
|
kind: check-finding
|
|
3220
3241
|
|
|
@@ -4522,7 +4543,7 @@ RIGHTS_GIT_PUSH:
|
|
|
4522
4543
|
causes_vi:
|
|
4523
4544
|
- "Agent định đẩy main, một nhánh thường, mọi thẻ hoặc một refspec quá rộng"
|
|
4524
4545
|
- "Coordinator hoặc Supervisor đẩy bản sao lưu nhưng đích không nằm trong refs/backup/* hoặc không có refspec tường minh"
|
|
4525
|
-
nextStep_vi: "Lệnh bị từ chối; để thay đổi đi qua starci release
|
|
4546
|
+
nextStep_vi: "Lệnh bị từ chối; để thay đổi đi qua starci release push, hoặc nếu là coordinator hay Supervisor thì dùng starci git backup với đích refs/backup/* tường minh."
|
|
4526
4547
|
owner: op-retry
|
|
4527
4548
|
kind: verb-refusal
|
|
4528
4549
|
|
|
@@ -4595,12 +4616,13 @@ RIGHTS_PROTECTED_ZONE:
|
|
|
4595
4616
|
RIGHTS_PUSH_NOT_RELEASE:
|
|
4596
4617
|
title: "Pre-push gate refused the push"
|
|
4597
4618
|
title_vi: "Cổng pre-push từ chối lần đẩy"
|
|
4598
|
-
meaning_vi: "Hook pre-push đã chặn một lần đẩy main hoặc thẻ v* của runtime
|
|
4619
|
+
meaning_vi: "Hook pre-push đã chặn một lần đẩy main hoặc thẻ v* của runtime hoặc app: main không phải fast-forward hoặc vùng đẩy có ứng viên bí mật; thẻ v* không phải thẻ annotated."
|
|
4599
4620
|
causes_vi:
|
|
4600
4621
|
- "Thao tác xóa main hoặc xóa thẻ phát hành"
|
|
4601
4622
|
- "Đẩy main không phải fast-forward của main trên remote (force push, rewind hoặc nhánh rẽ)"
|
|
4602
|
-
- "
|
|
4603
|
-
|
|
4623
|
+
- "Vùng đẩy chứa ứng viên bí mật (tên tệp, dòng và mẫu, không bao giờ giá trị)"
|
|
4624
|
+
- "Đẩy thẻ v* không phải thẻ annotated"
|
|
4625
|
+
nextStep_vi: "Gỡ hoặc mã hóa ứng viên bí mật, fetch main rồi đẩy lại bằng starci release push; thẻ thì tạo bằng starci release cut --tag v<version> (thêm --plan để xem trước)."
|
|
4604
4626
|
owner: supervisor
|
|
4605
4627
|
kind: verb-refusal
|
|
4606
4628
|
|
|
@@ -4618,11 +4640,11 @@ RIGHTS_RAW_TOOL:
|
|
|
4618
4640
|
RIGHTS_RELEASE_CUT:
|
|
4619
4641
|
title: "Release cut refused"
|
|
4620
4642
|
title_vi: "Từ chối cắt bản phát hành"
|
|
4621
|
-
meaning_vi: "Release cut
|
|
4643
|
+
meaning_vi: "Release cut gắn thẻ bản phát hành cho một commit đã đẩy và publish gói là quyết định phát hành của owner; worker, lead, Supervisor và coordinator không được tự khởi động chúng."
|
|
4622
4644
|
causes_vi:
|
|
4623
4645
|
- "Agent gọi starci release cut hoặc đường script tương đương"
|
|
4624
4646
|
- "Agent định publish bản phát hành mà chưa có phê duyệt của owner"
|
|
4625
|
-
nextStep_vi: "Lệnh bị từ chối;
|
|
4647
|
+
nextStep_vi: "Lệnh bị từ chối; đẩy main bằng starci release push, và báo để owner phê duyệt starci release cut hoặc publish."
|
|
4626
4648
|
owner: op-retry
|
|
4627
4649
|
kind: verb-refusal
|
|
4628
4650
|
|
|
@@ -4643,7 +4665,7 @@ RIGHTS_SUITE_RUN:
|
|
|
4643
4665
|
meaning_vi: "Toàn bộ suite chỉ chạy một lần trong mỗi release hoặc theo yêu cầu trong unit.verify hay e2e.verify; một worker chỉ chạy tệp spec vừa thay đổi hoặc để op gate chọn phạm vi."
|
|
4644
4666
|
causes_vi:
|
|
4645
4667
|
- "Agent gọi npm test, một script test toàn bộ hoặc node --test trên cả cây tests"
|
|
4646
|
-
- "Agent chạy suite ngoài
|
|
4668
|
+
- "Agent chạy suite ngoài CI và ngoài op verify được yêu cầu"
|
|
4647
4669
|
nextStep_vi: "Lệnh bị từ chối; chạy riêng tệp spec đã thay đổi, để op gate chọn spec liên quan, hoặc dùng unit.verify/e2e.verify khi owner hay mục tiêu yêu cầu."
|
|
4648
4670
|
owner: op-retry
|
|
4649
4671
|
kind: verb-refusal
|
|
@@ -5008,11 +5030,11 @@ RT_HELD_UNSURFACED:
|
|
|
5008
5030
|
RT_HOOK_SHAPE:
|
|
5009
5031
|
title: "App hook template breaks the gate model"
|
|
5010
5032
|
title_vi: "Template hook của app phá vỡ mô hình cổng"
|
|
5011
|
-
meaning_vi: "Template hook
|
|
5033
|
+
meaning_vi: "Template hook của app không đúng R225: pre-commit chạy L0; pre-push gọi chính sách push dùng chung trong gói, không chạy build, test, typecheck hay lint."
|
|
5012
5034
|
causes_vi:
|
|
5013
5035
|
- "Thêm lại npm run typecheck hoặc test:affected vào pre-commit"
|
|
5014
|
-
- "Đưa typecheck, lint hay test vào pre-push
|
|
5015
|
-
nextStep_vi: "
|
|
5036
|
+
- "Đưa build, typecheck, lint hay test vào pre-push hoặc bỏ lời gọi chính sách push dùng chung"
|
|
5037
|
+
nextStep_vi: "Khôi phục template theo R225 rồi chạy starci app sync --write để tạo lại hook."
|
|
5016
5038
|
owner: supervisor
|
|
5017
5039
|
kind: check-finding
|
|
5018
5040
|
|
|
@@ -131,12 +131,6 @@ removed:
|
|
|
131
131
|
use: '`starci debug digest` (read-only) and `starci debug run <probe>`'
|
|
132
132
|
since: 1.0.0-alpha.6
|
|
133
133
|
match: '(?<![\w-])debug pass\b'
|
|
134
|
-
- id: config-supervisor-landgate-push
|
|
135
|
-
kind: config-key
|
|
136
|
-
name: supervisor.landGate.push
|
|
137
|
-
use: 'a land fast-forwards local main only and never pushes; the remote main of the runtime moves once per release, through starci release cut'
|
|
138
|
-
since: 1.0.0-alpha.7
|
|
139
|
-
match: 'landGate\.push\b'
|
|
140
134
|
- id: flag-land-no-push
|
|
141
135
|
kind: flag
|
|
142
136
|
name: --no-push
|
|
@@ -355,7 +349,7 @@ removed:
|
|
|
355
349
|
- id: verb-test-run
|
|
356
350
|
kind: verb
|
|
357
351
|
name: starci test run
|
|
358
|
-
use: 'the L1 to L4 spec ladder is gone with the runtime specs; `starci runtime verify` proves a change,
|
|
352
|
+
use: 'the L1 to L4 spec ladder is gone with the runtime specs; `starci runtime verify` proves a change, CI runs the release rows'
|
|
359
353
|
since: 1.0.0-alpha.19
|
|
360
354
|
match: '\bstarci test run\b'
|
|
361
355
|
- id: verb-gate-runtime-coverage
|
|
@@ -430,3 +424,35 @@ removed:
|
|
|
430
424
|
use: 'DropdownMenuItem.tone uses neutral or danger; destructive commands use danger'
|
|
431
425
|
since: 1.0.0-alpha.31
|
|
432
426
|
match: 'DropdownMenuItem(?:Tone|\.tone)[^\r\n]*["'']negative["'']'
|
|
427
|
+
|
|
428
|
+
- id: code-release-receipt-missing
|
|
429
|
+
kind: code
|
|
430
|
+
name: RELEASE_RECEIPT_MISSING
|
|
431
|
+
use: 'the push of main asks for a fast-forward and a clean secret scan; a release tag asks for no receipt'
|
|
432
|
+
since: 1.0.0-alpha.31.1
|
|
433
|
+
- id: code-release-receipt-incomplete
|
|
434
|
+
kind: code
|
|
435
|
+
name: RELEASE_RECEIPT_INCOMPLETE
|
|
436
|
+
use: 'the push of main asks for a fast-forward and a clean secret scan; CI judges the full suite'
|
|
437
|
+
since: 1.0.0-alpha.31.1
|
|
438
|
+
- id: code-release-suite-mode
|
|
439
|
+
kind: code
|
|
440
|
+
name: RELEASE_SUITE_MODE
|
|
441
|
+
use: 'release.suite chooses whether the cut also runs the L4 rows; no record or gate reads the mode'
|
|
442
|
+
since: 1.0.0-alpha.31.1
|
|
443
|
+
- id: code-release-version-unchanged
|
|
444
|
+
kind: code
|
|
445
|
+
name: RELEASE_VERSION_UNCHANGED
|
|
446
|
+
use: 'main is pushed without a version move; RELEASE_VERSION_NOT_NEXT judges the version of a tag against the v* tags'
|
|
447
|
+
since: 1.0.0-alpha.31.1
|
|
448
|
+
- id: code-release-remote-head-unknown
|
|
449
|
+
kind: code
|
|
450
|
+
name: RELEASE_REMOTE_HEAD_UNKNOWN
|
|
451
|
+
use: 'a release tag is judged against the v* tags, not against the remote main head'
|
|
452
|
+
since: 1.0.0-alpha.31.1
|
|
453
|
+
- id: config-release-candidate-blocking
|
|
454
|
+
kind: config-key
|
|
455
|
+
name: candidate.blocking
|
|
456
|
+
use: 'the runtime cut has no candidate step; the ci, examples and sonar workflows judge each push to main (suite.ci.workflows)'
|
|
457
|
+
since: 1.0.0-alpha.31.1
|
|
458
|
+
match: 'candidate\.blocking\b'
|