@b00dea/cockpit 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +474 -0
- package/LICENSE +21 -0
- package/README.md +25 -2
- package/bin/cockpit.mjs +33 -0
- package/cockpit.schema.json +2644 -0
- package/dist/autonomy.d.ts +45 -0
- package/dist/autonomy.js +173 -0
- package/dist/bounded-read.d.ts +14 -0
- package/dist/bounded-read.js +42 -0
- package/dist/browser/commands.d.ts +117 -0
- package/dist/browser/commands.js +453 -0
- package/dist/browser/package-paths.d.ts +1 -0
- package/dist/browser/package-paths.js +6 -0
- package/dist/browser/proxy.d.ts +22 -0
- package/dist/browser/proxy.js +37 -0
- package/dist/browser/runtime.d.ts +118 -0
- package/dist/browser/runtime.js +186 -0
- package/dist/browser/surfaces.d.ts +141 -0
- package/dist/browser/surfaces.js +561 -0
- package/dist/browser/ui-gate.d.ts +15 -0
- package/dist/browser/ui-gate.js +18 -0
- package/dist/canonical-json.d.ts +2 -0
- package/dist/canonical-json.js +5 -0
- package/dist/cli-guard.d.ts +5 -0
- package/dist/cli-guard.js +71 -0
- package/dist/cli-main.d.ts +176 -0
- package/dist/cli-main.js +1584 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +16 -0
- package/dist/command-table.d.ts +121 -0
- package/dist/command-table.js +664 -0
- package/dist/config/load.d.ts +183 -0
- package/dist/config/load.js +367 -0
- package/dist/config/sibling-toml.d.ts +48 -0
- package/dist/config/sibling-toml.js +160 -0
- package/dist/config/surface-tools-toml.d.ts +9 -0
- package/dist/config/surface-tools-toml.js +37 -0
- package/dist/config/unconfirmed-toml.d.ts +9 -0
- package/dist/config/unconfirmed-toml.js +29 -0
- package/dist/config/verified-write.d.ts +43 -0
- package/dist/config/verified-write.js +89 -0
- package/dist/diagnostics.d.ts +26 -0
- package/dist/diagnostics.js +344 -0
- package/dist/docs/check.d.ts +16 -0
- package/dist/docs/check.js +197 -0
- package/dist/docs/decision-record.d.ts +34 -0
- package/dist/docs/decision-record.js +135 -0
- package/dist/docs/decisions.d.ts +16 -0
- package/dist/docs/decisions.js +103 -0
- package/dist/docs/markdown.d.ts +36 -0
- package/dist/docs/markdown.js +88 -0
- package/dist/docs/references.d.ts +32 -0
- package/dist/docs/references.js +148 -0
- package/dist/emit.d.ts +22 -0
- package/dist/emit.js +43 -0
- package/dist/entrypoint-kinds.d.ts +3 -0
- package/dist/entrypoint-kinds.js +11 -0
- package/dist/environment/application/confirm.d.ts +35 -0
- package/dist/environment/application/confirm.js +122 -0
- package/dist/environment/application/destructive.d.ts +6 -0
- package/dist/environment/application/destructive.js +429 -0
- package/dist/environment/application/doctor.d.ts +94 -0
- package/dist/environment/application/doctor.js +247 -0
- package/dist/environment/application/guard.d.ts +49 -0
- package/dist/environment/application/guard.js +677 -0
- package/dist/environment/application/health.d.ts +143 -0
- package/dist/environment/application/health.js +385 -0
- package/dist/environment/application/lifecycle-context.d.ts +235 -0
- package/dist/environment/application/lifecycle-context.js +154 -0
- package/dist/environment/application/lifecycle-state.d.ts +25 -0
- package/dist/environment/application/lifecycle-state.js +202 -0
- package/dist/environment/application/lifecycle.d.ts +18 -0
- package/dist/environment/application/lifecycle.js +737 -0
- package/dist/environment/application/logs.d.ts +45 -0
- package/dist/environment/application/logs.js +127 -0
- package/dist/environment/application/services.d.ts +60 -0
- package/dist/environment/application/services.js +89 -0
- package/dist/environment/application/setup.d.ts +129 -0
- package/dist/environment/application/setup.js +741 -0
- package/dist/environment/application/shared.d.ts +11 -0
- package/dist/environment/application/shared.js +61 -0
- package/dist/environment/application/siblings.d.ts +43 -0
- package/dist/environment/application/siblings.js +269 -0
- package/dist/environment/claims.d.ts +389 -0
- package/dist/environment/claims.js +1701 -0
- package/dist/environment/codex-hook-trust.d.ts +11 -0
- package/dist/environment/codex-hook-trust.js +185 -0
- package/dist/environment/compose-environment.d.ts +1 -0
- package/dist/environment/compose-environment.js +6 -0
- package/dist/environment/compose-ownership.d.ts +21 -0
- package/dist/environment/compose-ownership.js +117 -0
- package/dist/environment/compose-topology.d.ts +125 -0
- package/dist/environment/compose-topology.js +490 -0
- package/dist/environment/contract.d.ts +522 -0
- package/dist/environment/contract.js +794 -0
- package/dist/environment/declaration.d.ts +14 -0
- package/dist/environment/declaration.js +39 -0
- package/dist/environment/destructive-composition.d.ts +29 -0
- package/dist/environment/destructive-composition.js +333 -0
- package/dist/environment/docker-daemon.d.ts +30 -0
- package/dist/environment/docker-daemon.js +78 -0
- package/dist/environment/docker-endpoint.d.ts +10 -0
- package/dist/environment/docker-endpoint.js +19 -0
- package/dist/environment/env-schema-observation.d.ts +46 -0
- package/dist/environment/env-schema-observation.js +241 -0
- package/dist/environment/evidence.d.ts +136 -0
- package/dist/environment/evidence.js +437 -0
- package/dist/environment/executable.d.ts +38 -0
- package/dist/environment/executable.js +190 -0
- package/dist/environment/generated-application/authorities.d.ts +5 -0
- package/dist/environment/generated-application/authorities.js +61 -0
- package/dist/environment/generated-application/bootstrap-skill.d.ts +16 -0
- package/dist/environment/generated-application/bootstrap-skill.js +328 -0
- package/dist/environment/generated-application/check.d.ts +13 -0
- package/dist/environment/generated-application/check.js +191 -0
- package/dist/environment/generated-application/custom-surface.d.ts +26 -0
- package/dist/environment/generated-application/custom-surface.js +289 -0
- package/dist/environment/generated-application/prepare.d.ts +29 -0
- package/dist/environment/generated-application/prepare.js +278 -0
- package/dist/environment/generated-application/publication.d.ts +33 -0
- package/dist/environment/generated-application/publication.js +547 -0
- package/dist/environment/generated-application/shared.d.ts +492 -0
- package/dist/environment/generated-application/shared.js +354 -0
- package/dist/environment/generated-application/surface-observation.d.ts +4 -0
- package/dist/environment/generated-application/surface-observation.js +69 -0
- package/dist/environment/generated-application/surface-outputs.d.ts +71 -0
- package/dist/environment/generated-application/surface-outputs.js +400 -0
- package/dist/environment/generated-application/surfaces.d.ts +3 -0
- package/dist/environment/generated-application/surfaces.js +704 -0
- package/dist/environment/generated-application/sync.d.ts +6 -0
- package/dist/environment/generated-application/sync.js +124 -0
- package/dist/environment/git-observation.d.ts +202 -0
- package/dist/environment/git-observation.js +553 -0
- package/dist/environment/health-composition.d.ts +12 -0
- package/dist/environment/health-composition.js +645 -0
- package/dist/environment/host-observation.d.ts +56 -0
- package/dist/environment/host-observation.js +52 -0
- package/dist/environment/identity.d.ts +48 -0
- package/dist/environment/identity.js +85 -0
- package/dist/environment/init-application.d.ts +333 -0
- package/dist/environment/init-application.js +726 -0
- package/dist/environment/install-policy-observation.d.ts +71 -0
- package/dist/environment/install-policy-observation.js +284 -0
- package/dist/environment/install-policy.d.ts +48 -0
- package/dist/environment/install-policy.js +96 -0
- package/dist/environment/instance.d.ts +33 -0
- package/dist/environment/instance.js +122 -0
- package/dist/environment/instruction-pointer.d.ts +19 -0
- package/dist/environment/instruction-pointer.js +144 -0
- package/dist/environment/lifecycle-composition.d.ts +31 -0
- package/dist/environment/lifecycle-composition.js +742 -0
- package/dist/environment/lifecycle-stage.d.ts +120 -0
- package/dist/environment/lifecycle-stage.js +408 -0
- package/dist/environment/lifecycle.d.ts +75 -0
- package/dist/environment/lifecycle.js +354 -0
- package/dist/environment/logs.d.ts +132 -0
- package/dist/environment/logs.js +519 -0
- package/dist/environment/map-limit.d.ts +2 -0
- package/dist/environment/map-limit.js +13 -0
- package/dist/environment/mcp-observation.d.ts +9 -0
- package/dist/environment/mcp-observation.js +46 -0
- package/dist/environment/observations.d.ts +46 -0
- package/dist/environment/observations.js +346 -0
- package/dist/environment/operator-application.d.ts +79 -0
- package/dist/environment/operator-application.js +197 -0
- package/dist/environment/ports.d.ts +182 -0
- package/dist/environment/ports.js +1205 -0
- package/dist/environment/posture-observation.d.ts +68 -0
- package/dist/environment/posture-observation.js +253 -0
- package/dist/environment/presence.d.ts +4 -0
- package/dist/environment/presence.js +26 -0
- package/dist/environment/read-composition.d.ts +32 -0
- package/dist/environment/read-composition.js +397 -0
- package/dist/environment/readiness-composition.d.ts +57 -0
- package/dist/environment/readiness-composition.js +151 -0
- package/dist/environment/reclaim.d.ts +370 -0
- package/dist/environment/reclaim.js +1940 -0
- package/dist/environment/repository-identity.d.ts +13 -0
- package/dist/environment/repository-identity.js +108 -0
- package/dist/environment/runtime-measurement-record.d.ts +38 -0
- package/dist/environment/runtime-measurement-record.js +111 -0
- package/dist/environment/runtime-measurement.d.ts +27 -0
- package/dist/environment/runtime-measurement.js +224 -0
- package/dist/environment/runtime-probe-residue.d.ts +43 -0
- package/dist/environment/runtime-probe-residue.js +148 -0
- package/dist/environment/runtime-probe.d.ts +72 -0
- package/dist/environment/runtime-probe.js +725 -0
- package/dist/environment/runtime.d.ts +551 -0
- package/dist/environment/runtime.js +2405 -0
- package/dist/environment/seed.d.ts +16 -0
- package/dist/environment/seed.js +43 -0
- package/dist/environment/selftest-application.d.ts +86 -0
- package/dist/environment/selftest-application.js +713 -0
- package/dist/environment/sensitive.d.ts +3 -0
- package/dist/environment/sensitive.js +48 -0
- package/dist/environment/served-checkout-grants.d.ts +53 -0
- package/dist/environment/served-checkout-grants.js +182 -0
- package/dist/environment/setup-observation.d.ts +62 -0
- package/dist/environment/setup-observation.js +1473 -0
- package/dist/environment/setup.d.ts +43 -0
- package/dist/environment/setup.js +176 -0
- package/dist/environment/sibling-checkouts.d.ts +119 -0
- package/dist/environment/sibling-checkouts.js +254 -0
- package/dist/environment/sibling-instructions.d.ts +33 -0
- package/dist/environment/sibling-instructions.js +224 -0
- package/dist/environment/sibling-observation.d.ts +27 -0
- package/dist/environment/sibling-observation.js +45 -0
- package/dist/environment/sibling-pull.d.ts +23 -0
- package/dist/environment/sibling-pull.js +95 -0
- package/dist/environment/sibling-registration.d.ts +45 -0
- package/dist/environment/sibling-registration.js +538 -0
- package/dist/environment/stack-inputs.d.ts +18 -0
- package/dist/environment/stack-inputs.js +210 -0
- package/dist/environment/stack-inventory-prune.d.ts +31 -0
- package/dist/environment/stack-inventory-prune.js +129 -0
- package/dist/environment/stack-inventory-report.d.ts +44 -0
- package/dist/environment/stack-inventory-report.js +78 -0
- package/dist/environment/stack-inventory.d.ts +94 -0
- package/dist/environment/stack-inventory.js +418 -0
- package/dist/environment/state.d.ts +410 -0
- package/dist/environment/state.js +2153 -0
- package/dist/environment/templates.d.ts +140 -0
- package/dist/environment/templates.js +685 -0
- package/dist/environment/tool-registration.d.ts +9 -0
- package/dist/environment/tool-registration.js +92 -0
- package/dist/environment/upgrade-application.d.ts +19 -0
- package/dist/environment/upgrade-application.js +175 -0
- package/dist/environment-exec/execute.d.ts +105 -0
- package/dist/environment-exec/execute.js +1179 -0
- package/dist/environment-exec/libexec-program.d.ts +6 -0
- package/dist/environment-exec/libexec-program.js +22 -0
- package/dist/environment-exec/verified-digest.d.ts +4 -0
- package/dist/environment-exec/verified-digest.js +16 -0
- package/dist/exact-keys.d.ts +7 -0
- package/dist/exact-keys.js +15 -0
- package/dist/fix-catalogue.d.ts +32 -0
- package/dist/fix-catalogue.js +60 -0
- package/dist/freshness.d.ts +29 -0
- package/dist/freshness.js +75 -0
- package/dist/generated-environment/adopt.d.ts +16 -0
- package/dist/generated-environment/adopt.js +271 -0
- package/dist/generated-environment/command.d.ts +47 -0
- package/dist/generated-environment/command.js +75 -0
- package/dist/generated-environment/transaction.d.ts +186 -0
- package/dist/generated-environment/transaction.js +1770 -0
- package/dist/grading/evaluate.d.ts +71 -0
- package/dist/grading/evaluate.js +403 -0
- package/dist/grading/grade.d.ts +33 -0
- package/dist/grading/grade.js +57 -0
- package/dist/guards/answer-confirmation.d.ts +41 -0
- package/dist/guards/answer-confirmation.js +109 -0
- package/dist/guards/evaluator.d.ts +130 -0
- package/dist/guards/evaluator.js +1258 -0
- package/dist/guards/harness-adapter.d.ts +17 -0
- package/dist/guards/harness-adapter.js +57 -0
- package/dist/guards/host.d.ts +8 -0
- package/dist/guards/host.js +10 -0
- package/dist/guards/invocation-parser.d.ts +66 -0
- package/dist/guards/invocation-parser.js +595 -0
- package/dist/guards/local-identity.d.ts +44 -0
- package/dist/guards/local-identity.js +129 -0
- package/dist/guards/policy-change.d.ts +43 -0
- package/dist/guards/policy-change.js +159 -0
- package/dist/guards/policy-pin.d.ts +13 -0
- package/dist/guards/policy-pin.js +177 -0
- package/dist/guards/protected-locations.d.ts +62 -0
- package/dist/guards/protected-locations.js +461 -0
- package/dist/guards/read-only.d.ts +37 -0
- package/dist/guards/read-only.js +315 -0
- package/dist/init/command.d.ts +41 -0
- package/dist/init/command.js +330 -0
- package/dist/init/derive.d.ts +53 -0
- package/dist/init/derive.js +819 -0
- package/dist/init/draft.d.ts +140 -0
- package/dist/init/draft.js +1228 -0
- package/dist/init/render.d.ts +47 -0
- package/dist/init/render.js +104 -0
- package/dist/init/scaffold.d.ts +53 -0
- package/dist/init/scaffold.js +121 -0
- package/dist/invocation.d.ts +13 -0
- package/dist/invocation.js +20 -0
- package/dist/package-metadata.d.ts +9 -0
- package/dist/package-metadata.js +19 -0
- package/dist/readiness.d.ts +53 -0
- package/dist/readiness.js +88 -0
- package/dist/references/named.d.ts +24 -0
- package/dist/references/named.js +27 -0
- package/dist/refusal.d.ts +15 -0
- package/dist/refusal.js +7 -0
- package/dist/registry-derivation.d.ts +31 -0
- package/dist/registry-derivation.js +32 -0
- package/dist/registry-resolve/command.d.ts +47 -0
- package/dist/registry-resolve/command.js +226 -0
- package/dist/registry-resolve/registry.d.ts +68 -0
- package/dist/registry-resolve/registry.js +292 -0
- package/dist/registry-resolve/render-shell.d.ts +18 -0
- package/dist/registry-resolve/render-shell.js +36 -0
- package/dist/repair.d.ts +29 -0
- package/dist/repair.js +8 -0
- package/dist/repository-path.d.ts +9 -0
- package/dist/repository-path.js +16 -0
- package/dist/schema-json.d.ts +1 -0
- package/dist/schema-json.js +177 -0
- package/dist/schema.d.ts +1289 -0
- package/dist/schema.js +985 -0
- package/dist/setup/command.d.ts +4 -0
- package/dist/setup/command.js +4 -0
- package/dist/sha256.d.ts +1 -0
- package/dist/sha256.js +4 -0
- package/dist/surfaces/adoption.d.ts +41 -0
- package/dist/surfaces/adoption.js +217 -0
- package/dist/surfaces/consumer-instructions.d.ts +20 -0
- package/dist/surfaces/consumer-instructions.js +43 -0
- package/dist/surfaces/contract.d.ts +42 -0
- package/dist/surfaces/contract.js +169 -0
- package/dist/surfaces/core-declarations.d.ts +24 -0
- package/dist/surfaces/core-declarations.js +79 -0
- package/dist/surfaces/development-markers.d.ts +1 -0
- package/dist/surfaces/development-markers.js +18 -0
- package/dist/surfaces/generator-adapter.d.ts +71 -0
- package/dist/surfaces/generator-adapter.js +809 -0
- package/dist/surfaces/harness-registry.d.ts +260 -0
- package/dist/surfaces/harness-registry.js +146 -0
- package/dist/surfaces/mcp.d.ts +145 -0
- package/dist/surfaces/mcp.js +349 -0
- package/dist/surfaces/merge.d.ts +30 -0
- package/dist/surfaces/merge.js +105 -0
- package/dist/surfaces/ownership.d.ts +190 -0
- package/dist/surfaces/ownership.js +795 -0
- package/dist/surfaces/projection.d.ts +136 -0
- package/dist/surfaces/projection.js +716 -0
- package/dist/surfaces/render.d.ts +79 -0
- package/dist/surfaces/render.js +636 -0
- package/dist/surfaces/restrictions.d.ts +130 -0
- package/dist/surfaces/restrictions.js +447 -0
- package/dist/surfaces/selftest.d.ts +162 -0
- package/dist/surfaces/selftest.js +393 -0
- package/dist/surfaces/skill-contract.d.ts +85 -0
- package/dist/surfaces/skill-contract.js +449 -0
- package/dist/surfaces/sync.d.ts +57 -0
- package/dist/surfaces/sync.js +164 -0
- package/dist/team-metadata.d.ts +41 -0
- package/dist/team-metadata.js +513 -0
- package/dist/testing/gates.d.ts +91 -0
- package/dist/testing/gates.js +182 -0
- package/dist/testing/hooks.d.ts +59 -0
- package/dist/testing/hooks.js +139 -0
- package/dist/testing/test-protection.d.ts +34 -0
- package/dist/testing/test-protection.js +137 -0
- package/dist/testing/verdict.d.ts +29 -0
- package/dist/testing/verdict.js +62 -0
- package/dist/testing/weakening.d.ts +15 -0
- package/dist/testing/weakening.js +30 -0
- package/dist/work-orders/application-closure.d.ts +7 -0
- package/dist/work-orders/application-closure.js +76 -0
- package/dist/work-orders/application-context.d.ts +162 -0
- package/dist/work-orders/application-context.js +137 -0
- package/dist/work-orders/application-decisions.d.ts +27 -0
- package/dist/work-orders/application-decisions.js +121 -0
- package/dist/work-orders/application-grading.d.ts +11 -0
- package/dist/work-orders/application-grading.js +212 -0
- package/dist/work-orders/application.d.ts +6 -0
- package/dist/work-orders/application.js +223 -0
- package/dist/work-orders/composition.d.ts +101 -0
- package/dist/work-orders/composition.js +1202 -0
- package/dist/work-orders/contract.d.ts +77 -0
- package/dist/work-orders/contract.js +140 -0
- package/dist/work-orders/decisions.d.ts +23 -0
- package/dist/work-orders/decisions.js +55 -0
- package/dist/work-orders/store.d.ts +207 -0
- package/dist/work-orders/store.js +291 -0
- package/docs/README.md +68 -0
- package/docs/architecture.md +136 -0
- package/docs/capability-matrix.md +58 -0
- package/docs/cli-reference.md +807 -0
- package/docs/command-list.md +62 -0
- package/docs/compose-patterns.md +99 -0
- package/docs/concepts.md +173 -0
- package/docs/configuration-essentials.md +177 -0
- package/docs/configuration-reference.md +636 -0
- package/docs/docs-check.md +76 -0
- package/docs/environment.md +948 -0
- package/docs/extending.md +43 -0
- package/docs/faq.md +113 -0
- package/docs/getting-started.md +692 -0
- package/docs/harness-surfaces.md +500 -0
- package/docs/joining-a-team.md +76 -0
- package/docs/release-policy.md +49 -0
- package/docs/safety-and-autonomy.md +463 -0
- package/docs/troubleshooting.md +382 -0
- package/docs/tutorial.md +171 -0
- package/docs/work-and-browser.md +420 -0
- package/libexec/browser-operation.cjs +270 -0
- package/libexec/fence-marker.cjs +101 -0
- package/libexec/fence-marker.d.cts +24 -0
- package/libexec/fenced-group-runner.cjs +77 -0
- package/libexec/fenced-supervisor.cjs +137 -0
- package/libexec/generated-filesystem.cjs +731 -0
- package/libexec/install-filesystem.cjs +225 -0
- package/libexec/rulesync-generator.NOTICE.md +1112 -0
- package/libexec/rulesync-generator.cjs +743 -0
- package/libexec/surface-generator-worker.cjs +266 -0
- package/libexec/surface-output-reader.cjs +99 -0
- package/libexec/surface-scratch-cleanup.cjs +14 -0
- package/libexec/validate-harness-evidence.d.mts +183 -0
- package/libexec/validate-harness-evidence.mjs +1312 -0
- package/package.json +74 -4
- package/surfaces/README.md +33 -0
- package/surfaces/agents/evaluator.agent.md +76 -0
- package/surfaces/guards/bash-guard.sh +48 -0
- package/surfaces/guards/claude.adapter.json +25 -0
- package/surfaces/guards/codex.adapter.json +24 -0
- package/surfaces/guards/opencode.adapter.json +25 -0
- package/surfaces/references/browser.md +63 -0
- package/surfaces/references/test-data-and-personas.md +84 -0
- package/surfaces/references/testing-tiers.md +11 -0
- package/surfaces/skills/cockpit-browser.skill.md +59 -0
- package/surfaces/skills/cockpit-explain.skill.md +32 -0
- package/surfaces/skills/cockpit-fix.skill.md +37 -0
- package/surfaces/skills/cockpit-setup.skill.md +39 -0
- package/surfaces/skills/cockpit-work.skill.md +34 -0
- package/surfaces/skills/collection.json +5 -0
- package/surfaces/skills/procedures/explain.md +66 -0
- package/surfaces/skills/procedures/fix.md +95 -0
- package/surfaces/skills/procedures/setup.md +452 -0
- package/surfaces/skills/procedures/work.md +134 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { GeneratedSurfaceSelftestProjection } from '../environment/generated-application/shared.js';
|
|
2
|
+
import { type SurfaceDesiredEntry } from './ownership.js';
|
|
3
|
+
export type SelftestCapability = 'instruction-loading' | 'guard-firing' | 'skill-parity';
|
|
4
|
+
export type BuiltinHarnessRef = Readonly<{
|
|
5
|
+
kind: 'builtin';
|
|
6
|
+
target: 'claudecode';
|
|
7
|
+
name: 'Claude Code';
|
|
8
|
+
}> | Readonly<{
|
|
9
|
+
kind: 'builtin';
|
|
10
|
+
target: 'codexcli';
|
|
11
|
+
name: 'Codex';
|
|
12
|
+
}> | Readonly<{
|
|
13
|
+
kind: 'builtin';
|
|
14
|
+
target: 'opencode';
|
|
15
|
+
name: 'OpenCode';
|
|
16
|
+
}>;
|
|
17
|
+
export interface CustomHarnessRef {
|
|
18
|
+
readonly kind: 'custom';
|
|
19
|
+
readonly id: string;
|
|
20
|
+
readonly declarationSha256: string;
|
|
21
|
+
readonly module: string;
|
|
22
|
+
readonly sourceSha256: string;
|
|
23
|
+
readonly export: string;
|
|
24
|
+
}
|
|
25
|
+
export type SelftestHarnessRef = BuiltinHarnessRef | Readonly<CustomHarnessRef>;
|
|
26
|
+
export type SelftestCellKey = Readonly<{
|
|
27
|
+
harness: SelftestHarnessRef;
|
|
28
|
+
capability: 'instruction-loading' | 'guard-firing';
|
|
29
|
+
}> | Readonly<{
|
|
30
|
+
harness: SelftestHarnessRef;
|
|
31
|
+
capability: 'skill-parity';
|
|
32
|
+
skill: string;
|
|
33
|
+
}>;
|
|
34
|
+
export interface SelftestExpectation {
|
|
35
|
+
readonly schema: 'cockpit-harness-selftest-expectation/v1';
|
|
36
|
+
readonly generatedManifestSha256: string;
|
|
37
|
+
readonly cells: readonly SelftestCellKey[];
|
|
38
|
+
readonly shippedSkills: readonly string[];
|
|
39
|
+
readonly reviewHarnesses: readonly [
|
|
40
|
+
Readonly<{
|
|
41
|
+
name: 'Claude Code';
|
|
42
|
+
nativeDiagnostics: false;
|
|
43
|
+
}>,
|
|
44
|
+
Readonly<{
|
|
45
|
+
name: 'Codex';
|
|
46
|
+
nativeDiagnostics: true;
|
|
47
|
+
}>
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A probe row exists only for a built-in tool. cockpit runs a probe through an executable it found
|
|
52
|
+
* on PATH and the person approved by digest; a custom harness has no such executable, only a module
|
|
53
|
+
* cockpit loads to render files, and running a worker command cockpit built around that module would
|
|
54
|
+
* be cockpit vouching for itself. So a custom cell never gets a row: selftest records it
|
|
55
|
+
* human_needed, and a row written for one is refused rather than accepted and ignored.
|
|
56
|
+
*/
|
|
57
|
+
export interface SelftestProbeSpecification {
|
|
58
|
+
readonly harness: BuiltinHarnessRef;
|
|
59
|
+
readonly capability: SelftestCapability;
|
|
60
|
+
readonly invocation: readonly [string, ...string[]];
|
|
61
|
+
readonly versionCommand: readonly [string, ...string[]];
|
|
62
|
+
readonly helpCommand: readonly [string, ...string[]];
|
|
63
|
+
readonly candidateCode: string;
|
|
64
|
+
readonly brokenCode: string;
|
|
65
|
+
readonly unavailableCode: string;
|
|
66
|
+
readonly sample: Readonly<{
|
|
67
|
+
skill: string;
|
|
68
|
+
features: readonly ('name' | 'trigger' | 'body-load' | 'tools' | 'non-goals')[];
|
|
69
|
+
}> | null;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A specification row as cockpit can write it. The fields left null are the probe itself: the
|
|
73
|
+
* arguments that make a tool load a control file and the lines it prints for an accepted and a
|
|
74
|
+
* rejected control. Nothing in the generated manifest says what those are for a given tool and
|
|
75
|
+
* version, so a person supplies them and approves the whole file before anything in it runs.
|
|
76
|
+
*/
|
|
77
|
+
export type SelftestProbeSpecificationDraft = Omit<SelftestProbeSpecification, 'invocation' | 'candidateCode' | 'brokenCode' | 'sample'> & Readonly<{
|
|
78
|
+
invocation: null;
|
|
79
|
+
candidateCode: null;
|
|
80
|
+
brokenCode: null;
|
|
81
|
+
sample: Readonly<{
|
|
82
|
+
skill: string;
|
|
83
|
+
features: null;
|
|
84
|
+
}> | null;
|
|
85
|
+
}>;
|
|
86
|
+
export interface PlannedSelftestProbe extends SelftestProbeSpecification {
|
|
87
|
+
readonly cell: SelftestCellKey;
|
|
88
|
+
readonly entries: readonly SurfaceDesiredEntry[];
|
|
89
|
+
}
|
|
90
|
+
export type SurfaceSelftestPlanResult = Readonly<{
|
|
91
|
+
kind: 'complete';
|
|
92
|
+
expectation: SelftestExpectation;
|
|
93
|
+
probes: readonly PlannedSelftestProbe[];
|
|
94
|
+
/** Custom cells: no probe can run for them, so each is recorded human_needed. */
|
|
95
|
+
unprobeable: readonly SelftestCellKey[];
|
|
96
|
+
}> | Readonly<{
|
|
97
|
+
kind: 'refusal';
|
|
98
|
+
code: 'SELFTEST_SOURCE_INVALID';
|
|
99
|
+
reason: string;
|
|
100
|
+
}>;
|
|
101
|
+
export type SelftestHumanNeededRecord = Readonly<{
|
|
102
|
+
kind: 'human_needed';
|
|
103
|
+
harness: SelftestHarnessRef;
|
|
104
|
+
capability: 'instruction-loading' | 'guard-firing';
|
|
105
|
+
generatedManifestSha256: string;
|
|
106
|
+
observedAt: string;
|
|
107
|
+
reason: 'executable_missing' | 'authentication_required' | 'isolation_unavailable' | 'enforcement_probe_required';
|
|
108
|
+
operatorAction: string;
|
|
109
|
+
}> | Readonly<{
|
|
110
|
+
kind: 'human_needed';
|
|
111
|
+
harness: SelftestHarnessRef;
|
|
112
|
+
capability: 'skill-parity';
|
|
113
|
+
skill: string;
|
|
114
|
+
generatedManifestSha256: string;
|
|
115
|
+
observedAt: string;
|
|
116
|
+
reason: 'executable_missing' | 'authentication_required' | 'isolation_unavailable' | 'enforcement_probe_required';
|
|
117
|
+
operatorAction: string;
|
|
118
|
+
}>;
|
|
119
|
+
export interface SelftestSkillReviewRecord {
|
|
120
|
+
readonly skill: string;
|
|
121
|
+
readonly harness: 'Claude Code' | 'Codex';
|
|
122
|
+
readonly status: 'evidenced' | 'human_needed';
|
|
123
|
+
readonly reviewedOn: string | null;
|
|
124
|
+
readonly evidenceRunId: string | null;
|
|
125
|
+
readonly reviewers: readonly ('official-authoring' | 'harness-diagnostics')[];
|
|
126
|
+
readonly checks: Readonly<{
|
|
127
|
+
trigger: 'pass';
|
|
128
|
+
silence: 'pass';
|
|
129
|
+
bodyLoad: 'pass';
|
|
130
|
+
tools: 'pass';
|
|
131
|
+
nonGoals: 'pass';
|
|
132
|
+
}> | null;
|
|
133
|
+
readonly operatorAction: string | null;
|
|
134
|
+
}
|
|
135
|
+
export declare function surfaceSelftestExpectation(projection: GeneratedSurfaceSelftestProjection): SelftestExpectation | undefined;
|
|
136
|
+
/** `<harness> <capability>[ <skill>]`, the name a person sees for a cell. */
|
|
137
|
+
export declare function surfaceSelftestCellLabel(cell: SelftestCellKey): string;
|
|
138
|
+
/**
|
|
139
|
+
* One row per built-in cell, with everything the generated manifest determines filled in and the
|
|
140
|
+
* probe itself left null. The version and help commands are the identity probes every built-in tool
|
|
141
|
+
* answers with `--version` and `--help`; they run before the controls and are shown for approval.
|
|
142
|
+
* Custom cells get no row (see SelftestProbeSpecification) and are returned as `unprobeable`.
|
|
143
|
+
*
|
|
144
|
+
* A row from `previous` (an earlier file, usually stale after a config change) is kept as it is when
|
|
145
|
+
* it is a complete, valid row for a cell that still exists, so the probes a person wrote survive the
|
|
146
|
+
* rewrite; a row for a changed or removed cell, or one still partly null, is not carried.
|
|
147
|
+
*/
|
|
148
|
+
export declare function draftSurfaceSelftestSpecifications(projection: GeneratedSurfaceSelftestProjection, previous?: readonly unknown[]): Readonly<{
|
|
149
|
+
specifications: readonly (SelftestProbeSpecificationDraft | SelftestProbeSpecification)[];
|
|
150
|
+
unprobeable: readonly SelftestCellKey[];
|
|
151
|
+
carried: number;
|
|
152
|
+
}> | undefined;
|
|
153
|
+
/** Names each row whose person-supplied fields are still null, as `<harness> <capability>[ <skill>]: <fields>`. */
|
|
154
|
+
export declare function unfilledSurfaceSelftestSpecifications(specifications: readonly unknown[]): readonly string[];
|
|
155
|
+
export declare function planSurfaceSelftest(projection: GeneratedSurfaceSelftestProjection, specifications: readonly SelftestProbeSpecification[]): SurfaceSelftestPlanResult;
|
|
156
|
+
export declare function surfaceSelftestHumanNeeded(expectation: SelftestExpectation, cell: SelftestCellKey, reason: SelftestHumanNeededRecord['reason'], observedAt: string, operatorAction: string): SelftestHumanNeededRecord | undefined;
|
|
157
|
+
export declare function surfaceSelftestReviews(expectation: SelftestExpectation, evidenced: readonly Readonly<{
|
|
158
|
+
skill: string;
|
|
159
|
+
harness: 'Claude Code' | 'Codex';
|
|
160
|
+
reviewedOn: string;
|
|
161
|
+
evidenceRunId: string;
|
|
162
|
+
}>[]): readonly SelftestSkillReviewRecord[] | undefined;
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import { sha256Hex } from '../sha256.js';
|
|
2
|
+
import { snapshotGeneratedJson } from '../environment/contract.js';
|
|
3
|
+
import { SURFACE_TARGETS } from './harness-registry.js';
|
|
4
|
+
import { classifySurfaceOwnership } from './ownership.js';
|
|
5
|
+
const SHA256 = /^[a-f0-9]{64}$/;
|
|
6
|
+
const CODE = /^[A-Z][A-Z0-9_]{2,127}$/;
|
|
7
|
+
const CUSTOM_ID = /^[a-z0-9][a-z0-9-]{0,127}$/;
|
|
8
|
+
const SKILL_NAME = /^[a-z0-9][a-z0-9-]{0,127}$/;
|
|
9
|
+
const SAMPLE_FEATURES = new Set(['name', 'trigger', 'body-load', 'tools', 'non-goals']);
|
|
10
|
+
const CAPABILITIES = Object.freeze(['instruction-loading', 'guard-firing', 'skill-parity']);
|
|
11
|
+
const BUILTINS = Object.freeze({
|
|
12
|
+
claudecode: Object.freeze({ kind: 'builtin', target: 'claudecode', name: 'Claude Code' }),
|
|
13
|
+
codexcli: Object.freeze({ kind: 'builtin', target: 'codexcli', name: 'Codex' }),
|
|
14
|
+
opencode: Object.freeze({ kind: 'builtin', target: 'opencode', name: 'OpenCode' }),
|
|
15
|
+
});
|
|
16
|
+
const FEATURE_CAPABILITY = Object.freeze({
|
|
17
|
+
rules: 'instruction-loading',
|
|
18
|
+
hooks: 'guard-firing',
|
|
19
|
+
permissions: 'guard-firing',
|
|
20
|
+
skills: 'skill-parity',
|
|
21
|
+
});
|
|
22
|
+
function compareCodeUnits(left, right) {
|
|
23
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
24
|
+
}
|
|
25
|
+
function cellKey(cell) {
|
|
26
|
+
const harness = cell.harness.kind === 'builtin' ? cell.harness.target : cell.harness.id;
|
|
27
|
+
return `${cell.harness.kind}\0${harness}\0${cell.capability}\0${cell.capability === 'skill-parity' ? cell.skill : ''}`;
|
|
28
|
+
}
|
|
29
|
+
function declarationDigest(declaration) {
|
|
30
|
+
const captured = snapshotGeneratedJson(declaration);
|
|
31
|
+
return captured === undefined ? undefined : sha256Hex(JSON.stringify(captured));
|
|
32
|
+
}
|
|
33
|
+
function customKey(harness, feature) {
|
|
34
|
+
return `${harness}\0${feature}`;
|
|
35
|
+
}
|
|
36
|
+
function customRef(declaration, capability, input) {
|
|
37
|
+
const declarationSha256 = declarationDigest(declaration);
|
|
38
|
+
if (!declarationSha256 || !CUSTOM_ID.test(declaration.name) || SURFACE_TARGETS.includes(declaration.name)
|
|
39
|
+
|| capability.state === 'unavailable'
|
|
40
|
+
|| capability.mechanism.module !== input.module || capability.mechanism.export !== input.export
|
|
41
|
+
|| capability.measured_on !== input.measuredOn || capability.state !== input.state
|
|
42
|
+
|| !SHA256.test(input.sourceSha256) || !SHA256.test(input.resultSha256))
|
|
43
|
+
return undefined;
|
|
44
|
+
return {
|
|
45
|
+
kind: 'custom', id: declaration.name, declarationSha256,
|
|
46
|
+
module: input.module, sourceSha256: input.sourceSha256, export: input.export,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function shippedSkills(entries) {
|
|
50
|
+
const names = new Set();
|
|
51
|
+
for (const entry of entries) {
|
|
52
|
+
const match = /^(?:\.claude|\.opencode|\.agents)\/skills\/([^/]+)\/(?:SKILL\.md|skill\.md)$/.exec(entry.path);
|
|
53
|
+
if (!match)
|
|
54
|
+
continue;
|
|
55
|
+
if (!SKILL_NAME.test(match[1]))
|
|
56
|
+
return undefined;
|
|
57
|
+
names.add(match[1]);
|
|
58
|
+
}
|
|
59
|
+
return [...names].sort(compareCodeUnits);
|
|
60
|
+
}
|
|
61
|
+
export function surfaceSelftestExpectation(projection) {
|
|
62
|
+
try {
|
|
63
|
+
const captured = snapshotGeneratedJson(projection);
|
|
64
|
+
if (!captured || captured.schema !== 'cockpit-generated-surface-selftest/v1' || !SHA256.test(captured.manifestSha256))
|
|
65
|
+
return undefined;
|
|
66
|
+
const payload = {
|
|
67
|
+
schema: captured.schema,
|
|
68
|
+
configuredHarnesses: captured.configuredHarnesses,
|
|
69
|
+
declarations: captured.declarations,
|
|
70
|
+
entries: captured.entries,
|
|
71
|
+
custom: captured.custom,
|
|
72
|
+
};
|
|
73
|
+
if (sha256Hex(JSON.stringify(payload)) !== captured.manifestSha256)
|
|
74
|
+
return undefined;
|
|
75
|
+
const ownership = classifySurfaceOwnership({ configuredHarnesses: captured.configuredHarnesses, entries: captured.entries });
|
|
76
|
+
if (ownership.kind !== 'complete')
|
|
77
|
+
return undefined;
|
|
78
|
+
const configured = new Set(captured.configuredHarnesses);
|
|
79
|
+
if (configured.size !== captured.configuredHarnesses.length
|
|
80
|
+
|| captured.configuredHarnesses.length > SURFACE_TARGETS.length + 64
|
|
81
|
+
|| [...configured].some((name) => !SURFACE_TARGETS.includes(name)
|
|
82
|
+
&& !captured.declarations.some((row) => row.name === name)))
|
|
83
|
+
return undefined;
|
|
84
|
+
if (captured.declarations.length > 64 || new Set(captured.declarations.map((row) => row.name)).size !== captured.declarations.length
|
|
85
|
+
|| captured.declarations.some((row) => !configured.has(row.name)))
|
|
86
|
+
return undefined;
|
|
87
|
+
const inputs = new Map();
|
|
88
|
+
for (const input of captured.custom) {
|
|
89
|
+
const key = customKey(input.harness, input.feature);
|
|
90
|
+
if (inputs.has(key) || !configured.has(input.harness))
|
|
91
|
+
return undefined;
|
|
92
|
+
inputs.set(key, input);
|
|
93
|
+
}
|
|
94
|
+
const skills = shippedSkills(ownership.entries);
|
|
95
|
+
if (!skills || skills.length === 0 || skills.length > 256)
|
|
96
|
+
return undefined;
|
|
97
|
+
const cells = [];
|
|
98
|
+
const usedInputs = new Set();
|
|
99
|
+
const customCells = new Map();
|
|
100
|
+
for (const target of Object.keys(BUILTINS).sort(compareCodeUnits)) {
|
|
101
|
+
if (!configured.has(target))
|
|
102
|
+
continue;
|
|
103
|
+
for (const capability of CAPABILITIES) {
|
|
104
|
+
if (capability === 'skill-parity') {
|
|
105
|
+
for (const skill of skills)
|
|
106
|
+
cells.push({ harness: BUILTINS[target], capability, skill });
|
|
107
|
+
}
|
|
108
|
+
else
|
|
109
|
+
cells.push({ harness: BUILTINS[target], capability });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const declaration of captured.declarations) {
|
|
113
|
+
for (const capability of declaration.capabilities) {
|
|
114
|
+
const evidenceCapability = FEATURE_CAPABILITY[capability.feature];
|
|
115
|
+
const inputKey = customKey(declaration.name, capability.feature);
|
|
116
|
+
const input = inputs.get(inputKey);
|
|
117
|
+
if (capability.state === 'unavailable') {
|
|
118
|
+
if (input !== undefined)
|
|
119
|
+
return undefined;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (!input)
|
|
123
|
+
return undefined;
|
|
124
|
+
usedInputs.add(inputKey);
|
|
125
|
+
if (!evidenceCapability)
|
|
126
|
+
continue;
|
|
127
|
+
const harness = customRef(declaration, capability, input);
|
|
128
|
+
if (!harness)
|
|
129
|
+
return undefined;
|
|
130
|
+
const capabilitySkills = evidenceCapability === 'skill-parity' ? skills : [undefined];
|
|
131
|
+
for (const skill of capabilitySkills) {
|
|
132
|
+
const key = `${declaration.name}\0${evidenceCapability}\0${skill ?? ''}`;
|
|
133
|
+
const existing = customCells.get(key);
|
|
134
|
+
if (existing)
|
|
135
|
+
return undefined;
|
|
136
|
+
customCells.set(key, evidenceCapability === 'skill-parity'
|
|
137
|
+
? { harness, capability: evidenceCapability, skill: skill }
|
|
138
|
+
: { harness, capability: evidenceCapability });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
cells.push(...customCells.values());
|
|
143
|
+
if (inputs.size !== captured.custom.length || usedInputs.size !== inputs.size || cells.length === 0 || cells.length > 256)
|
|
144
|
+
return undefined;
|
|
145
|
+
const uniqueCells = new Map(cells.map((cell) => [cellKey(cell), cell]));
|
|
146
|
+
if (uniqueCells.size !== cells.length)
|
|
147
|
+
return undefined;
|
|
148
|
+
return {
|
|
149
|
+
schema: 'cockpit-harness-selftest-expectation/v1',
|
|
150
|
+
generatedManifestSha256: captured.manifestSha256,
|
|
151
|
+
cells: [...uniqueCells.values()].sort((left, right) => compareCodeUnits(cellKey(left), cellKey(right))),
|
|
152
|
+
shippedSkills: skills,
|
|
153
|
+
reviewHarnesses: [
|
|
154
|
+
{ name: 'Claude Code', nativeDiagnostics: false },
|
|
155
|
+
{ name: 'Codex', nativeDiagnostics: true },
|
|
156
|
+
],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch { /* a guard over caller-supplied values; a throwing accessor or proxy trap is malformed input the caller refuses. */
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function sameHarness(left, right) {
|
|
164
|
+
return left.kind === right.kind && (left.kind === 'builtin'
|
|
165
|
+
? right.kind === 'builtin' && left.target === right.target && left.name === right.name
|
|
166
|
+
: right.kind === 'custom' && left.id === right.id && left.declarationSha256 === right.declarationSha256
|
|
167
|
+
&& left.module === right.module && left.sourceSha256 === right.sourceSha256 && left.export === right.export);
|
|
168
|
+
}
|
|
169
|
+
function specificationCellKey(value) {
|
|
170
|
+
if (value === null || typeof value !== 'object')
|
|
171
|
+
return undefined;
|
|
172
|
+
const row = value;
|
|
173
|
+
if (row.harness === null || typeof row.harness !== 'object' || typeof row.capability !== 'string')
|
|
174
|
+
return undefined;
|
|
175
|
+
try {
|
|
176
|
+
if (row.capability === 'skill-parity') {
|
|
177
|
+
const sample = row.sample;
|
|
178
|
+
if (!sample || typeof sample.skill !== 'string')
|
|
179
|
+
return undefined;
|
|
180
|
+
return cellKey({ harness: row.harness, capability: 'skill-parity', skill: sample.skill });
|
|
181
|
+
}
|
|
182
|
+
if (row.capability !== 'instruction-loading' && row.capability !== 'guard-firing')
|
|
183
|
+
return undefined;
|
|
184
|
+
return cellKey({ harness: row.harness, capability: row.capability });
|
|
185
|
+
}
|
|
186
|
+
catch { /* a guard over caller-supplied values; a throwing accessor or proxy trap is malformed input the caller refuses. */
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function validProbe(probe, cell) {
|
|
191
|
+
try {
|
|
192
|
+
if (Object.keys(probe).sort().join('\0') !== [
|
|
193
|
+
'brokenCode', 'candidateCode', 'capability', 'harness', 'helpCommand', 'invocation',
|
|
194
|
+
'sample', 'unavailableCode', 'versionCommand',
|
|
195
|
+
].sort().join('\0'))
|
|
196
|
+
return false;
|
|
197
|
+
if (probe.harness.kind !== 'builtin' || Object.keys(probe.harness).sort().join('\0') !== 'kind\0name\0target')
|
|
198
|
+
return false;
|
|
199
|
+
const commands = [probe.invocation, probe.versionCommand, probe.helpCommand];
|
|
200
|
+
const sample = probe.sample;
|
|
201
|
+
const validSample = sample !== null && Object.keys(sample).sort().join('\0') === 'features\0skill'
|
|
202
|
+
&& SKILL_NAME.test(sample.skill) && sample.features.length > 0 && sample.features.length <= SAMPLE_FEATURES.size
|
|
203
|
+
&& new Set(sample.features).size === sample.features.length && sample.features.every((feature) => SAMPLE_FEATURES.has(feature));
|
|
204
|
+
return sameHarness(probe.harness, cell.harness) && probe.capability === cell.capability
|
|
205
|
+
&& commands.every((command) => Array.isArray(command) && command.length > 0 && command.length <= 128
|
|
206
|
+
&& command.every((value) => typeof value === 'string' && value.length > 0 && Buffer.byteLength(value) <= 65_536 && !value.includes('\0')))
|
|
207
|
+
&& probe.invocation.filter((value) => value === '{control}').length === 1
|
|
208
|
+
&& [probe.candidateCode, probe.brokenCode, probe.unavailableCode].every((code) => CODE.test(code))
|
|
209
|
+
&& probe.unavailableCode === `${probe.capability.replaceAll('-', '_').toUpperCase()}_UNAVAILABLE`
|
|
210
|
+
&& commands.every((command) => command[0] === '{executable}')
|
|
211
|
+
&& (cell.capability === 'skill-parity'
|
|
212
|
+
? validSample && probe.sample.skill === cell.skill
|
|
213
|
+
: probe.sample === null);
|
|
214
|
+
}
|
|
215
|
+
catch { /* a guard over caller-supplied values; a throwing accessor or proxy trap is malformed input the caller refuses. */
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function harnessLabel(harness) {
|
|
220
|
+
return harness.kind === 'builtin' ? harness.name : harness.id;
|
|
221
|
+
}
|
|
222
|
+
/** `<harness> <capability>[ <skill>]`, the name a person sees for a cell. */
|
|
223
|
+
export function surfaceSelftestCellLabel(cell) {
|
|
224
|
+
return `${harnessLabel(cell.harness)} ${cell.capability}${cell.capability === 'skill-parity' ? ` ${cell.skill}` : ''}`;
|
|
225
|
+
}
|
|
226
|
+
function builtinCell(cell) {
|
|
227
|
+
return cell.harness.kind === 'builtin';
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* One row per built-in cell, with everything the generated manifest determines filled in and the
|
|
231
|
+
* probe itself left null. The version and help commands are the identity probes every built-in tool
|
|
232
|
+
* answers with `--version` and `--help`; they run before the controls and are shown for approval.
|
|
233
|
+
* Custom cells get no row (see SelftestProbeSpecification) and are returned as `unprobeable`.
|
|
234
|
+
*
|
|
235
|
+
* A row from `previous` (an earlier file, usually stale after a config change) is kept as it is when
|
|
236
|
+
* it is a complete, valid row for a cell that still exists, so the probes a person wrote survive the
|
|
237
|
+
* rewrite; a row for a changed or removed cell, or one still partly null, is not carried.
|
|
238
|
+
*/
|
|
239
|
+
export function draftSurfaceSelftestSpecifications(projection, previous = []) {
|
|
240
|
+
const expectation = surfaceSelftestExpectation(projection);
|
|
241
|
+
if (!expectation)
|
|
242
|
+
return undefined;
|
|
243
|
+
const earlier = new Map();
|
|
244
|
+
for (const row of previous) {
|
|
245
|
+
const key = specificationCellKey(row);
|
|
246
|
+
const captured = key === undefined ? undefined : snapshotGeneratedJson(row);
|
|
247
|
+
if (key !== undefined && captured !== undefined && !earlier.has(key))
|
|
248
|
+
earlier.set(key, captured);
|
|
249
|
+
}
|
|
250
|
+
let carried = 0;
|
|
251
|
+
const specifications = expectation.cells.filter(builtinCell).map((cell) => {
|
|
252
|
+
const kept = earlier.get(cellKey(cell));
|
|
253
|
+
if (kept && validProbe(kept, cell)) {
|
|
254
|
+
carried += 1;
|
|
255
|
+
return kept;
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
harness: cell.harness,
|
|
259
|
+
capability: cell.capability,
|
|
260
|
+
invocation: null,
|
|
261
|
+
versionCommand: ['{executable}', '--version'],
|
|
262
|
+
helpCommand: ['{executable}', '--help'],
|
|
263
|
+
candidateCode: null,
|
|
264
|
+
brokenCode: null,
|
|
265
|
+
unavailableCode: `${cell.capability.replaceAll('-', '_').toUpperCase()}_UNAVAILABLE`,
|
|
266
|
+
sample: cell.capability === 'skill-parity' ? { skill: cell.skill, features: null } : null,
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
return {
|
|
270
|
+
specifications,
|
|
271
|
+
unprobeable: expectation.cells.filter((cell) => !builtinCell(cell)),
|
|
272
|
+
carried,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
/** Names each row whose person-supplied fields are still null, as `<harness> <capability>[ <skill>]: <fields>`. */
|
|
276
|
+
export function unfilledSurfaceSelftestSpecifications(specifications) {
|
|
277
|
+
const rows = [];
|
|
278
|
+
for (const value of specifications) {
|
|
279
|
+
if (value === null || typeof value !== 'object')
|
|
280
|
+
continue;
|
|
281
|
+
const row = value;
|
|
282
|
+
const fields = ['invocation', 'candidateCode', 'brokenCode'].filter((field) => row[field] === null);
|
|
283
|
+
const sample = row.sample;
|
|
284
|
+
if (sample !== null && typeof sample === 'object' && sample.features === null)
|
|
285
|
+
fields.push('sample.features');
|
|
286
|
+
if (fields.length === 0)
|
|
287
|
+
continue;
|
|
288
|
+
const harness = row.harness;
|
|
289
|
+
const label = harness !== null && typeof harness === 'object' ? harnessLabel(harness) : 'unknown harness';
|
|
290
|
+
const skill = sample !== null && typeof sample === 'object' && typeof sample.skill === 'string' ? ` ${sample.skill}` : '';
|
|
291
|
+
rows.push(`${label} ${String(row.capability)}${skill}: ${fields.join(', ')}`);
|
|
292
|
+
}
|
|
293
|
+
return rows;
|
|
294
|
+
}
|
|
295
|
+
export function planSurfaceSelftest(projection, specifications) {
|
|
296
|
+
const capturedProjection = snapshotGeneratedJson(projection);
|
|
297
|
+
const expectation = capturedProjection ? surfaceSelftestExpectation(capturedProjection) : undefined;
|
|
298
|
+
if (!expectation)
|
|
299
|
+
return { kind: 'refusal', code: 'SELFTEST_SOURCE_INVALID', reason: 'generated selftest projection is invalid' };
|
|
300
|
+
const capturedSpecifications = snapshotGeneratedJson(specifications);
|
|
301
|
+
if (!capturedSpecifications)
|
|
302
|
+
return { kind: 'refusal', code: 'SELFTEST_SOURCE_INVALID', reason: 'probe specifications are malformed' };
|
|
303
|
+
const customRows = capturedSpecifications.flatMap((row) => {
|
|
304
|
+
const harness = row?.harness;
|
|
305
|
+
return harness !== null && typeof harness === 'object' && harness.kind === 'custom'
|
|
306
|
+
? [`${typeof harness.id === 'string' ? harness.id : 'unknown harness'} ${String(row.capability)}`] : [];
|
|
307
|
+
});
|
|
308
|
+
if (customRows.length > 0)
|
|
309
|
+
return {
|
|
310
|
+
kind: 'refusal', code: 'SELFTEST_SOURCE_INVALID',
|
|
311
|
+
reason: `probe specification rows for custom harnesses cannot run and would be ignored, so they are refused: ${customRows.slice(0, 5).join('; ')}${customRows.length > 5 ? `; and ${customRows.length - 5} more` : ''}; selftest records custom cells human_needed without a row, so delete these rows`,
|
|
312
|
+
};
|
|
313
|
+
const probeCells = expectation.cells.filter(builtinCell);
|
|
314
|
+
if (capturedSpecifications.length !== probeCells.length || capturedSpecifications.length > 256) {
|
|
315
|
+
return { kind: 'refusal', code: 'SELFTEST_SOURCE_INVALID', reason: 'probe specifications do not exactly cover expected cells' };
|
|
316
|
+
}
|
|
317
|
+
const byCell = new Map();
|
|
318
|
+
for (const specification of capturedSpecifications) {
|
|
319
|
+
const key = specificationCellKey(specification);
|
|
320
|
+
if (!key)
|
|
321
|
+
return { kind: 'refusal', code: 'SELFTEST_SOURCE_INVALID', reason: 'probe specification is malformed' };
|
|
322
|
+
if (byCell.has(key))
|
|
323
|
+
return { kind: 'refusal', code: 'SELFTEST_SOURCE_INVALID', reason: 'probe specifications contain duplicate cells' };
|
|
324
|
+
byCell.set(key, specification);
|
|
325
|
+
}
|
|
326
|
+
const probes = [];
|
|
327
|
+
for (const cell of probeCells) {
|
|
328
|
+
const specification = byCell.get(cellKey(cell));
|
|
329
|
+
if (!specification || !validProbe(specification, cell)) {
|
|
330
|
+
return { kind: 'refusal', code: 'SELFTEST_SOURCE_INVALID', reason: 'probe specification is malformed or does not match source authority' };
|
|
331
|
+
}
|
|
332
|
+
if (specification.sample && !expectation.shippedSkills.includes(specification.sample.skill)) {
|
|
333
|
+
return { kind: 'refusal', code: 'SELFTEST_SOURCE_INVALID', reason: 'skill parity sample is not a shipped skill' };
|
|
334
|
+
}
|
|
335
|
+
probes.push({
|
|
336
|
+
...specification,
|
|
337
|
+
cell,
|
|
338
|
+
entries: capturedProjection.entries.filter((entry) => entry.harnesses.includes(cell.harness.target)),
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
kind: 'complete', expectation, probes,
|
|
343
|
+
unprobeable: expectation.cells.filter((cell) => !builtinCell(cell)),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
export function surfaceSelftestHumanNeeded(expectation, cell, reason, observedAt, operatorAction) {
|
|
347
|
+
if (!expectation.cells.some((candidate) => cellKey(candidate) === cellKey(cell))
|
|
348
|
+
|| !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(observedAt)
|
|
349
|
+
|| operatorAction.length === 0 || Buffer.byteLength(operatorAction) > 4096 || operatorAction.includes('\0'))
|
|
350
|
+
return undefined;
|
|
351
|
+
if (cell.capability === 'skill-parity')
|
|
352
|
+
return {
|
|
353
|
+
kind: 'human_needed', harness: cell.harness, capability: cell.capability, skill: cell.skill,
|
|
354
|
+
generatedManifestSha256: expectation.generatedManifestSha256, observedAt, reason, operatorAction,
|
|
355
|
+
};
|
|
356
|
+
return {
|
|
357
|
+
kind: 'human_needed', harness: cell.harness, capability: cell.capability,
|
|
358
|
+
generatedManifestSha256: expectation.generatedManifestSha256, observedAt, reason, operatorAction,
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
export function surfaceSelftestReviews(expectation, evidenced) {
|
|
362
|
+
const rows = new Map(evidenced.map((row) => [`${row.skill}\0${row.harness}`, row]));
|
|
363
|
+
if (rows.size !== evidenced.length || evidenced.some((row) => !expectation.shippedSkills.includes(row.skill)
|
|
364
|
+
|| !expectation.reviewHarnesses.some((harness) => harness.name === row.harness)
|
|
365
|
+
|| typeof row.reviewedOn !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(row.reviewedOn)
|
|
366
|
+
|| new Date(`${row.reviewedOn}T00:00:00.000Z`).toISOString().slice(0, 10) !== row.reviewedOn
|
|
367
|
+
|| typeof row.evidenceRunId !== 'string' || row.evidenceRunId.length === 0
|
|
368
|
+
|| Buffer.byteLength(row.evidenceRunId) > 128 || row.evidenceRunId.includes('\0'))
|
|
369
|
+
|| new Set(evidenced.map((row) => row.evidenceRunId)).size !== evidenced.length)
|
|
370
|
+
return undefined;
|
|
371
|
+
const reviews = [];
|
|
372
|
+
for (const skill of expectation.shippedSkills)
|
|
373
|
+
for (const harness of expectation.reviewHarnesses) {
|
|
374
|
+
const row = rows.get(`${skill}\0${harness.name}`);
|
|
375
|
+
if (row)
|
|
376
|
+
reviews.push({
|
|
377
|
+
skill, harness: harness.name, status: 'evidenced', reviewedOn: row.reviewedOn,
|
|
378
|
+
evidenceRunId: row.evidenceRunId,
|
|
379
|
+
reviewers: harness.nativeDiagnostics
|
|
380
|
+
? ['official-authoring', 'harness-diagnostics']
|
|
381
|
+
: ['official-authoring'],
|
|
382
|
+
checks: { trigger: 'pass', silence: 'pass', bodyLoad: 'pass', tools: 'pass', nonGoals: 'pass' },
|
|
383
|
+
operatorAction: null,
|
|
384
|
+
});
|
|
385
|
+
else
|
|
386
|
+
reviews.push({
|
|
387
|
+
skill, harness: harness.name, status: 'human_needed', reviewedOn: null, evidenceRunId: null,
|
|
388
|
+
reviewers: [], checks: null,
|
|
389
|
+
operatorAction: `Review ${skill} with ${harness.name} and record the five authoring checks`,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
return reviews;
|
|
393
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { type RenderedSurfaceFile, type SurfaceTarget } from './contract.js';
|
|
2
|
+
/** The skill `cockpit init` installs first; init and the reference renderer both address it by this name. */
|
|
3
|
+
export declare const SHIPPED_SETUP_SKILL: 'cockpit-setup';
|
|
4
|
+
export interface SkillContractDiagnostic {
|
|
5
|
+
readonly severity: 'error';
|
|
6
|
+
readonly code: string;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
readonly target?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SkillStandingCost {
|
|
11
|
+
readonly target: SurfaceTarget;
|
|
12
|
+
readonly metric: 'frontmatter_bytes';
|
|
13
|
+
readonly value: number;
|
|
14
|
+
readonly cap: number;
|
|
15
|
+
readonly status: 'within_cap' | 'exceeded';
|
|
16
|
+
}
|
|
17
|
+
export interface AdmittedSkillCollection {
|
|
18
|
+
readonly source: Readonly<{
|
|
19
|
+
path: string;
|
|
20
|
+
sha256: string;
|
|
21
|
+
}>;
|
|
22
|
+
readonly skills: readonly string[];
|
|
23
|
+
readonly metric: 'frontmatter_bytes';
|
|
24
|
+
readonly caps: Readonly<Record<SurfaceTarget, number>>;
|
|
25
|
+
/** The packaged procedures of the shipped skills, each read on demand by the skill that names it. */
|
|
26
|
+
readonly procedures: readonly Readonly<{
|
|
27
|
+
path: string;
|
|
28
|
+
sha256: string;
|
|
29
|
+
loading: 'pull';
|
|
30
|
+
budget: 'uncapped';
|
|
31
|
+
}>[];
|
|
32
|
+
readonly entrypoints: readonly Readonly<{
|
|
33
|
+
path: string;
|
|
34
|
+
cap: number;
|
|
35
|
+
metric: 'lines';
|
|
36
|
+
displacement: string;
|
|
37
|
+
}>[];
|
|
38
|
+
}
|
|
39
|
+
export type SkillContractResult = Readonly<{
|
|
40
|
+
kind: 'complete';
|
|
41
|
+
status: 0;
|
|
42
|
+
diagnostics: readonly [];
|
|
43
|
+
skills: readonly string[];
|
|
44
|
+
costs: readonly SkillStandingCost[];
|
|
45
|
+
collection: AdmittedSkillCollection;
|
|
46
|
+
files: readonly RenderedSurfaceFile[];
|
|
47
|
+
}> | Readonly<{
|
|
48
|
+
kind: 'refusal';
|
|
49
|
+
status: 2;
|
|
50
|
+
diagnostics: readonly SkillContractDiagnostic[];
|
|
51
|
+
files: readonly [];
|
|
52
|
+
}> | Readonly<{
|
|
53
|
+
kind: 'unavailable';
|
|
54
|
+
status: 3;
|
|
55
|
+
diagnostics: readonly SkillContractDiagnostic[];
|
|
56
|
+
files: readonly [];
|
|
57
|
+
}>;
|
|
58
|
+
export type SkillStandingExtensionResult = Readonly<{
|
|
59
|
+
kind: 'complete';
|
|
60
|
+
status: 0;
|
|
61
|
+
diagnostics: readonly [];
|
|
62
|
+
skills: readonly string[];
|
|
63
|
+
costs: readonly SkillStandingCost[];
|
|
64
|
+
inventorySha256: string;
|
|
65
|
+
}> | Extract<SkillContractResult, {
|
|
66
|
+
kind: 'refusal';
|
|
67
|
+
}>;
|
|
68
|
+
export type InspectedBootstrapOutputs = Readonly<{
|
|
69
|
+
kind: 'complete';
|
|
70
|
+
/** The first projected target: the one the durable bootstrap state is keyed by. */
|
|
71
|
+
target: SurfaceTarget;
|
|
72
|
+
skill: typeof SHIPPED_SETUP_SKILL;
|
|
73
|
+
files: readonly RenderedSurfaceFile[];
|
|
74
|
+
}>;
|
|
75
|
+
export type InspectedSkillOutputProjection = InspectedBootstrapOutputs | Readonly<{
|
|
76
|
+
kind: 'refusal';
|
|
77
|
+
status: 2;
|
|
78
|
+
diagnostics: readonly SkillContractDiagnostic[];
|
|
79
|
+
files: readonly [];
|
|
80
|
+
}>;
|
|
81
|
+
export declare function isInspectedBootstrapOutputs(value: unknown): value is InspectedBootstrapOutputs;
|
|
82
|
+
export declare function extendInspectedSkillStandingCosts(result: SkillContractResult, additionalFiles: readonly RenderedSurfaceFile[]): SkillStandingExtensionResult;
|
|
83
|
+
export declare function projectInspectedSkillOutputs(result: SkillContractResult, targets: readonly SurfaceTarget[], skill: typeof SHIPPED_SETUP_SKILL): InspectedSkillOutputProjection;
|
|
84
|
+
export declare function inspectSkillSet(input: unknown): Promise<SkillContractResult>;
|
|
85
|
+
export declare function inspectRenderedSkillSet(input: unknown): Promise<SkillContractResult>;
|