@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,152 @@
|
|
|
1
|
+
// workspace.bind's own law over one branch, on top of the shared step check: the receipt and the route
|
|
2
|
+
// name the same checkout and head; a source checkout carries no directory and a sibling one
|
|
3
|
+
// does; the hydrated route belongs to this Source and to the requested project and role; the observed
|
|
4
|
+
// checkout is the one the route resolved to; mutation is ready only on the mutation branch and a
|
|
5
|
+
// forbidden worktree policy binds nowhere else; the businesses root is derived from the checkout, never
|
|
6
|
+
// typed; a consumed runtime is ready, owned by someone else, bound to this project, and reachable only
|
|
7
|
+
// through distinct origin-only localhost projections, and only when runtimeNeed asked for one; no hint
|
|
8
|
+
// survives the gate; and a blocked branch records neither a hydration nor a consumed runtime.
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
10
|
+
import { readFile } from 'node:fs/promises';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import process from 'node:process';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
import { validateStep } from '../../scripts/validate-step.mjs';
|
|
15
|
+
import { tableUnder } from '../../scripts/validate-response.mjs';
|
|
16
|
+
|
|
17
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
18
|
+
// Only an origin-only localhost URL is an endpoint. 127.0.0.1, a host, or a path is not.
|
|
19
|
+
const LOCAL_ORIGIN = /^http:\/\/localhost:([1-9][0-9]{0,4})$/;
|
|
20
|
+
const empty = (v) => v === undefined || v === null || v === '' || v === '—';
|
|
21
|
+
const fields = (rows) => Object.fromEntries((rows ?? []).map(([k, v]) => [k, v]));
|
|
22
|
+
// Route paths arrive from two files written on different machines, so they are compared as normalised
|
|
23
|
+
// strings: separator style and a trailing slash are notation, not identity.
|
|
24
|
+
const comparablePath = (value) => String(value).replaceAll('\\', '/').replace(/\/+$/, '').toLowerCase();
|
|
25
|
+
|
|
26
|
+
export async function validateWorkspaceStep(branchDir, root = ROOT) {
|
|
27
|
+
const base = await validateStep(root, branchDir);
|
|
28
|
+
const errors = [...base.errors];
|
|
29
|
+
const { response, requirements = {}, present = new Set() } = base;
|
|
30
|
+
if (!response || response.operatorId !== 'workspace.bind') return { errors };
|
|
31
|
+
const has = (f) => existsSync(path.join(branchDir, f));
|
|
32
|
+
const read = (f) => readFile(path.join(branchDir, f), 'utf8');
|
|
33
|
+
|
|
34
|
+
let route = null;
|
|
35
|
+
if (present.has('route') && has('response/data/route.json')) {
|
|
36
|
+
try { route = JSON.parse(await read('response/data/route.json')); } catch { route = null; }
|
|
37
|
+
}
|
|
38
|
+
if (response.status === 'blocked' && route !== null) {
|
|
39
|
+
errors.push('response/response.json: a blocked branch cannot carry a route');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (route) {
|
|
43
|
+
const { checkout, gitPolicy, mutationReadiness, runtime } = route;
|
|
44
|
+
if (!empty(requirements.project) && route.project !== requirements.project) errors.push(`response/data/route.json: project ${route.project} differs from the request's ${requirements.project}`);
|
|
45
|
+
if (!empty(requirements.role) && route.role !== requirements.role) errors.push(`response/data/route.json: role ${route.role} differs from the request's ${requirements.role}`);
|
|
46
|
+
if (checkout.sourceHead !== route.sourceHead) errors.push('response/data/route.json: the binding and the routed checkout must name the same source head');
|
|
47
|
+
if (comparablePath(checkout.diskPath) !== comparablePath(checkout.gitRoot)) errors.push('response/data/route.json: the checkout disk path and Git root must be the same checkout');
|
|
48
|
+
if (checkout.repositoryKind === 'source' && checkout.directory !== null) errors.push('response/data/route.json: a source checkout must report a null directory');
|
|
49
|
+
if (checkout.repositoryKind === 'sibling' && checkout.directory === null) errors.push('response/data/route.json: a sibling checkout must report its relative directory');
|
|
50
|
+
|
|
51
|
+
// The businesses root is derived from the checkout, never typed by a caller: on a source checkout it
|
|
52
|
+
// is <gitRoot>/.worktrees/businesses when that worktree exists, otherwise null; a sibling checkout
|
|
53
|
+
// carries no business authority. business.decide copies this value.
|
|
54
|
+
const derivedBusinesses = `${String(checkout.gitRoot).replace(/[\\/]+$/, '')}/.worktrees/businesses`;
|
|
55
|
+
const { businesses } = route.authorityRoots;
|
|
56
|
+
if (checkout.repositoryKind === 'sibling' && businesses !== null) errors.push('response/data/route.json: a sibling checkout carries no business authority root');
|
|
57
|
+
if (businesses !== null && businesses !== derivedBusinesses) errors.push(`response/data/route.json: authorityRoots.businesses must be derived from the checkout as ${derivedBusinesses}`);
|
|
58
|
+
|
|
59
|
+
// Mutation is ready only on the declared mutation branch. Reporting readiness anywhere else is how a
|
|
60
|
+
// task branch acquires permission it was never routed.
|
|
61
|
+
const asked = requirements.gitPolicy ?? null;
|
|
62
|
+
if (asked && !empty(asked.worktreeBranches) && gitPolicy.worktreeBranches !== asked.worktreeBranches) errors.push(`response/data/route.json: the routed policy ${gitPolicy.worktreeBranches} differs from the request's ${asked.worktreeBranches}`);
|
|
63
|
+
if (asked && !empty(asked.mutationBranch) && gitPolicy.mutationBranch !== asked.mutationBranch) errors.push(`response/data/route.json: the routed mutation branch ${gitPolicy.mutationBranch} differs from the request's ${asked.mutationBranch}`);
|
|
64
|
+
const declaredRoots = Array.isArray(requirements.declaredWriteRoots) ? requirements.declaredWriteRoots : [];
|
|
65
|
+
for (const root of declaredRoots) if (!route.writeRoots.includes(root)) errors.push(`response/data/route.json: the request declared the write root ${root}, which the binding does not carry`);
|
|
66
|
+
if (mutationReadiness === 'ready' && checkout.branch !== gitPolicy.mutationBranch) errors.push(`response/data/route.json: mutation is ready only on ${gitPolicy.mutationBranch}, not on ${checkout.branch}`);
|
|
67
|
+
if (gitPolicy.worktreeBranches === 'session-only' && checkout.branch !== gitPolicy.mutationBranch && !/^session\//.test(checkout.branch)) errors.push(`response/data/route.json: a session-only worktree policy binds only on ${gitPolicy.mutationBranch} or on a session/<sessionId> branch, not on ${checkout.branch}`);
|
|
68
|
+
if (gitPolicy.worktreeBranches === 'forbidden' && checkout.branch !== gitPolicy.mutationBranch) errors.push('response/data/route.json: a forbidden worktree policy cannot bind a route on another branch');
|
|
69
|
+
|
|
70
|
+
if (runtime !== null) {
|
|
71
|
+
// A consumer may run only against a ready owner generation. A port that merely listens, a starting
|
|
72
|
+
// owner, or a degraded one is evidence to report, not a runtime to use.
|
|
73
|
+
if ((requirements.runtimeNeed ?? 'none') === 'none') errors.push('response/data/route.json: runtimeNeed is none, so step 5 never ran and no runtime may be bound');
|
|
74
|
+
if (runtime.status !== 'ready') errors.push(`response/data/route.json: a route cannot bind a ${runtime.status} runtime owner for consumption`);
|
|
75
|
+
if (runtime.endpointBinding.project !== route.project) errors.push('response/data/route.json: the endpoint binding belongs to another project than the bound route');
|
|
76
|
+
const services = Object.values(runtime.endpointBinding.services);
|
|
77
|
+
if (new Set(services).size !== services.length) errors.push('response/data/route.json: endpoint binding service keys must be distinct');
|
|
78
|
+
const ports = [];
|
|
79
|
+
for (const [role, endpoint] of Object.entries(runtime.endpoints)) {
|
|
80
|
+
const match = LOCAL_ORIGIN.exec(endpoint);
|
|
81
|
+
if (match === null) { errors.push(`response/data/route.json: the ${role} endpoint ${endpoint} is not an origin-only localhost projection`); continue; }
|
|
82
|
+
const port = Number(match[1]);
|
|
83
|
+
if (port > 65535) errors.push(`response/data/route.json: the ${role} endpoint port exceeds 65535`);
|
|
84
|
+
ports.push(port);
|
|
85
|
+
}
|
|
86
|
+
if (new Set(ports).size !== ports.length) errors.push('response/data/route.json: the frontend, api, and identity endpoints must resolve to distinct ports');
|
|
87
|
+
} else if (requirements.runtimeNeed === 'consume' && response.status === 'done') {
|
|
88
|
+
errors.push('response/data/route.json: a caller that consumes the shared runtime must bind the runtime owner');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (present.has('workspace-route-binding') && has('response/response.md')) {
|
|
93
|
+
const text = await read('response/response.md');
|
|
94
|
+
const binding = fields(tableUnder(text, '## Binding'));
|
|
95
|
+
const checkout = fields(tableUnder(text, '## Checkout'));
|
|
96
|
+
const policy = fields(tableUnder(text, '## Policy'));
|
|
97
|
+
const runtimeRows = fields(tableUnder(text, '## Runtime'));
|
|
98
|
+
const writeRoots = (tableUnder(text, '## Write roots') ?? []).map(([p]) => p);
|
|
99
|
+
|
|
100
|
+
const findingKeys = new Set();
|
|
101
|
+
for (const [code, subject] of tableUnder(text, '## Findings') ?? []) {
|
|
102
|
+
const key = `${code}|${subject}`;
|
|
103
|
+
if (findingKeys.has(key)) errors.push(`response/response.md: finding ${code} repeats subject ${subject}`);
|
|
104
|
+
findingKeys.add(key);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (route) {
|
|
108
|
+
if (binding.Project !== route.project) errors.push(`response/response.md: Project ${binding.Project} differs from the route binding's ${route.project}`);
|
|
109
|
+
if (binding.Role !== route.role) errors.push(`response/response.md: Role ${binding.Role} differs from the route binding's ${route.role}`);
|
|
110
|
+
if (binding['Source head'] !== route.sourceHead) errors.push('response/response.md: Source head differs from the route binding');
|
|
111
|
+
if (binding['Hydrated route'] !== route.hydratedRouteRef) errors.push('response/response.md: Hydrated route differs from the route binding');
|
|
112
|
+
if (checkout.Branch !== route.checkout.branch) errors.push(`response/response.md: Branch ${checkout.Branch} differs from the route binding's ${route.checkout.branch}`);
|
|
113
|
+
if (checkout['Mutation readiness'] !== route.mutationReadiness) errors.push('response/response.md: Mutation readiness differs from the route binding');
|
|
114
|
+
if (checkout['Repository kind'] !== route.checkout.repositoryKind) errors.push('response/response.md: Repository kind differs from the route binding');
|
|
115
|
+
const businesses = route.authorityRoots.businesses;
|
|
116
|
+
if ((empty(checkout['Businesses root']) ? null : checkout['Businesses root']) !== businesses) errors.push(`response/response.md: Businesses root ${checkout['Businesses root']} differs from the derived ${String(businesses)}`);
|
|
117
|
+
if (policy['Worktree branches'] !== route.gitPolicy.worktreeBranches) errors.push('response/response.md: Worktree branches differs from the route binding');
|
|
118
|
+
if (policy['Mutation branch'] !== route.gitPolicy.mutationBranch) errors.push('response/response.md: Mutation branch differs from the route binding');
|
|
119
|
+
if (writeRoots.length !== route.writeRoots.length || writeRoots.some((p) => !route.writeRoots.includes(p))) errors.push('response/response.md: Write roots differ from the route binding');
|
|
120
|
+
|
|
121
|
+
// The portable-to-hydrated resolution is the whole authority of this receipt, so it is stated
|
|
122
|
+
// rather than assumed.
|
|
123
|
+
if (!findingKeys.has(`ROUTE_HYDRATED_FROM_PORTABLE|${route.hydratedRouteRef}`)) errors.push('response/response.md: a bound route must record the hydrated route it resolved from');
|
|
124
|
+
if (route.gitPolicy.worktreeBranches === 'forbidden' && !findingKeys.has(`WORKTREE_BRANCH_FORBIDDEN|${route.gitPolicy.mutationBranch}`)) errors.push('response/response.md: a forbidden worktree policy must be recorded on the bound route');
|
|
125
|
+
if (route.provenanceHeadRef !== null && !findingKeys.has(`PROVENANCE_HEAD_BOUND|${route.provenanceHeadRef}`)) errors.push('response/response.md: a bound provenance head must be recorded');
|
|
126
|
+
if (route.runtime !== null) {
|
|
127
|
+
if (!findingKeys.has(`RUNTIME_CONSUMED_NOT_OWNED|${route.runtime.ownerTaskId}`)) errors.push('response/response.md: a consumed runtime must record that the caller does not own it');
|
|
128
|
+
if (runtimeRows['Owner task'] !== route.runtime.ownerTaskId) errors.push('response/response.md: Owner task differs from the route binding');
|
|
129
|
+
if (runtimeRows.Status !== route.runtime.status) errors.push('response/response.md: Runtime status differs from the route binding');
|
|
130
|
+
if (runtimeRows['Consumer role'] !== 'consumer') errors.push('response/response.md: the caller consumes the runtime and never owns it');
|
|
131
|
+
for (const [label, key] of [['Frontend', 'frontend'], ['Api', 'api'], ['Identity', 'identity']]) {
|
|
132
|
+
if (runtimeRows[label] !== route.runtime.endpoints[key]) errors.push(`response/response.md: the ${key} endpoint differs from the route binding`);
|
|
133
|
+
}
|
|
134
|
+
} else if (Object.keys(runtimeRows).length) errors.push('response/response.md: the Runtime section carries rows but no runtime was bound');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Hints are refused at the gate, so no finding may record one as if it had been weighed.
|
|
138
|
+
for (const key of findingKeys) if (key.startsWith('HINT_REJECTED|')) errors.push('response/response.md: a hint is INVALID_INPUT at the gate, so a bound receipt never records HINT_REJECTED');
|
|
139
|
+
// Only the sealed roster reference is bound; no credential is read.
|
|
140
|
+
if (![...findingKeys].some((key) => key.startsWith('IDENTITY_ROSTER_SEALED|'))) errors.push('response/response.md: a bound route must record that the credential roster was sealed and never read');
|
|
141
|
+
} else if (response.status === 'done') {
|
|
142
|
+
errors.push('response/response.md: a bound branch needs the route receipt');
|
|
143
|
+
}
|
|
144
|
+
return { errors };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
148
|
+
const target = process.argv[2];
|
|
149
|
+
if (!target) { process.stderr.write('usage: node validate.mjs <session>/step-N/parallel-M\n'); process.exit(2); }
|
|
150
|
+
const { errors } = await validateWorkspaceStep(path.resolve(target));
|
|
151
|
+
if (errors.length) { process.stderr.write(`${errors.join('\n')}\n`); process.exitCode = 1; } else process.stdout.write('valid workspace.bind branch\n');
|
|
152
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@starci/skills",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "StarCi Skills: one entry, fourteen operators, a closed routing map and a tool registry, installed as the .claude runtime of a repository for Claude Code and Codex.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"starci-skills": "bin/starci-skills.mjs"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin/",
|
|
15
|
+
"INDEX.md",
|
|
16
|
+
"INDEX.vi.md",
|
|
17
|
+
"SKILL.md",
|
|
18
|
+
"SKILL.vi.md",
|
|
19
|
+
"routing.json",
|
|
20
|
+
"alias/",
|
|
21
|
+
"knowledge/",
|
|
22
|
+
"operators/",
|
|
23
|
+
"readiness/",
|
|
24
|
+
"resources/",
|
|
25
|
+
"scripts/",
|
|
26
|
+
"templates/",
|
|
27
|
+
"workflows/",
|
|
28
|
+
"README.md",
|
|
29
|
+
"README.vi.md"
|
|
30
|
+
],
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/starci183/starci-skills.git"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://harness.starci.org",
|
|
36
|
+
"keywords": [
|
|
37
|
+
"claude-code",
|
|
38
|
+
"codex",
|
|
39
|
+
"agent",
|
|
40
|
+
"skills",
|
|
41
|
+
"starci"
|
|
42
|
+
],
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"test": "node scripts/validate-routing.mjs && node scripts/validate-resources.mjs && node scripts/validate-knowledge-citations.mjs && node scripts/validate-alias.mjs && node scripts/generate-alias-doc.mjs --check && node scripts/validate-operator.mjs && node scripts/validate-workflows.mjs && node scripts/validate-defaults.mjs && node scripts/generate-operators-index.mjs --check && node scripts/validate-templates.mjs && node scripts/run-operator-self-tests.mjs && node --test scripts/*.spec.mjs && npm run docs:check",
|
|
48
|
+
"docs:check": "node docs/scripts/generate-docs.mjs --check"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"id": "workspace-multidevice-commit-boundary-v1",
|
|
4
|
+
"purpose": "Git synchronizes portable intent and encrypted authority; every device rebuilds machine-local state from that intent after proving the same master identity.",
|
|
5
|
+
"sourceRepository": {
|
|
6
|
+
"requiredTracked": [
|
|
7
|
+
"AGENTS.md",
|
|
8
|
+
"CLAUDE.md",
|
|
9
|
+
".workspaces/config.json"
|
|
10
|
+
],
|
|
11
|
+
"requiredTrackedTree": ".workspaces/projects",
|
|
12
|
+
"forbiddenTrackedPrefixes": [
|
|
13
|
+
".workspace/",
|
|
14
|
+
".workspaces/local/",
|
|
15
|
+
".worktrees/",
|
|
16
|
+
".gitmounts/",
|
|
17
|
+
".sessions/"
|
|
18
|
+
],
|
|
19
|
+
"requiredIgnoredRoots": [
|
|
20
|
+
".workspace",
|
|
21
|
+
".workspaces/local",
|
|
22
|
+
".worktrees",
|
|
23
|
+
".gitmounts",
|
|
24
|
+
".sessions"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
"classes": {
|
|
28
|
+
"portableWorkspace": {
|
|
29
|
+
"commit": true,
|
|
30
|
+
"paths": [".workspaces/config.json", ".workspaces/device-state.json", ".workspaces/projects/<project>/<role>.json"],
|
|
31
|
+
"rule": "Contains only machine-independent repository, branch, role, Grammar and contract intent. Absolute paths, observed commits, credentials and runtime state are forbidden."
|
|
32
|
+
},
|
|
33
|
+
"hydratedWorkspace": {
|
|
34
|
+
"commit": false,
|
|
35
|
+
"paths": [".workspaces/local/**"],
|
|
36
|
+
"rule": "Derived independently on each device from portable declarations and the local filesystem."
|
|
37
|
+
},
|
|
38
|
+
"durableWorktree": {
|
|
39
|
+
"commit": "inside-own-checkout-only",
|
|
40
|
+
"paths": [".worktrees/<kind>/**"],
|
|
41
|
+
"rule": "The Source repository never tracks the container. Approved product or authority changes are committed by the worktree's own Git repository and branch."
|
|
42
|
+
},
|
|
43
|
+
"ephemeralWorktree": {
|
|
44
|
+
"commit": false,
|
|
45
|
+
"paths": [".claude/worktrees/<id>/**"],
|
|
46
|
+
"rule": "Agent checkout state is disposable and never portable authority."
|
|
47
|
+
},
|
|
48
|
+
"referenceCheckout": {
|
|
49
|
+
"commit": false,
|
|
50
|
+
"paths": [".worktrees/references/<project>-<role>/**"],
|
|
51
|
+
"rule": "Read-only clean precedent checkout. Refresh or rebuild it; never author or push from it."
|
|
52
|
+
},
|
|
53
|
+
"gitmountCheckout": {
|
|
54
|
+
"commit": false,
|
|
55
|
+
"paths": [".gitmounts/<name>/**"],
|
|
56
|
+
"rule": "A private operational-data checkout is reconstructed by npm run sync from metadata.json. Its optional credentialEnv and credentialFallbackEnvs resolve only to encrypted runtime files; plaintext tokens, authenticated URLs and the checkout itself never enter Git."
|
|
57
|
+
},
|
|
58
|
+
"runtimeState": {
|
|
59
|
+
"commit": false,
|
|
60
|
+
"paths": [".sessions/**", ".workspaces/local/state/**", ".workspaces/local/routes/**"],
|
|
61
|
+
"rule": "Generated runtime config, virtual environments, PIDs, receipts, caches and absolute routes are local-only."
|
|
62
|
+
},
|
|
63
|
+
"encryptedDeviceState": {
|
|
64
|
+
"commit": false,
|
|
65
|
+
"remote": "private-release-assets",
|
|
66
|
+
"paths": [".workspace/device-state/**"],
|
|
67
|
+
"rule": "Quiesced Docker-volume archives are encrypted to the master age recipient, chunked, checksum-bound and published only as private release assets. Plain archives, Docker volumes and downloaded ciphertext remain local-only; Git tracks only the portable contract."
|
|
68
|
+
},
|
|
69
|
+
"encryptedAuthority": {
|
|
70
|
+
"commit": true,
|
|
71
|
+
"paths": [".stacks/**/*.enc", ".stacks/**/KEYS.md", ".stacks/*/infra/compose/**"],
|
|
72
|
+
"rule": "Ciphertext, recipient metadata and value-free topology are portable. The master identity and decrypted twins are never committed."
|
|
73
|
+
},
|
|
74
|
+
"masterIdentity": {
|
|
75
|
+
"commit": false,
|
|
76
|
+
"paths": ["~/.starci/master.identity"],
|
|
77
|
+
"rule": "Provision out of band on each trusted device. Possession permits decryption but never changes Git tracking rules."
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"deviceBootstrap": [
|
|
81
|
+
"Pull the Source repository and its independent StarCi skills checkout.",
|
|
82
|
+
"Prove the out-of-band master identity without printing its contents.",
|
|
83
|
+
"Validate tracked portable declarations and the Git boundary.",
|
|
84
|
+
"Run workspace-portable.mjs bootstrap --apply to clone absent declared sibling checkouts and hydrate machine-local project-role routes.",
|
|
85
|
+
"Create or verify declared worktrees and clean reference checkouts.",
|
|
86
|
+
"Run npm run sync to decrypt local secret twins and reconstruct declared private gitmounts without per-device GitHub login.",
|
|
87
|
+
"Fetch the latest compatible encrypted device-state release; restore only into absent or explicitly approved replaceable Docker volumes.",
|
|
88
|
+
"Rebuild declared runtime state without a derived search index.",
|
|
89
|
+
"Run Source readiness and prove default search against one exact canonical reference before product work."
|
|
90
|
+
]
|
|
91
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://starci.dev/v6/readiness/initialization/workspaces/config.schema.json",
|
|
4
|
+
"title": "StarCi workspace defaults",
|
|
5
|
+
"oneOf": [
|
|
6
|
+
{ "$ref": "#/$defs/v1" },
|
|
7
|
+
{ "$ref": "#/$defs/v6" }
|
|
8
|
+
],
|
|
9
|
+
"$defs": {
|
|
10
|
+
"languageTag": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$"
|
|
13
|
+
},
|
|
14
|
+
"v1": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"additionalProperties": false,
|
|
17
|
+
"required": ["version", "defaultLang"],
|
|
18
|
+
"properties": {
|
|
19
|
+
"$schema": { "type": "string" },
|
|
20
|
+
"version": { "const": 1 },
|
|
21
|
+
"defaultLang": { "$ref": "#/$defs/languageTag" }
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"v6": {
|
|
25
|
+
"type": "object",
|
|
26
|
+
"additionalProperties": false,
|
|
27
|
+
"required": ["schemaVersion", "defaultLang"],
|
|
28
|
+
"properties": {
|
|
29
|
+
"$schema": { "type": "string" },
|
|
30
|
+
"schemaVersion": { "const": 6 },
|
|
31
|
+
"defaultLang": { "$ref": "#/$defs/languageTag" }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://starci.dev/v6/workspaces/device-state.schema.json",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"additionalProperties": false,
|
|
6
|
+
"required": ["$schema", "schemaVersion", "id", "project", "docker", "encryption", "remote"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"$schema": { "type": "string", "minLength": 1 },
|
|
9
|
+
"schemaVersion": { "const": 1 },
|
|
10
|
+
"id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*-v[0-9]+$" },
|
|
11
|
+
"project": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
|
12
|
+
"docker": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"additionalProperties": false,
|
|
15
|
+
"required": ["helperImage", "volumes"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"helperImage": { "type": "string", "minLength": 1 },
|
|
18
|
+
"volumes": {
|
|
19
|
+
"type": "array",
|
|
20
|
+
"minItems": 1,
|
|
21
|
+
"uniqueItems": true,
|
|
22
|
+
"items": {
|
|
23
|
+
"type": "object",
|
|
24
|
+
"additionalProperties": false,
|
|
25
|
+
"required": ["name"],
|
|
26
|
+
"properties": {
|
|
27
|
+
"name": { "type": "string", "pattern": "^[A-Za-z0-9_.-]+$" }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"encryption": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"required": ["masterIdentity"],
|
|
37
|
+
"properties": {
|
|
38
|
+
"masterIdentity": { "type": "string", "pattern": "^~[\\\\/].+" }
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"remote": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"additionalProperties": false,
|
|
44
|
+
"required": ["repository", "releasePrefix", "tokenFile", "chunkBytes", "retention"],
|
|
45
|
+
"properties": {
|
|
46
|
+
"repository": { "type": "string", "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$" },
|
|
47
|
+
"releasePrefix": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
|
48
|
+
"tokenFile": { "type": "string", "pattern": "^(?![\\\\/]|[A-Za-z]:[\\\\/])(?!.*(?:^|[\\\\/])\\.\\.(?:[\\\\/]|$)).+$" },
|
|
49
|
+
"chunkBytes": { "type": "integer", "minimum": 1048576, "maximum": 134217728 },
|
|
50
|
+
"retention": { "type": "integer", "minimum": 1, "maximum": 20 }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://starci.dev/v6/readiness/initialization/workspaces/local-route.schema.json",
|
|
4
|
+
"title": "Machine-local StarCi workspace route",
|
|
5
|
+
"description": "Hydrated machine-local route. V1 and unrevisioned V6 remain readable for safe migration; schemaRevision 2 is the current V6 contract-aware envelope.",
|
|
6
|
+
"oneOf": [
|
|
7
|
+
{ "$ref": "#/$defs/v1" },
|
|
8
|
+
{ "$ref": "#/$defs/v6Legacy" },
|
|
9
|
+
{ "$ref": "#/$defs/v6Current" }
|
|
10
|
+
],
|
|
11
|
+
"$defs": {
|
|
12
|
+
"slug": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"maxLength": 80,
|
|
15
|
+
"pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
|
|
16
|
+
},
|
|
17
|
+
"absolutePath": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"minLength": 2,
|
|
20
|
+
"maxLength": 1024,
|
|
21
|
+
"pattern": "^([A-Za-z]:[\\\\/]|/).+"
|
|
22
|
+
},
|
|
23
|
+
"source": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"additionalProperties": false,
|
|
26
|
+
"required": ["path", "trust", "skills", "workspaceRoot"],
|
|
27
|
+
"properties": {
|
|
28
|
+
"path": { "$ref": "#/$defs/absolutePath" },
|
|
29
|
+
"trust": { "$ref": "#/$defs/absolutePath" },
|
|
30
|
+
"skills": { "$ref": "#/$defs/absolutePath" },
|
|
31
|
+
"workspaceRoot": { "$ref": "#/$defs/absolutePath" }
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "object",
|
|
36
|
+
"additionalProperties": false,
|
|
37
|
+
"required": ["diskPath", "gitRoot", "gitRepository", "branch", "head"],
|
|
38
|
+
"properties": {
|
|
39
|
+
"diskPath": { "$ref": "#/$defs/absolutePath" },
|
|
40
|
+
"gitRoot": { "$ref": "#/$defs/absolutePath" },
|
|
41
|
+
"gitRepository": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"maxLength": 512,
|
|
44
|
+
"pattern": "^https://github\\.com/[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+(?:\\.git)?$"
|
|
45
|
+
},
|
|
46
|
+
"branch": { "type": "string", "minLength": 1, "maxLength": 255 },
|
|
47
|
+
"head": { "type": "string", "pattern": "^[0-9a-f]{40}$" },
|
|
48
|
+
"gitPolicy": { "$ref": "#/$defs/gitPolicy" }
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"gitPolicy": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"additionalProperties": false,
|
|
54
|
+
"required": ["mutationBranch", "worktreeBranches", "incomingBranchRefs"],
|
|
55
|
+
"properties": {
|
|
56
|
+
"mutationBranch": { "type": "string", "minLength": 1, "maxLength": 255 },
|
|
57
|
+
"worktreeBranches": { "enum": ["forbidden", "session-only"], "description": "forbidden: no branch or worktree besides the mutation branch; session-only: a git worktree on session/<sessionId> may be created for one runtime session and is merged into the mutation branch by git.publish, then removed" },
|
|
58
|
+
"incomingBranchRefs": { "const": "merge-into-mutation-branch" }
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"commonContext": {
|
|
62
|
+
"properties": {
|
|
63
|
+
"instructions": {
|
|
64
|
+
"type": "array",
|
|
65
|
+
"uniqueItems": true,
|
|
66
|
+
"items": { "$ref": "#/$defs/absolutePath" }
|
|
67
|
+
},
|
|
68
|
+
"manifests": {
|
|
69
|
+
"type": "array",
|
|
70
|
+
"uniqueItems": true,
|
|
71
|
+
"items": { "$ref": "#/$defs/absolutePath" }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"v1Context": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"additionalProperties": false,
|
|
78
|
+
"required": ["instructions", "manifests", "grammar", "grammarProfile"],
|
|
79
|
+
"properties": {
|
|
80
|
+
"instructions": { "$ref": "#/$defs/commonContext/properties/instructions" },
|
|
81
|
+
"manifests": { "$ref": "#/$defs/commonContext/properties/manifests" },
|
|
82
|
+
"grammar": {
|
|
83
|
+
"oneOf": [
|
|
84
|
+
{ "$ref": "#/$defs/slug" },
|
|
85
|
+
{ "type": "null" }
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
"grammarProfile": {
|
|
89
|
+
"oneOf": [
|
|
90
|
+
{ "$ref": "#/$defs/slug" },
|
|
91
|
+
{ "type": "null" }
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"allOf": [
|
|
96
|
+
{
|
|
97
|
+
"if": { "properties": { "grammar": { "type": "null" } }, "required": ["grammar"] },
|
|
98
|
+
"then": { "properties": { "grammarProfile": { "type": "null" } } },
|
|
99
|
+
"else": { "properties": { "grammarProfile": { "$ref": "#/$defs/slug" } } }
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"v6PreContractContext": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"additionalProperties": false,
|
|
106
|
+
"required": ["instructions", "manifests", "grammarId"],
|
|
107
|
+
"properties": {
|
|
108
|
+
"instructions": { "$ref": "#/$defs/commonContext/properties/instructions" },
|
|
109
|
+
"manifests": { "$ref": "#/$defs/commonContext/properties/manifests" },
|
|
110
|
+
"grammarId": { "enum": ["core", "offset-pop", null] }
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"v6ContractContext": {
|
|
114
|
+
"type": "object",
|
|
115
|
+
"additionalProperties": false,
|
|
116
|
+
"required": ["instructions", "contract", "contractSource", "manifests", "grammarId"],
|
|
117
|
+
"properties": {
|
|
118
|
+
"instructions": { "$ref": "#/$defs/commonContext/properties/instructions" },
|
|
119
|
+
"contract": {
|
|
120
|
+
"oneOf": [
|
|
121
|
+
{ "$ref": "#/$defs/absolutePath" },
|
|
122
|
+
{ "type": "null" }
|
|
123
|
+
]
|
|
124
|
+
},
|
|
125
|
+
"contractSource": {
|
|
126
|
+
"oneOf": [
|
|
127
|
+
{ "type": "string", "minLength": 1, "maxLength": 1024 },
|
|
128
|
+
{ "type": "null" }
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
"manifests": { "$ref": "#/$defs/commonContext/properties/manifests" },
|
|
132
|
+
"grammarId": { "enum": ["core", "offset-pop", null] }
|
|
133
|
+
},
|
|
134
|
+
"allOf": [
|
|
135
|
+
{
|
|
136
|
+
"if": { "properties": { "contract": { "type": "null" } }, "required": ["contract"] },
|
|
137
|
+
"then": { "properties": { "contractSource": { "type": "null" } } },
|
|
138
|
+
"else": { "properties": { "contractSource": { "type": "string", "minLength": 1 } } }
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
"v1": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"additionalProperties": false,
|
|
145
|
+
"required": ["version", "project", "role", "source", "repository", "context", "updatedAt"],
|
|
146
|
+
"properties": {
|
|
147
|
+
"$schema": { "type": "string" },
|
|
148
|
+
"version": { "const": 1 },
|
|
149
|
+
"project": { "$ref": "#/$defs/slug" },
|
|
150
|
+
"role": { "$ref": "#/$defs/slug" },
|
|
151
|
+
"source": { "$ref": "#/$defs/source" },
|
|
152
|
+
"repository": { "$ref": "#/$defs/repository" },
|
|
153
|
+
"context": { "$ref": "#/$defs/v1Context" },
|
|
154
|
+
"updatedAt": { "type": "string", "format": "date-time" }
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"v6Legacy": {
|
|
158
|
+
"type": "object",
|
|
159
|
+
"additionalProperties": false,
|
|
160
|
+
"required": ["$schema", "schemaVersion", "project", "role", "source", "repository", "context", "updatedAt"],
|
|
161
|
+
"properties": {
|
|
162
|
+
"$schema": { "type": "string" },
|
|
163
|
+
"schemaVersion": { "const": 6 },
|
|
164
|
+
"project": { "$ref": "#/$defs/slug" },
|
|
165
|
+
"role": { "$ref": "#/$defs/slug" },
|
|
166
|
+
"source": { "$ref": "#/$defs/source" },
|
|
167
|
+
"repository": { "$ref": "#/$defs/repository" },
|
|
168
|
+
"context": {
|
|
169
|
+
"oneOf": [
|
|
170
|
+
{ "$ref": "#/$defs/v6PreContractContext" },
|
|
171
|
+
{ "$ref": "#/$defs/v6ContractContext" }
|
|
172
|
+
]
|
|
173
|
+
},
|
|
174
|
+
"updatedAt": { "type": "string", "format": "date-time" }
|
|
175
|
+
},
|
|
176
|
+
"allOf": [
|
|
177
|
+
{
|
|
178
|
+
"if": { "properties": { "role": { "enum": ["be", "chart"] } }, "required": ["role"] },
|
|
179
|
+
"then": { "properties": { "context": { "properties": { "grammarId": { "type": "null" } } } } }
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
},
|
|
183
|
+
"v6Current": {
|
|
184
|
+
"type": "object",
|
|
185
|
+
"additionalProperties": false,
|
|
186
|
+
"required": ["$schema", "schemaVersion", "schemaRevision", "project", "role", "source", "repository", "context", "updatedAt"],
|
|
187
|
+
"properties": {
|
|
188
|
+
"$schema": { "type": "string" },
|
|
189
|
+
"schemaVersion": { "const": 6 },
|
|
190
|
+
"schemaRevision": { "const": 2 },
|
|
191
|
+
"project": { "$ref": "#/$defs/slug" },
|
|
192
|
+
"role": { "$ref": "#/$defs/slug" },
|
|
193
|
+
"source": { "$ref": "#/$defs/source" },
|
|
194
|
+
"repository": { "$ref": "#/$defs/repository" },
|
|
195
|
+
"context": { "$ref": "#/$defs/v6ContractContext" },
|
|
196
|
+
"updatedAt": { "type": "string", "format": "date-time" }
|
|
197
|
+
},
|
|
198
|
+
"allOf": [
|
|
199
|
+
{
|
|
200
|
+
"if": { "properties": { "role": { "enum": ["be", "chart"] } }, "required": ["role"] },
|
|
201
|
+
"then": { "properties": { "context": { "properties": { "grammarId": { "type": "null" } } } } }
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|