@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,193 @@
|
|
|
1
|
+
// platform.operate's own law over one branch, on top of the shared step check: the branch's closed
|
|
2
|
+
// effect, proof and capability sets; the approved plan hash and the approval that covers it; every
|
|
3
|
+
// desired resource inventoried under the same kind and inside the mutable ceiling; port claims owned
|
|
4
|
+
// by this operation and never freed by mutating their holder; every mutation inventoried first and
|
|
5
|
+
// inside the approved set; convergence agreeing with the mutation count; the complete proof set
|
|
6
|
+
// passed before an operated outcome; and no capability handle or credential-shaped token anywhere.
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
8
|
+
import { readFile } from 'node:fs/promises';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import process from 'node:process';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { validateStep } from '../../scripts/validate-step.mjs';
|
|
13
|
+
import { tableUnder } from '../../scripts/validate-response.mjs';
|
|
14
|
+
|
|
15
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
16
|
+
|
|
17
|
+
// The three service kinds are branches of one job. Each publishes its own closed effect set, its own
|
|
18
|
+
// required proof set, and the exact capabilities it needs; a cross-filed effect is how an unapproved
|
|
19
|
+
// change acquires the appearance of authority.
|
|
20
|
+
export const KIND_EFFECTS = {
|
|
21
|
+
observability: ['update-config', 'restart-service', 'upsert-dashboard', 'update-remote-write'],
|
|
22
|
+
sonar: ['create-project', 'assign-profile', 'assign-gate', 'enforce-setting'],
|
|
23
|
+
tunnel: ['create-tunnel', 'update-tunnel-route', 'upsert-proxied-dns'],
|
|
24
|
+
};
|
|
25
|
+
export const KIND_CHECKS = {
|
|
26
|
+
observability: ['service-health', 'target-boundary', 'label-boundary', 'remote-write-delivery', 'sample-ordering', 'retry-backoff', 'sensitive-data-filter'],
|
|
27
|
+
sonar: ['service-available', 'project-exists', 'source-revision', 'profile-assigned', 'gate-assigned', 'enforcement-active'],
|
|
28
|
+
tunnel: ['dns-target', 'tunnel-route', 'tls', 'public-https'],
|
|
29
|
+
};
|
|
30
|
+
export const KIND_CAPABILITIES = {
|
|
31
|
+
observability: ['metrics:remote-write'],
|
|
32
|
+
sonar: ['sonar:project-admin'],
|
|
33
|
+
tunnel: ['tunnel:write', 'dns:write'],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// A credential is resolved for use, never written down. Fingerprints and commit heads are legitimate
|
|
37
|
+
// long hex, so they are scrubbed before the unbroken-run heuristic runs.
|
|
38
|
+
export function credentialLeak(value) {
|
|
39
|
+
if (value.includes('capability://')) return true;
|
|
40
|
+
const scrubbed = value.replaceAll(/sha256:[0-9a-f]{64}/g, '').replaceAll(/\b[0-9a-f]{40}\b/g, '');
|
|
41
|
+
if (/(?:token|secret|password|api[_-]?key|bearer|authorization)\s*[:=]\s*\S/i.test(scrubbed)) return true;
|
|
42
|
+
return /[A-Za-z0-9+=]{32,}/.test(scrubbed);
|
|
43
|
+
}
|
|
44
|
+
function forEachString(value, visit, at = '$') {
|
|
45
|
+
if (typeof value === 'string') return visit(value, at);
|
|
46
|
+
if (Array.isArray(value)) { value.forEach((item, i) => forEachString(item, visit, `${at}[${i}]`)); return; }
|
|
47
|
+
if (value !== null && typeof value === 'object') for (const [k, child] of Object.entries(value)) forEachString(child, visit, `${at}.${k}`);
|
|
48
|
+
}
|
|
49
|
+
const empty = (v) => v === undefined || v === null || v === '' || v === '—';
|
|
50
|
+
const asList = (v) => (Array.isArray(v) ? v : []);
|
|
51
|
+
|
|
52
|
+
export async function validatePlatformStep(branchDir, root = ROOT) {
|
|
53
|
+
const base = await validateStep(root, branchDir);
|
|
54
|
+
const errors = [...base.errors];
|
|
55
|
+
const { response, request, requirements = {}, present = new Set() } = base;
|
|
56
|
+
if (!response || response.operatorId !== 'platform.operate') return { errors };
|
|
57
|
+
const has = (f) => existsSync(path.join(branchDir, f));
|
|
58
|
+
const read = (f) => readFile(path.join(branchDir, f), 'utf8');
|
|
59
|
+
|
|
60
|
+
const desired = requirements.desiredState ?? {};
|
|
61
|
+
const kind = String(desired.serviceKind ?? '');
|
|
62
|
+
const desiredEffects = asList(desired.effects);
|
|
63
|
+
const desiredResources = asList(desired.resourceRefs);
|
|
64
|
+
const mutable = new Set(asList(desired.mutableResourceRefs));
|
|
65
|
+
const observationOnly = new Set(asList(desired.observationOnlyResourceRefs));
|
|
66
|
+
const portClaims = asList(requirements.portClaims);
|
|
67
|
+
const service = requirements.service;
|
|
68
|
+
|
|
69
|
+
if (empty(requirements.approval)) errors.push('request.json: approval has no default; changing a shared runtime is never something an agent decides alone');
|
|
70
|
+
if (kind && !KIND_EFFECTS[kind]) errors.push(`request.json: ${kind} is not a service kind this operator operates`);
|
|
71
|
+
if (KIND_EFFECTS[kind]) {
|
|
72
|
+
if (new Set(desiredEffects).size !== desiredEffects.length) errors.push('request.json: desiredState.effects must not repeat an effect');
|
|
73
|
+
for (const e of desiredEffects) if (!KIND_EFFECTS[kind].includes(e)) errors.push(`request.json: requested effect ${e} does not belong to the ${kind} service kind`);
|
|
74
|
+
}
|
|
75
|
+
if (!empty(service) && mutable.size && !mutable.has(service)) errors.push('request.json: the operated service must be inside desiredState.mutableResourceRefs');
|
|
76
|
+
for (const r of mutable) if (observationOnly.has(r)) errors.push(`request.json: resource ${r} cannot be both mutable and observation-only`);
|
|
77
|
+
for (const r of desiredResources) if (mutable.size && !mutable.has(r)) errors.push(`request.json: desired resource ${r} lies outside the mutable ceiling`);
|
|
78
|
+
const claimedPorts = portClaims.map((c) => c.port);
|
|
79
|
+
if (new Set(claimedPorts).size !== claimedPorts.length) errors.push('request.json: portClaims must not claim the same port twice');
|
|
80
|
+
for (const c of portClaims) if (desiredResources.length && !desiredResources.includes(c.resourceRef)) errors.push(`request.json: port ${c.port} is claimed for ${c.resourceRef}, which this operation does not own`);
|
|
81
|
+
// The input contract refuses any string that carries credential material.
|
|
82
|
+
forEachString(request?.requirements ?? {}, (text, at) => { if (credentialLeak(text)) errors.push(`request.json: ${at} carries a credential value, which cannot enter the operator contract`); });
|
|
83
|
+
|
|
84
|
+
let delta = null;
|
|
85
|
+
if (present.has('delta') && has('response/data/delta.json')) { try { delta = JSON.parse(await read('response/data/delta.json')); } catch { delta = null; } }
|
|
86
|
+
let checks = null;
|
|
87
|
+
if (present.has('checks') && has('response/data/checks.json')) { try { checks = JSON.parse(await read('response/data/checks.json')); } catch { checks = null; } }
|
|
88
|
+
|
|
89
|
+
let inventoried = new Set();
|
|
90
|
+
if (delta) {
|
|
91
|
+
const refs = delta.inventoriedResources.map((r) => r.resourceRef);
|
|
92
|
+
inventoried = new Set(refs);
|
|
93
|
+
if (new Set(refs).size !== refs.length) errors.push('response/data/delta.json: inventoriedResources must not list a resource twice');
|
|
94
|
+
const ports = delta.observedPortHolders.map((h) => h.port);
|
|
95
|
+
if (new Set(ports).size !== ports.length) errors.push('response/data/delta.json: observedPortHolders must not list the same port twice');
|
|
96
|
+
if (!empty(service) && delta.serviceRef !== service) errors.push('response/data/delta.json: the delta operates a service the request did not name');
|
|
97
|
+
if (!empty(service) && !inventoried.has(service)) errors.push(`response/data/delta.json: service ${service} was not inventoried before the operation`);
|
|
98
|
+
if (kind && delta.serviceKind !== kind) errors.push(`response/data/delta.json: the delta names the ${delta.serviceKind} branch, not ${kind}`);
|
|
99
|
+
const byRef = new Map(delta.inventoriedResources.map((r) => [r.resourceRef, r]));
|
|
100
|
+
for (const r of desiredResources) {
|
|
101
|
+
const res = byRef.get(r);
|
|
102
|
+
if (!res) errors.push(`response/data/delta.json: desired resource ${r} is absent from the bound inventory`);
|
|
103
|
+
else if (res.kind !== kind) errors.push(`response/data/delta.json: resource ${r} belongs to the ${res.kind} branch and is outside this ${kind} operation`);
|
|
104
|
+
}
|
|
105
|
+
if (!empty(requirements.approval) && delta.approvalRef !== requirements.approval) errors.push('response/data/delta.json: the bound approval is not the one the request declared');
|
|
106
|
+
if (!empty(desired.planSha256) && delta.planSha256 !== desired.planSha256) errors.push('response/data/delta.json: planSha256 must equal the approved plan hash');
|
|
107
|
+
for (const e of delta.allowedEffects) if (KIND_EFFECTS[kind] && !KIND_EFFECTS[kind].includes(e)) errors.push(`response/data/delta.json: approved effect ${e} does not belong to the ${kind} service kind`);
|
|
108
|
+
for (const e of desiredEffects) if (!delta.allowedEffects.includes(e)) errors.push(`response/data/delta.json: requested effect ${e} is outside the approved effect set`);
|
|
109
|
+
|
|
110
|
+
const held = delta.capabilities.map((c) => c.capability);
|
|
111
|
+
if (new Set(held).size !== held.length) errors.push('response/data/delta.json: capabilities must not repeat a capability');
|
|
112
|
+
for (const c of held) if (KIND_CAPABILITIES[kind] && !KIND_CAPABILITIES[kind].includes(c)) errors.push(`response/data/delta.json: capability ${c} is not used by the ${kind} branch`);
|
|
113
|
+
for (const c of KIND_CAPABILITIES[kind] ?? []) if (!held.includes(c)) errors.push(`response/data/delta.json: the ${kind} branch requires the ${c} capability`);
|
|
114
|
+
|
|
115
|
+
const portHolderRefs = new Set(delta.observedPortHolders.map((h) => h.holderRef));
|
|
116
|
+
const applied = new Set(delta.appliedEffects);
|
|
117
|
+
const mutatedEffects = new Set();
|
|
118
|
+
for (const m of delta.mutations) {
|
|
119
|
+
if (KIND_EFFECTS[kind] && !KIND_EFFECTS[kind].includes(m.effect)) errors.push(`response/data/delta.json: effect ${m.effect} does not belong to the ${kind} service kind`);
|
|
120
|
+
if (!applied.has(m.effect)) errors.push(`response/data/delta.json: mutation effect ${m.effect} is absent from appliedEffects`);
|
|
121
|
+
mutatedEffects.add(m.effect);
|
|
122
|
+
if (!inventoried.has(m.resourceRef)) errors.push(`response/data/delta.json: resource ${m.resourceRef} was mutated without being inventoried first`);
|
|
123
|
+
// A port already in use is a coordination finding, never permission to reclaim it.
|
|
124
|
+
if (portHolderRefs.has(m.resourceRef)) errors.push(`response/data/delta.json: resource ${m.resourceRef} holds a claimed port and must never be mutated to free it`);
|
|
125
|
+
if (delta.mutableResourceRefs.length && !delta.mutableResourceRefs.includes(m.resourceRef)) errors.push(`response/data/delta.json: resource ${m.resourceRef} was mutated outside the mutable ceiling`);
|
|
126
|
+
}
|
|
127
|
+
for (const e of applied) if (!mutatedEffects.has(e)) errors.push(`response/data/delta.json: applied effect ${e} records no mutation`);
|
|
128
|
+
if (delta.convergence === 'already-converged' && delta.mutations.length > 0) errors.push('response/data/delta.json: an already-converged operation cannot report a mutation');
|
|
129
|
+
if (delta.convergence === 'converged' && delta.mutations.length === 0) errors.push('response/data/delta.json: a converged operation must report the mutation that converged it');
|
|
130
|
+
// The receipt is durable; a durable record of a capability is a leaked credential with a delay.
|
|
131
|
+
forEachString(delta, (text, at) => { if (credentialLeak(text)) errors.push(`response/data/delta.json: ${at} records a credential, which the receipt refuses`); });
|
|
132
|
+
} else if (response.status === 'done') errors.push('response/data/delta.json: a done branch needs the derived and applied delta');
|
|
133
|
+
|
|
134
|
+
let findingCount = 0;
|
|
135
|
+
if (checks) {
|
|
136
|
+
if (!empty(service) && checks.serviceRef !== service) errors.push('response/data/checks.json: the proof set names a service the request did not operate');
|
|
137
|
+
if (kind && checks.serviceKind !== kind) errors.push('response/data/checks.json: the proof set names another service kind');
|
|
138
|
+
// The required proof set is the whole set the branch publishes; the caller cannot ask for less.
|
|
139
|
+
const requested = new Set(checks.requiredCheckNames);
|
|
140
|
+
for (const n of KIND_CHECKS[kind] ?? []) if (!requested.has(n)) errors.push(`response/data/checks.json: the ${kind} branch must require the ${n} check`);
|
|
141
|
+
for (const n of requested) if (KIND_CHECKS[kind] && !KIND_CHECKS[kind].includes(n)) errors.push(`response/data/checks.json: check ${n} does not belong to the ${kind} service kind`);
|
|
142
|
+
|
|
143
|
+
const seen = new Set();
|
|
144
|
+
const proved = new Set();
|
|
145
|
+
for (const c of checks.checks) {
|
|
146
|
+
const key = `${c.name}|${c.resourceRef}`;
|
|
147
|
+
if (seen.has(key)) errors.push(`response/data/checks.json: check ${c.name} is recorded twice for ${c.resourceRef}`);
|
|
148
|
+
seen.add(key);
|
|
149
|
+
if (delta && !inventoried.has(c.resourceRef)) errors.push(`response/data/checks.json: check ${c.name} names uninventoried resource ${c.resourceRef}`);
|
|
150
|
+
if (c.status === 'passed') proved.add(c.name);
|
|
151
|
+
else if (response.status === 'done') errors.push(`response/data/checks.json: check ${c.name} failed, so the operation cannot be reported as operated`);
|
|
152
|
+
}
|
|
153
|
+
if (response.status === 'done') for (const n of KIND_CHECKS[kind] ?? []) if (!proved.has(n)) errors.push(`response/data/checks.json: the ${kind} branch cannot be proved without the ${n} check`);
|
|
154
|
+
|
|
155
|
+
findingCount = checks.findings.length;
|
|
156
|
+
for (const f of checks.findings) {
|
|
157
|
+
if (delta && !inventoried.has(f.resourceRef)) errors.push(`response/data/checks.json: finding on ${f.resourceRef} names an uninventoried resource`);
|
|
158
|
+
if (f.code !== 'PORT_COORDINATION_REQUIRED') continue;
|
|
159
|
+
if (f.port === null) errors.push('response/data/checks.json: a port coordination finding must name the port');
|
|
160
|
+
if (f.holderRef === null) errors.push('response/data/checks.json: a port coordination finding must name the process that already holds the port');
|
|
161
|
+
if (response.status === 'done') errors.push('response/data/checks.json: a port coordination finding cannot end in an operated outcome');
|
|
162
|
+
else if (response.stop !== 'PORT_CONFLICT') errors.push('response/response.json: a port coordination finding requires the PORT_CONFLICT failure');
|
|
163
|
+
}
|
|
164
|
+
forEachString(checks, (text, at) => { if (credentialLeak(text)) errors.push(`response/data/checks.json: ${at} records a credential, which the receipt refuses`); });
|
|
165
|
+
} else if (response.status === 'done') errors.push('response/data/checks.json: a done branch needs the proved check set');
|
|
166
|
+
|
|
167
|
+
if (present.has('platform-operation-receipt') && has('response/response.md')) {
|
|
168
|
+
const text = await read('response/response.md');
|
|
169
|
+
const binding = Object.fromEntries((tableUnder(text, '## Binding') ?? []).map(([k, v]) => [k, v]));
|
|
170
|
+
if (!empty(service) && binding.Service !== service) errors.push('response/response.md: Binding names a service the request did not operate');
|
|
171
|
+
if (kind && binding['Service kind'] !== kind) errors.push('response/response.md: Binding names another service kind');
|
|
172
|
+
if (!empty(desired.planSha256) && binding['Desired state'] !== desired.planSha256) errors.push('response/response.md: Binding carries a desired state the approval did not cover');
|
|
173
|
+
if (!empty(requirements.approval) && binding.Approval !== requirements.approval) errors.push('response/response.md: Binding names an approval the request did not declare');
|
|
174
|
+
const conv = Object.fromEntries((tableUnder(text, '## Convergence') ?? []).map(([k, v]) => [k, v]));
|
|
175
|
+
if (delta && conv.Convergence !== delta.convergence) errors.push(`response/response.md: Convergence says ${conv.Convergence} but the delta says ${delta.convergence}`);
|
|
176
|
+
const mdChecks = tableUnder(text, '## Checks') ?? [];
|
|
177
|
+
if (checks && mdChecks.length !== checks.checks.length) errors.push(`response/response.md: Checks has ${mdChecks.length} rows, the proof set has ${checks.checks.length}`);
|
|
178
|
+
const mdMutations = tableUnder(text, '## Mutations') ?? [];
|
|
179
|
+
if (delta && mdMutations.length !== delta.mutations.length) errors.push(`response/response.md: Mutations has ${mdMutations.length} rows, the delta has ${delta.mutations.length}`);
|
|
180
|
+
const mdFindings = tableUnder(text, '## Findings') ?? [];
|
|
181
|
+
if (checks && mdFindings.length !== findingCount) errors.push(`response/response.md: Findings has ${mdFindings.length} rows, the proof set has ${findingCount}`);
|
|
182
|
+
if (credentialLeak(text)) errors.push('response/response.md: a credential handle or secret-shaped token must never be recorded in the receipt');
|
|
183
|
+
} else if (response.status === 'done') errors.push('response/response.md: a done branch needs the operation receipt');
|
|
184
|
+
|
|
185
|
+
return { errors };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
189
|
+
const target = process.argv[2];
|
|
190
|
+
if (!target) { process.stderr.write('usage: node validate.mjs <session>/step-N/parallel-M\n'); process.exit(2); }
|
|
191
|
+
const { errors } = await validatePlatformStep(path.resolve(target));
|
|
192
|
+
if (errors.length) { process.stderr.write(`${errors.join('\n')}\n`); process.exitCode = 1; } else process.stdout.write('valid platform.operate branch\n');
|
|
193
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 9,
|
|
3
|
+
"note": "Stop codes only quality.verify emits. Same entry shape as operators/errors.json, scope implicit. Shared codes (INVALID_INPUT, SOURCE_DRIFT, NO_PROGRESS) come from operators/errors.json.",
|
|
4
|
+
"codes": {
|
|
5
|
+
"PREDECESSOR_MIXED": {
|
|
6
|
+
"domain": "caller",
|
|
7
|
+
"disposition": "terminate",
|
|
8
|
+
"meaning": {
|
|
9
|
+
"en": "Two predecessor receipts describe different source heads, so their union is a delivery nobody built.",
|
|
10
|
+
"vi": "Hai biên bản tiền nhiệm mô tả hai source head khác nhau, nên hợp của chúng là một delivery chẳng ai xây."
|
|
11
|
+
},
|
|
12
|
+
"resume": {
|
|
13
|
+
"en": "Supply one coherent predecessor set on one head.",
|
|
14
|
+
"vi": "Cấp một bộ tiền nhiệm nhất quán trên cùng một head."
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"PREDECESSOR_STALE": {
|
|
18
|
+
"domain": "caller",
|
|
19
|
+
"disposition": "terminate",
|
|
20
|
+
"meaning": {
|
|
21
|
+
"en": "A predecessor fingerprint no longer matches the frozen source.",
|
|
22
|
+
"vi": "Fingerprint của một tiền nhiệm không còn khớp source đã đóng băng."
|
|
23
|
+
},
|
|
24
|
+
"resume": {
|
|
25
|
+
"en": "Bring a refreshed upstream receipt.",
|
|
26
|
+
"vi": "Mang biên bản thượng nguồn đã làm mới."
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"GATE_UNAVAILABLE": {
|
|
30
|
+
"domain": "platform",
|
|
31
|
+
"disposition": "terminate",
|
|
32
|
+
"meaning": {
|
|
33
|
+
"en": "A required gate cannot be executed at all in this environment, and an unmeasurable gate is not a passed one.",
|
|
34
|
+
"vi": "Một cổng bắt buộc hoàn toàn không chạy được ở môi trường này, và cổng không đo được không phải cổng đã qua."
|
|
35
|
+
},
|
|
36
|
+
"resume": {
|
|
37
|
+
"en": "Provide a working gate environment.",
|
|
38
|
+
"vi": "Cấp một môi trường chạy cổng hoạt động được."
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"DEBT_UNAPPROVED": {
|
|
42
|
+
"domain": "caller",
|
|
43
|
+
"disposition": "terminate",
|
|
44
|
+
"meaning": {
|
|
45
|
+
"en": "A declared debt has no live owner approval, or it covers a gate that passed or a boundary-drift failure.",
|
|
46
|
+
"vi": "Một khoản nợ đã khai không có phê duyệt còn sống của chủ, hoặc phủ lên một cổng đã pass hay một lỗi boundary-drift."
|
|
47
|
+
},
|
|
48
|
+
"resume": {
|
|
49
|
+
"en": "Supply the unexpired owner approval, or drop the debt.",
|
|
50
|
+
"vi": "Cấp phê duyệt của chủ còn hạn, hoặc bỏ khoản nợ."
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 9,
|
|
3
|
+
"id": "quality.verify",
|
|
4
|
+
"domain": "quality",
|
|
5
|
+
"job": "Verify one bounded delivery by running its declared gates against an unchanged predecessor receipt at one frozen head, and return the exact measured verdict, repairing nothing.",
|
|
6
|
+
"package": "operator.md",
|
|
7
|
+
"errors": "errors.json",
|
|
8
|
+
"validator": "validate.mjs",
|
|
9
|
+
"selfTest": "self-test.mjs",
|
|
10
|
+
"resources": {
|
|
11
|
+
"profile": "luna",
|
|
12
|
+
"grammarBound": false,
|
|
13
|
+
"tools": {
|
|
14
|
+
"@tools/fileread": "context-aliases",
|
|
15
|
+
"@tools/git": "read",
|
|
16
|
+
"@tools/shell": "declared-commands",
|
|
17
|
+
"@tools/http": "probe"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# quality.verify
|
|
2
|
+
|
|
3
|
+
## Job
|
|
4
|
+
|
|
5
|
+
Verify one bounded delivery by running its declared gates against an unchanged predecessor receipt
|
|
6
|
+
at one frozen head, and return the exact measured verdict, repairing nothing.
|
|
7
|
+
|
|
8
|
+
## One delivery, one head, at least one producer receipt
|
|
9
|
+
|
|
10
|
+
The three Inputs are the three shapes a delivery arrives in: a backend implementation, a frontend
|
|
11
|
+
source application, and the `changes` record that names which paths moved and which gates and
|
|
12
|
+
surfaces they touch. Each is optional on its own and at least one must be present, because a
|
|
13
|
+
verification with no producer receipt has no head to freeze and no delivery to measure. Every
|
|
14
|
+
predecessor receipt must report the same source head, and that head must be the head
|
|
15
|
+
`request/request.json` froze; two predecessors on different heads describe two different deliveries,
|
|
16
|
+
and gating the union of them measures something nobody built. That is `PREDECESSOR_MIXED`, refused
|
|
17
|
+
before a single command runs rather than discovered later as a confusing gate failure. A predecessor
|
|
18
|
+
whose fingerprint no longer matches the frozen source is `PREDECESSOR_STALE`. What a predecessor
|
|
19
|
+
decided is consumed unchanged: this operator never re-plans the delivery, re-opens its boundary, or
|
|
20
|
+
forms an opinion about whether the change was a good one.
|
|
21
|
+
|
|
22
|
+
## The head is confirmed inside the gate
|
|
23
|
+
|
|
24
|
+
There is no separate head-verification step, because a head confirmed anywhere but at the gate is a
|
|
25
|
+
head that could drift before the first command. The producer wrote its delivery on the session
|
|
26
|
+
branch `session/<sessionId>` of the routed checkout, in a git worktree prepared from the frozen head,
|
|
27
|
+
and committed it once. `request/request.json` therefore pins `@workspaces/be` or `@workspaces/fe` at
|
|
28
|
+
that exact commit sha in `contexts[].head`, and step 1 confirms the observed head equals it before
|
|
29
|
+
anything else happens; a difference is `SOURCE_DRIFT`. The predecessor receipt's own commit must
|
|
30
|
+
equal that same head, because a receipt describing a commit the gates are not standing on is
|
|
31
|
+
`PREDECESSOR_STALE`. Every gate runs inside that session branch worktree and never on the person's
|
|
32
|
+
checked-out branch, so a gate result names one commit somebody can check out again.
|
|
33
|
+
|
|
34
|
+
## A red gate is a verdict, not a stop
|
|
35
|
+
|
|
36
|
+
Quality measures. It does not repair, redesign, reclassify, or negotiate. A failing gate produces a
|
|
37
|
+
red verdict naming the failure and its classification, and that verdict goes back to the owner who
|
|
38
|
+
can fix it; the branch is `done`, not `blocked`, because the operator did exactly what it was asked
|
|
39
|
+
to do. Only an inability to reach any verdict at all is a stop. The operator does not touch product
|
|
40
|
+
source, does not adjust a gate command or its configuration to change an outcome, and does not
|
|
41
|
+
substitute an easier check for a hard one.
|
|
42
|
+
|
|
43
|
+
## A gate result is measured, never narrated
|
|
44
|
+
|
|
45
|
+
Every executed gate carries its command reference, its exit code, and its evidence, in its own file
|
|
46
|
+
under `response/data/gates/`. One file per gate is what makes a gate result quotable on its own: a
|
|
47
|
+
later reader opens `lint.json` and sees one command, one exit code, one classification, without
|
|
48
|
+
reading around a bundle. A pass means exit code zero with evidence beside it; a failure means a
|
|
49
|
+
non-zero exit code with evidence and a classification. The classification is read from the structured
|
|
50
|
+
diagnostics after the command ran, never chosen before it: `in-boundary` when the delivery owner can
|
|
51
|
+
fix it, `boundary-drift` when fixing it would change an approved boundary, `flaky` when identical
|
|
52
|
+
source and environment produced contradictory outcomes, and `external-blocker` when the environment
|
|
53
|
+
or a dependency prevented a verdict at all. A rerun exists to tell those four apart. It never exists
|
|
54
|
+
to convert an unexplained failure into a pass. No gate is skipped, suppressed, substituted, or moved
|
|
55
|
+
with `passWithNoTests`, and a zero-test run is not a pass. Every gate file records the same source
|
|
56
|
+
head, because two gates standing on two heads measured two deliveries.
|
|
57
|
+
|
|
58
|
+
## Two facts about this codebase
|
|
59
|
+
|
|
60
|
+
Sonar measures new code only. The pinned gate is scoped to the change, so a green Sonar result is a
|
|
61
|
+
statement about the diff and not about the project, and a project may sit red beneath it. Under the
|
|
62
|
+
default `sonarScope` of `new-code`, a passing Sonar result is recorded together with a
|
|
63
|
+
`SONAR_NEW_CODE_ONLY` finding; without it a later reader takes a green gate for project health, which
|
|
64
|
+
is the exact misreading this operator exists to prevent.
|
|
65
|
+
|
|
66
|
+
End-to-end is never run unless a person asked for it in this invocation, which is why
|
|
67
|
+
`explicitE2eRequest` defaults to false. Otherwise the gate is recorded as `skipped-not-requested`
|
|
68
|
+
with an `E2E_NOT_REQUESTED` finding: no command, no exit code, no evidence, and no implication that
|
|
69
|
+
behaviour was proved. Planning the e2e gate without that request is invalid input.
|
|
70
|
+
|
|
71
|
+
## Coverage carries four thresholds, not one
|
|
72
|
+
|
|
73
|
+
Statements, lines, functions and branches are each compared against their own threshold, and
|
|
74
|
+
branches carry an independent one because a branch threshold folded into the statement figure is how
|
|
75
|
+
an untested error path passes. The thresholds default to the four percentages the routed gate
|
|
76
|
+
configuration already pins, so a person who names none is measured against the project's own bar. A
|
|
77
|
+
metric under its threshold makes the unit gate a failure and records `COVERAGE_BELOW_THRESHOLD`; it
|
|
78
|
+
is never a note beside a green result.
|
|
79
|
+
|
|
80
|
+
## Debt is explicit and owned
|
|
81
|
+
|
|
82
|
+
A gate stays red only when an owner-approved debt record covers it, naming the debt, the gate, the
|
|
83
|
+
approval, the owner and the expiry, and only when that approval is still live at the instant the
|
|
84
|
+
gate was measured. An expired approval is not a debt and a debt against a gate that passed is a
|
|
85
|
+
record of nothing; both are refused as `DEBT_UNAPPROVED`. A debt covers only an `in-boundary`
|
|
86
|
+
failure, the kind the delivery owner can fix; a `boundary-drift` failure belongs to whoever owns the
|
|
87
|
+
boundary and cannot be owed away here. `declaredDebts` defaults to the empty list, so carrying a red
|
|
88
|
+
gate is always something a person did on purpose.
|
|
89
|
+
|
|
90
|
+
## The verdict
|
|
91
|
+
|
|
92
|
+
`pass` requires every required gate to have passed, or to have failed `in-boundary` under a declared
|
|
93
|
+
debt. Every other shape is `fail`, including a required gate the environment blocked: an unmeasurable
|
|
94
|
+
gate is not a passed one. A non-required gate that fails is recorded and does not by itself turn the
|
|
95
|
+
verdict red, which is the whole reason `required` exists, and it is the gate plan's declaration and
|
|
96
|
+
never this operator's judgement.
|
|
97
|
+
|
|
98
|
+
## Boundary
|
|
99
|
+
|
|
100
|
+
Context is read-only. The operator writes only `response/` of its own branch: one `gate-result` file
|
|
101
|
+
per gate under `response/data/gates/`, `data/coverage.json`, `response.md` and `response.json`. It
|
|
102
|
+
does not modify product source, configuration, or a gate command; it does not redesign, repair, or
|
|
103
|
+
reclassify a measured failure into a pass; it does not run the end-to-end suite unasked; it does not
|
|
104
|
+
add, weaken, skip, suppress, or substitute a declared gate; it does not read a project-level Sonar
|
|
105
|
+
verdict out of a new-code quality gate; and it does not carry a debt no owner approved or whose
|
|
106
|
+
approval expired.
|
|
107
|
+
|
|
108
|
+
## Context
|
|
109
|
+
|
|
110
|
+
| Alias | Bind | Required |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| `@workspaces/<project>/<role>/gates` | the pinned gate commands, their configuration and the thresholds they carry; what "the same gate" means across runs | yes |
|
|
113
|
+
| `@workspaces/be` | the routed backend checkout at the pinned commit, the subject every gate measures when the delivery is a backend | no |
|
|
114
|
+
| `@workspaces/fe` | the routed frontend checkout at the pinned commit, the subject every gate measures when the delivery is a frontend | no |
|
|
115
|
+
| `@worktrees/debts` | owner-approved debt records and their expiry; a red gate is carried only from here | no |
|
|
116
|
+
|
|
117
|
+
## Inputs
|
|
118
|
+
|
|
119
|
+
| Kind | From | Required |
|
|
120
|
+
| --- | --- | --- |
|
|
121
|
+
| `backend-source-application` | `backend.source.apply`, the backend delivery to verify | no |
|
|
122
|
+
| `frontend-source-application` | `frontend.source.apply`, the frontend delivery to verify | no |
|
|
123
|
+
| `changes` | `backend.source.apply` or `frontend.source.apply`, the paths that moved and the gates and surfaces they name | no |
|
|
124
|
+
|
|
125
|
+
## Requirements
|
|
126
|
+
|
|
127
|
+
| Field | Type | Default | Ask |
|
|
128
|
+
| --- | --- | --- | --- |
|
|
129
|
+
| `gates` | list of `{gate, commandRef, configRef, required}` | the routed gate plan | Which pinned gates to run, once each, from format, lint, typecheck, build, unit-coverage, integration, e2e and sonar |
|
|
130
|
+
| `thresholds` | list of `{statements, lines, functions, branches}` | the four percentages the routed gate configuration pins | The percentage each coverage metric must meet, branches on its own |
|
|
131
|
+
| `explicitE2eRequest` | choice | false | false unless a person asked for the end-to-end suite in this invocation; true only then |
|
|
132
|
+
| `sonarScope` | choice | new-code | new-code or overall; it must agree with whether sonar is in the gate plan |
|
|
133
|
+
| `declaredDebts` | list of `{debtId, gate, approvalRef, ownerRef, expiresAt}` | [] | Owner-approved debts that let a named gate stay red |
|
|
134
|
+
| `resume` | token | null | The blocked branch's token when re-entering after a stop |
|
|
135
|
+
|
|
136
|
+
## Steps
|
|
137
|
+
|
|
138
|
+
| # | Step | Params | Reads | Writes | Stops with |
|
|
139
|
+
| --- | --- | --- | --- | --- | --- |
|
|
140
|
+
| 1 | Validate the gate, confirm the frozen head and the resume | `resume` | `request/request.json`, @workspaces/be or @workspaces/fe at the commit the request pinned, @tools/git | — | `INVALID_INPUT`, `SOURCE_DRIFT`, `NO_PROGRESS` |
|
|
141
|
+
| 2 | Consume the predecessors unchanged | — | inputs `backend-source-application`, `frontend-source-application` and `changes` at their fingerprints, and the commit each one recorded | — | `PREDECESSOR_MIXED`, `PREDECESSOR_STALE` |
|
|
142
|
+
| 3 | Run the gates in declared order | `gates`, `explicitE2eRequest`, `sonarScope` | @workspaces/<project>/<role>/gates, @workspaces/be or @workspaces/fe as the subject each gate measures, @tools/http | `response/data/gates/<gate>.json`, @tools/shell | `GATE_UNAVAILABLE` |
|
|
143
|
+
| 4 | Apply the coverage policy | `thresholds` | `response/data/gates/<gate>.json` of the unit gate | `response/data/coverage.json` | — |
|
|
144
|
+
| 5 | Classify each failure from its diagnostics | — | `response/data/gates/<gate>.json` of every red gate | — | — |
|
|
145
|
+
| 6 | Apply approved debt | `declaredDebts` | @worktrees/debts, `response/data/gates/<gate>.json` | — | `DEBT_UNAPPROVED` |
|
|
146
|
+
| 7 | Compute the verdict, write the receipt and emit | — | everything above | `response/response.md`, `response/response.json` | — |
|
|
147
|
+
|
|
148
|
+
A gate that could not be executed at all in this environment is `GATE_UNAVAILABLE` when it was
|
|
149
|
+
required; a non-required gate the environment blocked is recorded as `external-blocker` and the
|
|
150
|
+
verdict absorbs it. There is no repair code, because repair is not this operator's job: an
|
|
151
|
+
`in-boundary` failure returns as a red verdict to the owner who can fix it, and the fixed delivery
|
|
152
|
+
comes back as a new head with a new predecessor fingerprint. A resume reuses only unchanged
|
|
153
|
+
fingerprinted observations and consumes the exact delta; a resume that adds no predecessor, gate,
|
|
154
|
+
debt or source change is `NO_PROGRESS`, because the same fingerprint cannot yield a different answer.
|
|
155
|
+
|
|
156
|
+
## Outputs
|
|
157
|
+
|
|
158
|
+
| Kind | File | Type | Required |
|
|
159
|
+
| --- | --- | --- | --- |
|
|
160
|
+
| `quality-verification` | `response/response.md` | md | yes |
|
|
161
|
+
| `gate-result` | `response/data/gates/<gate>.json` | data | yes |
|
|
162
|
+
| `coverage` | `response/data/coverage.json` | data | no |
|
|
163
|
+
|
|
164
|
+
## Stops
|
|
165
|
+
|
|
166
|
+
| Code | Disposition |
|
|
167
|
+
| --- | --- |
|
|
168
|
+
| `INVALID_INPUT` | terminate |
|
|
169
|
+
| `SOURCE_DRIFT` | terminate |
|
|
170
|
+
| `NO_PROGRESS` | terminate |
|
|
171
|
+
| `PREDECESSOR_MIXED` | terminate |
|
|
172
|
+
| `PREDECESSOR_STALE` | terminate |
|
|
173
|
+
| `GATE_UNAVAILABLE` | terminate |
|
|
174
|
+
| `DEBT_UNAPPROVED` | terminate |
|
|
175
|
+
|
|
176
|
+
## Next
|
|
177
|
+
|
|
178
|
+
| When | Operator |
|
|
179
|
+
| --- | --- |
|
|
180
|
+
| a backend gate failed in boundary and the backend owner must fix it | `backend.source.apply` |
|
|
181
|
+
| a frontend gate failed in boundary and the frontend owner must apply the fix | `frontend.source.apply` |
|
|
182
|
+
| the verdict is green and the delivery is ready to publish | `git.publish` |
|
|
183
|
+
| the verdict is green and the published head must reach an environment | `release.deploy` |
|
|
184
|
+
| the gates are green and the promise must be reconciled against the delivered source | `business.decide` |
|
|
185
|
+
| the gates are green and a person asked for the journey to be walked | `uat.verify` |
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# quality.verify
|
|
2
|
+
|
|
3
|
+
## Việc
|
|
4
|
+
|
|
5
|
+
Kiểm định một delivery có ranh giới bằng cách chạy các cổng đã khai trên một biên bản tiền nhiệm còn
|
|
6
|
+
nguyên vẹn tại một head đã đóng băng, rồi trả về đúng phán quyết đo được, không sửa gì cả.
|
|
7
|
+
|
|
8
|
+
## Một delivery, một head, ít nhất một biên bản của người sản xuất
|
|
9
|
+
|
|
10
|
+
Ba Đầu vào là ba hình dạng mà một delivery có thể tới: một bản hiện thực backend, một bản áp source
|
|
11
|
+
frontend, và bản ghi `changes` nêu những path nào đã dịch chuyển cùng những cổng và bề mặt chúng chạm
|
|
12
|
+
tới. Từng cái đều không bắt buộc, nhưng phải có ít nhất một, vì một lần kiểm định không có biên bản
|
|
13
|
+
của người sản xuất thì chẳng có head nào để đóng băng và chẳng có delivery nào để đo. Mọi biên bản
|
|
14
|
+
tiền nhiệm phải báo cùng một source head, và head đó phải là head mà `request/request.json` đã đóng
|
|
15
|
+
băng; hai tiền nhiệm trên hai head khác nhau mô tả hai delivery khác nhau, và gác hợp của chúng là đo
|
|
16
|
+
một thứ không ai xây. Đó là `PREDECESSOR_MIXED`, bị từ chối trước khi có lệnh nào chạy thay vì lộ ra
|
|
17
|
+
về sau dưới dạng một lỗi cổng khó hiểu. Một tiền nhiệm có fingerprint không còn khớp source đã đóng
|
|
18
|
+
băng là `PREDECESSOR_STALE`. Điều tiền nhiệm đã quyết được tiêu thụ nguyên vẹn: operator này không
|
|
19
|
+
bao giờ lập lại kế hoạch delivery, mở lại ranh giới của nó, hay có ý kiến về việc thay đổi ấy hay dở.
|
|
20
|
+
|
|
21
|
+
## Head được xác nhận ngay trong cổng vào
|
|
22
|
+
|
|
23
|
+
Không có bước xác nhận head riêng, vì một head được xác nhận ở chỗ nào khác cổng vào là một head còn
|
|
24
|
+
kịp trôi trước lệnh đầu tiên. Người sản xuất đã viết delivery trên nhánh phiên `session/<sessionId>`
|
|
25
|
+
của checkout đã route, trong một git worktree dựng từ head đã đóng băng, và commit đúng một lần. Vì
|
|
26
|
+
thế `request/request.json` ghim `@workspaces/be` hoặc `@workspaces/fe` tại đúng commit sha đó trong
|
|
27
|
+
`contexts[].head`, và bước 1 xác nhận head quan sát được bằng đúng nó trước khi bất kỳ điều gì khác
|
|
28
|
+
xảy ra; khác nhau là `SOURCE_DRIFT`. Commit mà biên bản tiền nhiệm ghi phải bằng chính head đó, vì
|
|
29
|
+
một biên bản mô tả một commit mà các cổng không đứng trên là `PREDECESSOR_STALE`. Mọi cổng chạy bên
|
|
30
|
+
trong worktree của nhánh phiên ấy, không bao giờ trên nhánh người ta đang checkout, nên một kết quả
|
|
31
|
+
cổng gọi tên đúng một commit mà ai đó có thể checkout lại.
|
|
32
|
+
|
|
33
|
+
## Cổng đỏ là một phán quyết, không phải một mã dừng
|
|
34
|
+
|
|
35
|
+
Quality đo. Nó không sửa, không thiết kế lại, không phân loại lại, không mặc cả. Một cổng hỏng sinh
|
|
36
|
+
ra một phán quyết đỏ gọi tên cái hỏng và phân loại của nó, và phán quyết ấy quay về người chủ có thể
|
|
37
|
+
sửa; nhánh là `done`, không phải `blocked`, vì operator đã làm đúng việc được giao. Chỉ khi hoàn toàn
|
|
38
|
+
không đạt tới được phán quyết nào thì mới là một mã dừng. Operator không đụng vào source sản phẩm,
|
|
39
|
+
không chỉnh lệnh cổng hay cấu hình của nó để đổi kết quả, và không thay một kiểm khó bằng một kiểm dễ.
|
|
40
|
+
|
|
41
|
+
## Kết quả cổng là đo được, không phải kể lại
|
|
42
|
+
|
|
43
|
+
Mọi cổng đã chạy mang theo tham chiếu lệnh, mã thoát và bằng chứng của nó, trong file riêng dưới
|
|
44
|
+
`response/data/gates/`. Một file cho một cổng là thứ khiến kết quả cổng trích dẫn được một mình: người
|
|
45
|
+
đọc sau mở `lint.json` và thấy một lệnh, một mã thoát, một phân loại, không phải đọc vòng quanh một
|
|
46
|
+
bó. Pass nghĩa là mã thoát bằng không kèm bằng chứng bên cạnh; fail nghĩa là mã thoát khác không kèm
|
|
47
|
+
bằng chứng và một phân loại. Phân loại được đọc từ chẩn đoán có cấu trúc sau khi lệnh đã chạy, không
|
|
48
|
+
bao giờ chọn trước: `in-boundary` khi chủ delivery sửa được, `boundary-drift` khi sửa nó là đổi một
|
|
49
|
+
ranh giới đã duyệt, `flaky` khi cùng source cùng môi trường cho hai kết quả mâu thuẫn, và
|
|
50
|
+
`external-blocker` khi môi trường hay một phụ thuộc chặn hẳn phán quyết. Chạy lại tồn tại để phân
|
|
51
|
+
biệt bốn thứ đó. Nó không bao giờ tồn tại để biến một lỗi chưa giải thích được thành pass. Không cổng
|
|
52
|
+
nào bị bỏ, bịt, thay hay dời bằng `passWithNoTests`, và một lần chạy không có test nào không phải là
|
|
53
|
+
pass. Mọi file cổng ghi cùng một source head, vì hai cổng đứng trên hai head là đã đo hai delivery.
|
|
54
|
+
|
|
55
|
+
## Hai sự thật về codebase này
|
|
56
|
+
|
|
57
|
+
Sonar chỉ đo new code. Cổng đã ghim được thu hẹp theo thay đổi, nên một kết quả Sonar xanh là phát
|
|
58
|
+
biểu về cái diff chứ không phải về dự án, và dự án bên dưới vẫn có thể đỏ. Dưới mặc định `new-code`
|
|
59
|
+
của `sonarScope`, một kết quả Sonar pass được ghi kèm phát hiện `SONAR_NEW_CODE_ONLY`; thiếu nó thì
|
|
60
|
+
người đọc sau hiểu cổng xanh là sức khỏe dự án, đúng cái hiểu sai mà operator này sinh ra để chặn.
|
|
61
|
+
|
|
62
|
+
End-to-end không bao giờ chạy trừ khi có người yêu cầu trong chính lần gọi này, nên
|
|
63
|
+
`explicitE2eRequest` mặc định là false. Ngược lại cổng được ghi là `skipped-not-requested` kèm phát
|
|
64
|
+
hiện `E2E_NOT_REQUESTED`: không lệnh, không mã thoát, không bằng chứng, và không hàm ý nào rằng hành
|
|
65
|
+
vi đã được chứng minh. Lập kế hoạch cổng e2e mà không có yêu cầu ấy là đầu vào không hợp lệ.
|
|
66
|
+
|
|
67
|
+
## Coverage mang bốn ngưỡng, không phải một
|
|
68
|
+
|
|
69
|
+
Statement, line, function và branch mỗi thứ so với ngưỡng riêng của nó, và branch mang một ngưỡng độc
|
|
70
|
+
lập vì gộp ngưỡng branch vào con số statement chính là cách một nhánh lỗi chưa test đi lọt. Ngưỡng
|
|
71
|
+
mặc định lấy bốn phần trăm mà cấu hình cổng của route đã ghim, nên người không nêu ngưỡng nào vẫn được
|
|
72
|
+
đo theo đúng thanh của dự án. Một chỉ số dưới ngưỡng của nó làm cổng unit thành fail và ghi
|
|
73
|
+
`COVERAGE_BELOW_THRESHOLD`; nó không bao giờ là một ghi chú bên cạnh một kết quả xanh.
|
|
74
|
+
|
|
75
|
+
## Nợ phải tường minh và có chủ
|
|
76
|
+
|
|
77
|
+
Một cổng chỉ được ở lại màu đỏ khi có bản ghi nợ được chủ duyệt phủ lên nó, nêu khoản nợ, cổng, phê
|
|
78
|
+
duyệt, chủ và hạn, và chỉ khi phê duyệt ấy còn sống tại đúng thời điểm cổng được đo. Phê duyệt hết hạn
|
|
79
|
+
không phải là nợ, và một khoản nợ đặt lên cổng đã pass là bản ghi của hư không; cả hai bị từ chối bằng
|
|
80
|
+
`DEBT_UNAPPROVED`. Nợ chỉ phủ được lỗi `in-boundary`, loại mà chủ delivery sửa được; lỗi
|
|
81
|
+
`boundary-drift` thuộc về người sở hữu ranh giới và không thể khất ở đây. `declaredDebts` mặc định là
|
|
82
|
+
danh sách rỗng, nên mang một cổng đỏ luôn là việc có người cố ý làm.
|
|
83
|
+
|
|
84
|
+
## Phán quyết
|
|
85
|
+
|
|
86
|
+
`pass` đòi mọi cổng bắt buộc đã pass, hoặc đã fail `in-boundary` dưới một khoản nợ đã khai. Mọi hình
|
|
87
|
+
dạng khác là `fail`, kể cả một cổng bắt buộc bị môi trường chặn: cổng không đo được không phải cổng đã
|
|
88
|
+
qua. Một cổng không bắt buộc mà fail thì được ghi lại và tự nó không làm phán quyết đỏ, đó chính là lý
|
|
89
|
+
do `required` tồn tại, và nó là khai báo của kế hoạch cổng chứ không bao giờ là phán đoán của operator.
|
|
90
|
+
|
|
91
|
+
## Ranh giới
|
|
92
|
+
|
|
93
|
+
Context chỉ đọc. Operator chỉ ghi vào `response/` của nhánh mình: một file `gate-result` cho mỗi cổng
|
|
94
|
+
dưới `response/data/gates/`, `data/coverage.json`, `response.md` và `response.json`. Nó không sửa
|
|
95
|
+
source sản phẩm, cấu hình hay lệnh cổng; không thiết kế lại, sửa chữa hay phân loại một lỗi đã đo
|
|
96
|
+
thành pass; không chạy bộ end-to-end khi không được hỏi; không thêm, làm yếu, bỏ, bịt hay thay một
|
|
97
|
+
cổng đã khai; không đọc phán quyết mức dự án ra từ một quality gate chỉ đo new code; và không mang một
|
|
98
|
+
khoản nợ không ai duyệt hoặc đã hết hạn.
|
|
99
|
+
|
|
100
|
+
## Context
|
|
101
|
+
|
|
102
|
+
| Alias | Bind | Bắt buộc |
|
|
103
|
+
| --- | --- | --- |
|
|
104
|
+
| `@workspaces/<project>/<role>/gates` | lệnh cổng đã ghim, cấu hình và ngưỡng chúng mang; là nghĩa của "cùng một cổng" qua các lần chạy | có |
|
|
105
|
+
| `@workspaces/be` | checkout backend đã route tại commit đã ghim, chủ thể mọi cổng đo khi delivery là backend | không |
|
|
106
|
+
| `@workspaces/fe` | checkout frontend đã route tại commit đã ghim, chủ thể mọi cổng đo khi delivery là frontend | không |
|
|
107
|
+
| `@worktrees/debts` | bản ghi nợ được chủ duyệt và hạn của chúng; một cổng đỏ chỉ được mang từ đây | không |
|
|
108
|
+
|
|
109
|
+
## Đầu vào
|
|
110
|
+
|
|
111
|
+
| Kind | Từ đâu | Bắt buộc |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| `backend-source-application` | `backend.source.apply`, delivery backend cần kiểm định | không |
|
|
114
|
+
| `frontend-source-application` | `frontend.source.apply`, delivery frontend cần kiểm định | không |
|
|
115
|
+
| `changes` | `backend.source.apply` hoặc `frontend.source.apply`, những path đã dịch chuyển cùng cổng và bề mặt chúng nêu | không |
|
|
116
|
+
|
|
117
|
+
## Yêu cầu
|
|
118
|
+
|
|
119
|
+
| Field | Kiểu | Mặc định | Hỏi |
|
|
120
|
+
| --- | --- | --- | --- |
|
|
121
|
+
| `gates` | list of `{gate, commandRef, configRef, required}` | the routed gate plan | Chạy những cổng đã ghim nào, mỗi cổng một lần, trong format, lint, typecheck, build, unit-coverage, integration, e2e và sonar |
|
|
122
|
+
| `thresholds` | list of `{statements, lines, functions, branches}` | the four percentages the routed gate configuration pins | Mỗi chỉ số coverage phải đạt bao nhiêu phần trăm, branch đứng riêng |
|
|
123
|
+
| `explicitE2eRequest` | choice | false | false trừ khi có người yêu cầu bộ end-to-end trong chính lần gọi này; chỉ khi đó mới true |
|
|
124
|
+
| `sonarScope` | choice | new-code | new-code hoặc overall; phải khớp với việc sonar có nằm trong kế hoạch cổng hay không |
|
|
125
|
+
| `declaredDebts` | list of `{debtId, gate, approvalRef, ownerRef, expiresAt}` | [] | Những khoản nợ được chủ duyệt cho phép một cổng có tên ở lại màu đỏ |
|
|
126
|
+
| `resume` | token | null | Token của nhánh bị chặn khi vào lại sau một mã dừng |
|
|
127
|
+
|
|
128
|
+
## Các bước
|
|
129
|
+
|
|
130
|
+
| # | Bước | Tham số | Đọc | Ghi | Dừng với |
|
|
131
|
+
| --- | --- | --- | --- | --- | --- |
|
|
132
|
+
| 1 | Kiểm cổng vào, xác nhận head đã đóng băng và resume | `resume` | `request/request.json`, @workspaces/be hoặc @workspaces/fe tại commit mà request đã ghim, @tools/git | — | `INVALID_INPUT`, `SOURCE_DRIFT`, `NO_PROGRESS` |
|
|
133
|
+
| 2 | Tiêu thụ tiền nhiệm nguyên vẹn | — | Đầu vào `backend-source-application`, `frontend-source-application` và `changes` tại fingerprint của chúng, cùng commit mà mỗi cái đã ghi | — | `PREDECESSOR_MIXED`, `PREDECESSOR_STALE` |
|
|
134
|
+
| 3 | Chạy các cổng theo thứ tự đã khai | `gates`, `explicitE2eRequest`, `sonarScope` | @workspaces/<project>/<role>/gates, @workspaces/be hoặc @workspaces/fe là chủ thể mỗi cổng đo, @tools/http | `response/data/gates/<gate>.json`, @tools/shell | `GATE_UNAVAILABLE` |
|
|
135
|
+
| 4 | Áp chính sách coverage | `thresholds` | `response/data/gates/<gate>.json` của cổng unit | `response/data/coverage.json` | — |
|
|
136
|
+
| 5 | Phân loại từng lỗi từ chẩn đoán của nó | — | `response/data/gates/<gate>.json` của mọi cổng đỏ | — | — |
|
|
137
|
+
| 6 | Áp nợ đã được duyệt | `declaredDebts` | @worktrees/debts, `response/data/gates/<gate>.json` | — | `DEBT_UNAPPROVED` |
|
|
138
|
+
| 7 | Tính phán quyết, viết biên bản và phát | — | mọi thứ ở trên | `response/response.md`, `response/response.json` | — |
|
|
139
|
+
|
|
140
|
+
Một cổng hoàn toàn không chạy được ở môi trường này là `GATE_UNAVAILABLE` khi nó bắt buộc; một cổng
|
|
141
|
+
không bắt buộc bị môi trường chặn được ghi là `external-blocker` và phán quyết hấp thụ nó. Không có mã
|
|
142
|
+
sửa chữa, vì sửa chữa không phải việc của operator này: một lỗi `in-boundary` quay về dưới dạng phán
|
|
143
|
+
quyết đỏ tới người chủ sửa được nó, và delivery đã sửa quay lại thành một head mới với fingerprint
|
|
144
|
+
tiền nhiệm mới. Một lần resume chỉ dùng lại quan sát có fingerprint không đổi và tiêu thụ đúng phần
|
|
145
|
+
delta; một lần resume không thêm tiền nhiệm, cổng, nợ hay thay đổi source nào là `NO_PROGRESS`, vì
|
|
146
|
+
cùng một fingerprint không thể cho một câu trả lời khác.
|
|
147
|
+
|
|
148
|
+
## Đầu ra
|
|
149
|
+
|
|
150
|
+
| Kind | File | Kiểu | Bắt buộc |
|
|
151
|
+
| --- | --- | --- | --- |
|
|
152
|
+
| `quality-verification` | `response/response.md` | md | có |
|
|
153
|
+
| `gate-result` | `response/data/gates/<gate>.json` | data | có |
|
|
154
|
+
| `coverage` | `response/data/coverage.json` | data | không |
|
|
155
|
+
|
|
156
|
+
## Dừng
|
|
157
|
+
|
|
158
|
+
| Code | Xử lý |
|
|
159
|
+
| --- | --- |
|
|
160
|
+
| `INVALID_INPUT` | terminate |
|
|
161
|
+
| `SOURCE_DRIFT` | terminate |
|
|
162
|
+
| `NO_PROGRESS` | terminate |
|
|
163
|
+
| `PREDECESSOR_MIXED` | terminate |
|
|
164
|
+
| `PREDECESSOR_STALE` | terminate |
|
|
165
|
+
| `GATE_UNAVAILABLE` | terminate |
|
|
166
|
+
| `DEBT_UNAPPROVED` | terminate |
|
|
167
|
+
|
|
168
|
+
## Kế tiếp
|
|
169
|
+
|
|
170
|
+
| Khi | Operator |
|
|
171
|
+
| --- | --- |
|
|
172
|
+
| một cổng backend hỏng trong ranh giới và chủ backend phải sửa | `backend.source.apply` |
|
|
173
|
+
| một cổng frontend hỏng trong ranh giới và chủ frontend phải áp bản sửa | `frontend.source.apply` |
|
|
174
|
+
| phán quyết xanh và delivery sẵn sàng để publish | `git.publish` |
|
|
175
|
+
| phán quyết xanh và head đã publish phải tới được một môi trường | `release.deploy` |
|
|
176
|
+
| các gate xanh và lời hứa phải được đối chiếu với source đã giao | `business.decide` |
|
|
177
|
+
| các gate xanh và có người yêu cầu đi thử hành trình | `uat.verify` |
|