@enterpriseai/cli 3.19.0 → 3.19.2
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/package.json +3 -3
- package/resources/deploy/eai-app-template/producer-pin.json +5 -5
- package/resources/deploy/eai-app-template/tests/source-unknown-deployment-evidence.test.mjs +2141 -1
- package/resources/gofer/.gofer-version +4 -357
- package/resources/gofer/agents-skills/eai/SKILL.md +365 -0
- package/resources/gofer/agents-skills/eai-update/SKILL.md +56 -0
- package/resources/gofer/agents-skills/gofer-documentation/SKILL.md +93 -0
- package/resources/gofer/bash-scripts/create-new-feature.sh +3 -1
- package/resources/gofer/bash-scripts/sync-implementation-status.sh +4 -3
- package/resources/gofer/bash-scripts/update-agent-context.sh +7 -8
- package/resources/gofer/bash-scripts/validate-artifact.sh +10 -6
- package/resources/gofer/bash-scripts/verify-object-type-routing-workspace.sh +184 -0
- package/resources/gofer/claude-commands/eai-update.md +46 -0
- package/resources/gofer/claude-commands/eai.md +355 -0
- package/resources/gofer/claude-skills/eai/SKILL.md +365 -0
- package/resources/gofer/claude-skills/eai-update/SKILL.md +56 -0
- package/resources/gofer/claude-skills/gofer-documentation/SKILL.md +93 -0
- package/resources/gofer/commands/0_gofer_start.md +440 -99
- package/resources/gofer/commands/0a_problem_validation.md +187 -27
- package/resources/gofer/commands/10_gofer_cloud.md +183 -23
- package/resources/gofer/commands/1_gofer_research.md +239 -30
- package/resources/gofer/commands/2_gofer_specify.md +239 -35
- package/resources/gofer/commands/3_gofer_plan.md +259 -43
- package/resources/gofer/commands/4_gofer_tasks.md +401 -81
- package/resources/gofer/commands/5_gofer_implement.md +375 -84
- package/resources/gofer/commands/6_gofer_validate.md +284 -53
- package/resources/gofer/commands/7_gofer_save.md +187 -26
- package/resources/gofer/commands/7a_stakeholder_comms.md +183 -23
- package/resources/gofer/commands/8_gofer_branding.md +183 -23
- package/resources/gofer/commands/9_gofer_tests.md +212 -23
- package/resources/gofer/commands/gofer_bootstrap_workspace.md +121 -8
- package/resources/gofer/commands/gofer_check_workspace.md +123 -8
- package/resources/gofer/commands/gofer_constitution.md +183 -23
- package/resources/gofer/commands/gofer_diagnose.md +183 -23
- package/resources/gofer/commands/gofer_eai_first_run.md +254 -25
- package/resources/gofer/commands/gofer_hydrate.md +183 -23
- package/resources/gofer/commands/gofer_personality.md +117 -6
- package/resources/gofer/commands/gofer_plan.md +117 -6
- package/resources/gofer/commands/gofer_side.md +117 -6
- package/resources/gofer/commands/gofer_spec_summary.md +183 -23
- package/resources/gofer/commands/gofer_tdd.md +183 -23
- package/resources/gofer/commands/gofer_vocabulary.md +185 -25
- package/resources/gofer/commands/gofer_zoom_out.md +183 -23
- package/resources/gofer/config/typesafe-learning-review.json +16 -0
- package/resources/gofer/config/typesafe-semantic-review.json +9 -0
- package/resources/gofer/copilot-instructions/gofer-cost.instructions.md +15 -6
- package/resources/gofer/copilot-instructions/typescript.instructions.md +1 -1
- package/resources/gofer/copilot-prompts/eai-update.prompt.md +58 -0
- package/resources/gofer/copilot-prompts/eai.prompt.md +367 -0
- package/resources/gofer/gemini/commands/gofer/eai-update.md +22 -0
- package/resources/gofer/gemini/commands/gofer/eai-update.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/eai.md +357 -0
- package/resources/gofer/gemini/commands/gofer/eai.toml +2 -0
- package/resources/gofer/gemini/commands/gofer/manifest.json +3 -27
- package/resources/gofer/gemini/extension.json +4 -4
- package/resources/gofer/github-agents/eai.agent.md +83 -0
- package/resources/gofer/github-agents/gofer-business.agent.md +73 -4
- package/resources/gofer/github-agents/gofer-implement.agent.md +71 -2
- package/resources/gofer/github-agents/gofer-plan.agent.md +72 -2
- package/resources/gofer/github-agents/gofer-research.agent.md +72 -2
- package/resources/gofer/github-agents/gofer-validate.agent.md +72 -2
- package/resources/gofer/github-skills/eai/SKILL.md +365 -0
- package/resources/gofer/github-skills/eai-update/SKILL.md +56 -0
- package/resources/gofer/github-skills/gofer-documentation/SKILL.md +93 -0
- package/resources/gofer/grok-skills/eai/SKILL.md +267 -14
- package/resources/gofer/grok-skills/eai-update/SKILL.md +56 -0
- package/resources/gofer/hook-scripts/user-prompt-submit.mjs +58 -13
- package/resources/gofer/instruction-templates/base/agents-base.md +8 -4
- package/resources/gofer/instruction-templates/gofer/gofer-claude.md +7 -5
- package/resources/gofer/instruction-templates/gofer/gofer-copilot.md +10 -8
- package/resources/gofer/memory/constitution.md +9 -7
- package/resources/gofer/memory/gofer-model-policy.yaml +23 -1
- package/resources/gofer/memory/lessons.md +0 -31
- package/resources/gofer/node-scripts/canonical-descriptions.mjs +1 -1
- package/resources/gofer/node-scripts/check-version-alignment.mjs +4 -7
- package/resources/gofer/node-scripts/delivery-lineage-corpus.mjs +156 -0
- package/resources/gofer/node-scripts/generate-commands.mjs +1073 -265
- package/resources/gofer/node-scripts/gofer-acceptance-check.mjs +143 -0
- package/resources/gofer/node-scripts/gofer-agent-catalog.mjs +271 -0
- package/resources/gofer/node-scripts/gofer-benchmark-executor.mjs +123 -0
- package/resources/gofer/node-scripts/gofer-benchmark-sign.mjs +62 -0
- package/resources/gofer/node-scripts/gofer-benchmark-signer.mjs +89 -0
- package/resources/gofer/node-scripts/gofer-benchmark.mjs +76 -0
- package/resources/gofer/node-scripts/gofer-blocker-control.mjs +274 -0
- package/resources/gofer/node-scripts/gofer-claude-adapter.mjs +154 -0
- package/resources/gofer/node-scripts/gofer-closed-loop-audit.mjs +19 -2
- package/resources/gofer/node-scripts/gofer-delivery-check.mjs +121 -0
- package/resources/gofer/node-scripts/gofer-encrypted-key.mjs +58 -0
- package/resources/gofer/node-scripts/gofer-execution-metrics.mjs +477 -0
- package/resources/gofer/node-scripts/gofer-execution-recovery.mjs +334 -0
- package/resources/gofer/node-scripts/gofer-grok-adapter.mjs +184 -0
- package/resources/gofer/node-scripts/gofer-heldout-corpus.mjs +73 -0
- package/resources/gofer/node-scripts/gofer-heldout-snapshot.mjs +286 -0
- package/resources/gofer/node-scripts/gofer-heldout-verifier.mjs +211 -0
- package/resources/gofer/node-scripts/gofer-host-capability.mjs +335 -0
- package/resources/gofer/node-scripts/gofer-independent-reviewer.mjs +130 -0
- package/resources/gofer/node-scripts/gofer-live-routing.mjs +109 -0
- package/resources/gofer/node-scripts/gofer-local-capability-issuer.mjs +71 -0
- package/resources/gofer/node-scripts/gofer-local-isolation.mjs +175 -0
- package/resources/gofer/node-scripts/gofer-local-settings-cleanup.mjs +388 -0
- package/resources/gofer/node-scripts/gofer-loop-audit.mjs +54 -3
- package/resources/gofer/node-scripts/gofer-native-adapter.mjs +567 -0
- package/resources/gofer/node-scripts/gofer-native-benchmark-dispatch.mjs +145 -0
- package/resources/gofer/node-scripts/gofer-native-runtime.mjs +169 -0
- package/resources/gofer/node-scripts/gofer-pre-edit-check.mjs +204 -0
- package/resources/gofer/node-scripts/gofer-priority-check.mjs +214 -0
- package/resources/gofer/node-scripts/gofer-response-check.mjs +56 -0
- package/resources/gofer/node-scripts/gofer-reviewed-learning.mjs +1232 -0
- package/resources/gofer/node-scripts/gofer-run-verified-task.mjs +330 -0
- package/resources/gofer/node-scripts/gofer-runtime-ledger.mjs +289 -0
- package/resources/gofer/node-scripts/gofer-semantic-drift.mjs +584 -0
- package/resources/gofer/node-scripts/gofer-surface-update.mjs +1178 -0
- package/resources/gofer/node-scripts/gofer-trust-bootstrap.mjs +77 -0
- package/resources/gofer/node-scripts/gofer-trusted-benchmark.mjs +102 -0
- package/resources/gofer/node-scripts/gofer-trusted-evaluator.mjs +274 -0
- package/resources/gofer/node-scripts/gofer-tty-prompt.mjs +34 -0
- package/resources/gofer/node-scripts/gofer-typesafe-credentials.mjs +435 -0
- package/resources/gofer/node-scripts/gofer-ui-preview.mjs +1212 -0
- package/resources/gofer/node-scripts/gofer-verified-execution.mjs +484 -0
- package/resources/gofer/node-scripts/gofer-verifier-key-ceremony.mjs +101 -0
- package/resources/gofer/node-scripts/gofer-worktree-layout.mjs +32 -0
- package/resources/gofer/node-scripts/object-type-routing-phase-bundle.mjs +360 -0
- package/resources/gofer/node-scripts/package-agent-plugin.mjs +574 -82
- package/resources/gofer/node-scripts/parse-stage-command.mjs +1 -1
- package/resources/gofer/node-scripts/render-delivery-lineage.mjs +178 -0
- package/resources/gofer/node-scripts/sync-extension-resources.mjs +78 -2
- package/resources/gofer/node-scripts/validate-object-type-identifiers.mjs +767 -0
- package/resources/gofer/node-scripts/validate-object-type-routing-workspace.mjs +1325 -0
- package/resources/gofer/node-scripts/validate-v4-resource-contract.d.mts +44 -0
- package/resources/gofer/node-scripts/validate-v4-resource-contract.mjs +957 -0
- package/resources/gofer/node-scripts/workspace-bootstrap-lib.mjs +685 -116
- package/resources/gofer/references/agent-catalog.json +126 -0
- package/resources/gofer/references/blocker-mediation.md +156 -0
- package/resources/gofer/references/business-updates-and-goal-checks.md +109 -0
- package/resources/gofer/references/delivery-lineage.md +48 -0
- package/resources/gofer/references/mvp-capability-validation.md +102 -0
- package/resources/gofer/references/platform/README.md +1 -1
- package/resources/gofer/references/platform/eai-app-template.md +15 -13
- package/resources/gofer/references/platform/eai-auth-access.md +104 -0
- package/resources/gofer/references/platform/eai-config-driven-ui.md +5 -5
- package/resources/gofer/references/platform/eai-error-catalog.yaml +77 -19
- package/resources/gofer/references/platform/eai-repo-contract.md +61 -23
- package/resources/gofer/references/platform/eai-service-patterns.md +102 -105
- package/resources/gofer/references/platform/eai.md +44 -8
- package/resources/gofer/references/platform/vertical-template.md +10 -9
- package/resources/gofer/references/priority-outcome-protection.md +152 -0
- package/resources/gofer/references/verified-agent-execution.md +179 -0
- package/resources/gofer/schemas/gofer-learning-trace-v1.json +70 -0
- package/resources/gofer/specify-commands/0_gofer_start.md +440 -99
- package/resources/gofer/specify-commands/0a_problem_validation.md +187 -27
- package/resources/gofer/specify-commands/10_gofer_cloud.md +183 -23
- package/resources/gofer/specify-commands/1_gofer_research.md +239 -30
- package/resources/gofer/specify-commands/2_gofer_specify.md +239 -35
- package/resources/gofer/specify-commands/3_gofer_plan.md +259 -43
- package/resources/gofer/specify-commands/4_gofer_tasks.md +401 -81
- package/resources/gofer/specify-commands/5_gofer_implement.md +375 -84
- package/resources/gofer/specify-commands/6_gofer_validate.md +284 -53
- package/resources/gofer/specify-commands/7_gofer_save.md +187 -26
- package/resources/gofer/specify-commands/7a_stakeholder_comms.md +183 -23
- package/resources/gofer/specify-commands/8_gofer_branding.md +183 -23
- package/resources/gofer/specify-commands/9_gofer_tests.md +212 -23
- package/resources/gofer/specify-commands/gofer_bootstrap_workspace.md +121 -8
- package/resources/gofer/specify-commands/gofer_check_workspace.md +123 -8
- package/resources/gofer/specify-commands/gofer_constitution.md +183 -23
- package/resources/gofer/specify-commands/gofer_diagnose.md +183 -23
- package/resources/gofer/specify-commands/gofer_eai_first_run.md +254 -25
- package/resources/gofer/specify-commands/gofer_hydrate.md +183 -23
- package/resources/gofer/specify-commands/gofer_personality.md +117 -6
- package/resources/gofer/specify-commands/gofer_plan.md +117 -6
- package/resources/gofer/specify-commands/gofer_side.md +117 -6
- package/resources/gofer/specify-commands/gofer_spec_summary.md +183 -23
- package/resources/gofer/specify-commands/gofer_tdd.md +183 -23
- package/resources/gofer/specify-commands/gofer_vocabulary.md +185 -25
- package/resources/gofer/specify-commands/gofer_zoom_out.md +183 -23
- package/resources/gofer/specify-config/object-type-routing.json +24 -0
- package/resources/gofer/specify-config/typesafe-learning-review.json +16 -0
- package/resources/gofer/specify-config/typesafe-semantic-review.json +9 -0
- package/resources/gofer/system-skills/eai/SKILL.md +365 -0
- package/resources/gofer/system-skills/eai-update/SKILL.md +56 -0
- package/resources/gofer/system-skills/gofer-documentation/SKILL.md +93 -0
- package/resources/gofer/templates/build-map-template.md +64 -0
- package/resources/gofer/templates/business-scenarios-template.json +14 -0
- package/resources/gofer/templates/ciso-security-summary-template.md +3 -3
- package/resources/gofer/templates/context-bundle-template.md +4 -3
- package/resources/gofer/templates/contract-pack-template.md +8 -8
- package/resources/gofer/templates/cto-architecture-summary-template.md +3 -3
- package/resources/gofer/templates/delivery-lineage-template.json +8 -0
- package/resources/gofer/templates/eai-preflight-template.md +109 -68
- package/resources/gofer/templates/goal-ledger-template.json +1 -1
- package/resources/gofer/templates/gofer-model-policy.yaml +28 -6
- package/resources/gofer/templates/loop-contract-template.json +3 -1
- package/resources/gofer/templates/plan-template.md +8 -7
- package/resources/gofer/templates/priority-plan-template.json +29 -0
- package/resources/gofer/templates/proposal-review-template.md +6 -3
- package/resources/gofer/templates/release-capability-ledger-template.md +24 -0
- package/resources/gofer/templates/research-template.md +7 -6
- package/resources/gofer/templates/spec-summary-template.md +1 -1
- package/resources/gofer/templates/spec-template.md +45 -6
- package/resources/gofer/templates/tasks-template.md +12 -3
- package/resources/gofer/templates/ui-preview-brief-template.md +55 -5
- package/resources/gofer/templates/ui-review-log-template.md +21 -3
- package/resources/gofer/templates/ui-show-and-tell-template.md +46 -0
- package/resources/gofer/templates/visuals/capability-heatmap-template.md +2 -2
- package/resources/gofer/templates/working-backwards-prfaq-template.md +9 -8
- package/resources/linked-sources.json +4 -4
- package/resources/gofer/agents-skills/0_gofer_start/SKILL.md +0 -1287
- package/resources/gofer/agents-skills/0a_problem_validation/SKILL.md +0 -429
- package/resources/gofer/agents-skills/10_gofer_cloud/SKILL.md +0 -635
- package/resources/gofer/agents-skills/1_gofer_research/SKILL.md +0 -1159
- package/resources/gofer/agents-skills/2_gofer_specify/SKILL.md +0 -860
- package/resources/gofer/agents-skills/3_gofer_plan/SKILL.md +0 -899
- package/resources/gofer/agents-skills/4_gofer_tasks/SKILL.md +0 -760
- package/resources/gofer/agents-skills/5_gofer_implement/SKILL.md +0 -944
- package/resources/gofer/agents-skills/6_gofer_validate/SKILL.md +0 -2302
- package/resources/gofer/agents-skills/7_gofer_save/SKILL.md +0 -423
- package/resources/gofer/agents-skills/7a_stakeholder_comms/SKILL.md +0 -508
- package/resources/gofer/agents-skills/8_gofer_branding/SKILL.md +0 -315
- package/resources/gofer/agents-skills/9_gofer_tests/SKILL.md +0 -558
- package/resources/gofer/agents-skills/gofer_bootstrap_workspace/SKILL.md +0 -148
- package/resources/gofer/agents-skills/gofer_check_workspace/SKILL.md +0 -119
- package/resources/gofer/agents-skills/gofer_constitution/SKILL.md +0 -612
- package/resources/gofer/agents-skills/gofer_diagnose/SKILL.md +0 -114
- package/resources/gofer/agents-skills/gofer_eai_first_run/SKILL.md +0 -397
- package/resources/gofer/agents-skills/gofer_hydrate/SKILL.md +0 -561
- package/resources/gofer/agents-skills/gofer_personality/SKILL.md +0 -44
- package/resources/gofer/agents-skills/gofer_plan/SKILL.md +0 -40
- package/resources/gofer/agents-skills/gofer_side/SKILL.md +0 -45
- package/resources/gofer/agents-skills/gofer_spec_summary/SKILL.md +0 -112
- package/resources/gofer/agents-skills/gofer_tdd/SKILL.md +0 -112
- package/resources/gofer/agents-skills/gofer_vocabulary/SKILL.md +0 -112
- package/resources/gofer/agents-skills/gofer_zoom_out/SKILL.md +0 -112
- package/resources/gofer/claude-commands/0_gofer_start.md +0 -1282
- package/resources/gofer/claude-commands/0a_problem_validation.md +0 -424
- package/resources/gofer/claude-commands/10_gofer_cloud.md +0 -630
- package/resources/gofer/claude-commands/1_gofer_research.md +0 -1154
- package/resources/gofer/claude-commands/2_gofer_specify.md +0 -855
- package/resources/gofer/claude-commands/3_gofer_plan.md +0 -894
- package/resources/gofer/claude-commands/4_gofer_tasks.md +0 -755
- package/resources/gofer/claude-commands/5_gofer_implement.md +0 -939
- package/resources/gofer/claude-commands/6_gofer_validate.md +0 -2297
- package/resources/gofer/claude-commands/7_gofer_save.md +0 -418
- package/resources/gofer/claude-commands/7a_stakeholder_comms.md +0 -503
- package/resources/gofer/claude-commands/8_gofer_branding.md +0 -310
- package/resources/gofer/claude-commands/9_gofer_tests.md +0 -553
- package/resources/gofer/claude-commands/gofer_bootstrap_workspace.md +0 -143
- package/resources/gofer/claude-commands/gofer_check_workspace.md +0 -114
- package/resources/gofer/claude-commands/gofer_constitution.md +0 -607
- package/resources/gofer/claude-commands/gofer_diagnose.md +0 -109
- package/resources/gofer/claude-commands/gofer_eai_first_run.md +0 -392
- package/resources/gofer/claude-commands/gofer_hydrate.md +0 -556
- package/resources/gofer/claude-commands/gofer_personality.md +0 -39
- package/resources/gofer/claude-commands/gofer_plan.md +0 -35
- package/resources/gofer/claude-commands/gofer_side.md +0 -40
- package/resources/gofer/claude-commands/gofer_spec_summary.md +0 -107
- package/resources/gofer/claude-commands/gofer_tdd.md +0 -107
- package/resources/gofer/claude-commands/gofer_vocabulary.md +0 -107
- package/resources/gofer/claude-commands/gofer_zoom_out.md +0 -107
- package/resources/gofer/claude-skills/eai-gofer/SKILL.md +0 -56
- package/resources/gofer/copilot-prompts/0_gofer_start.prompt.md +0 -1308
- package/resources/gofer/copilot-prompts/0a_problem_validation.prompt.md +0 -429
- package/resources/gofer/copilot-prompts/10_gofer_cloud.prompt.md +0 -644
- package/resources/gofer/copilot-prompts/1_gofer_research.prompt.md +0 -1177
- package/resources/gofer/copilot-prompts/2_gofer_specify.prompt.md +0 -878
- package/resources/gofer/copilot-prompts/3_gofer_plan.prompt.md +0 -917
- package/resources/gofer/copilot-prompts/4_gofer_tasks.prompt.md +0 -778
- package/resources/gofer/copilot-prompts/5_gofer_implement.prompt.md +0 -962
- package/resources/gofer/copilot-prompts/6_gofer_validate.prompt.md +0 -2310
- package/resources/gofer/copilot-prompts/7_gofer_save.prompt.md +0 -433
- package/resources/gofer/copilot-prompts/7a_stakeholder_comms.prompt.md +0 -516
- package/resources/gofer/copilot-prompts/8_gofer_branding.prompt.md +0 -323
- package/resources/gofer/copilot-prompts/9_gofer_tests.prompt.md +0 -566
- package/resources/gofer/copilot-prompts/gofer_bootstrap_workspace.prompt.md +0 -161
- package/resources/gofer/copilot-prompts/gofer_check_workspace.prompt.md +0 -132
- package/resources/gofer/copilot-prompts/gofer_constitution.prompt.md +0 -621
- package/resources/gofer/copilot-prompts/gofer_diagnose.prompt.md +0 -127
- package/resources/gofer/copilot-prompts/gofer_eai_first_run.prompt.md +0 -410
- package/resources/gofer/copilot-prompts/gofer_hydrate.prompt.md +0 -571
- package/resources/gofer/copilot-prompts/gofer_personality.prompt.md +0 -57
- package/resources/gofer/copilot-prompts/gofer_plan.prompt.md +0 -53
- package/resources/gofer/copilot-prompts/gofer_side.prompt.md +0 -58
- package/resources/gofer/copilot-prompts/gofer_spec_summary.prompt.md +0 -125
- package/resources/gofer/copilot-prompts/gofer_tdd.prompt.md +0 -125
- package/resources/gofer/copilot-prompts/gofer_vocabulary.prompt.md +0 -125
- package/resources/gofer/copilot-prompts/gofer_zoom_out.prompt.md +0 -125
- package/resources/gofer/gemini/commands/gofer/0_gofer_start.md +0 -1282
- package/resources/gofer/gemini/commands/gofer/0_gofer_start.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/0a_problem_validation.md +0 -424
- package/resources/gofer/gemini/commands/gofer/0a_problem_validation.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/10_gofer_cloud.md +0 -630
- package/resources/gofer/gemini/commands/gofer/10_gofer_cloud.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/1_gofer_research.md +0 -1154
- package/resources/gofer/gemini/commands/gofer/1_gofer_research.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/2_gofer_specify.md +0 -855
- package/resources/gofer/gemini/commands/gofer/2_gofer_specify.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/3_gofer_plan.md +0 -894
- package/resources/gofer/gemini/commands/gofer/3_gofer_plan.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/4_gofer_tasks.md +0 -755
- package/resources/gofer/gemini/commands/gofer/4_gofer_tasks.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/5_gofer_implement.md +0 -939
- package/resources/gofer/gemini/commands/gofer/5_gofer_implement.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/6_gofer_validate.md +0 -2297
- package/resources/gofer/gemini/commands/gofer/6_gofer_validate.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/7_gofer_save.md +0 -418
- package/resources/gofer/gemini/commands/gofer/7_gofer_save.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/7a_stakeholder_comms.md +0 -503
- package/resources/gofer/gemini/commands/gofer/7a_stakeholder_comms.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/8_gofer_branding.md +0 -310
- package/resources/gofer/gemini/commands/gofer/8_gofer_branding.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/9_gofer_tests.md +0 -553
- package/resources/gofer/gemini/commands/gofer/9_gofer_tests.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_bootstrap_workspace.md +0 -143
- package/resources/gofer/gemini/commands/gofer/gofer_bootstrap_workspace.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_check_workspace.md +0 -114
- package/resources/gofer/gemini/commands/gofer/gofer_check_workspace.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_constitution.md +0 -607
- package/resources/gofer/gemini/commands/gofer/gofer_constitution.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_diagnose.md +0 -109
- package/resources/gofer/gemini/commands/gofer/gofer_diagnose.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_eai_first_run.md +0 -392
- package/resources/gofer/gemini/commands/gofer/gofer_eai_first_run.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_hydrate.md +0 -556
- package/resources/gofer/gemini/commands/gofer/gofer_hydrate.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_personality.md +0 -39
- package/resources/gofer/gemini/commands/gofer/gofer_personality.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_plan.md +0 -35
- package/resources/gofer/gemini/commands/gofer/gofer_plan.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_side.md +0 -40
- package/resources/gofer/gemini/commands/gofer/gofer_side.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_spec_summary.md +0 -107
- package/resources/gofer/gemini/commands/gofer/gofer_spec_summary.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_tdd.md +0 -107
- package/resources/gofer/gemini/commands/gofer/gofer_tdd.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_vocabulary.md +0 -107
- package/resources/gofer/gemini/commands/gofer/gofer_vocabulary.toml +0 -2
- package/resources/gofer/gemini/commands/gofer/gofer_zoom_out.md +0 -107
- package/resources/gofer/gemini/commands/gofer/gofer_zoom_out.toml +0 -2
- package/resources/gofer/github-skills/eai-gofer/SKILL.md +0 -56
- package/resources/gofer/system-skills/0_gofer_start/SKILL.md +0 -1287
- package/resources/gofer/system-skills/0a_problem_validation/SKILL.md +0 -429
- package/resources/gofer/system-skills/10_gofer_cloud/SKILL.md +0 -635
- package/resources/gofer/system-skills/1_gofer_research/SKILL.md +0 -1159
- package/resources/gofer/system-skills/2_gofer_specify/SKILL.md +0 -860
- package/resources/gofer/system-skills/3_gofer_plan/SKILL.md +0 -899
- package/resources/gofer/system-skills/4_gofer_tasks/SKILL.md +0 -760
- package/resources/gofer/system-skills/5_gofer_implement/SKILL.md +0 -944
- package/resources/gofer/system-skills/6_gofer_validate/SKILL.md +0 -2302
- package/resources/gofer/system-skills/7_gofer_save/SKILL.md +0 -423
- package/resources/gofer/system-skills/7a_stakeholder_comms/SKILL.md +0 -508
- package/resources/gofer/system-skills/8_gofer_branding/SKILL.md +0 -315
- package/resources/gofer/system-skills/9_gofer_tests/SKILL.md +0 -558
- package/resources/gofer/system-skills/gofer_bootstrap_workspace/SKILL.md +0 -148
- package/resources/gofer/system-skills/gofer_check_workspace/SKILL.md +0 -119
- package/resources/gofer/system-skills/gofer_constitution/SKILL.md +0 -612
- package/resources/gofer/system-skills/gofer_diagnose/SKILL.md +0 -114
- package/resources/gofer/system-skills/gofer_eai_first_run/SKILL.md +0 -397
- package/resources/gofer/system-skills/gofer_hydrate/SKILL.md +0 -561
- package/resources/gofer/system-skills/gofer_personality/SKILL.md +0 -44
- package/resources/gofer/system-skills/gofer_plan/SKILL.md +0 -40
- package/resources/gofer/system-skills/gofer_side/SKILL.md +0 -45
- package/resources/gofer/system-skills/gofer_spec_summary/SKILL.md +0 -112
- package/resources/gofer/system-skills/gofer_tdd/SKILL.md +0 -112
- package/resources/gofer/system-skills/gofer_vocabulary/SKILL.md +0 -112
- package/resources/gofer/system-skills/gofer_zoom_out/SKILL.md +0 -112
- package/resources/gofer/templates/ui-approval-template.md +0 -32
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/** Copy benchmark outputs into an owner-only, content-addressed local store.
|
|
2
|
+
* Capture preserves bytes; it does not verify functionality or grant routing authority. */
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
import { constants } from 'node:fs';
|
|
5
|
+
import { lstat, mkdir, mkdtemp, open, opendir, realpath, rename, rm } from 'node:fs/promises';
|
|
6
|
+
import os from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { loadTrustedHeldOutCorpus } from './gofer-trusted-evaluator.mjs';
|
|
10
|
+
|
|
11
|
+
const denied = () => new Error('HELDOUT_SNAPSHOT_REQUIRED');
|
|
12
|
+
const sha = bytes => createHash('sha256').update(bytes).digest('hex');
|
|
13
|
+
const safeName = value => typeof value === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/.test(value);
|
|
14
|
+
const safeEntry = value => typeof value === 'string' &&
|
|
15
|
+
/^(receipts|worktrees)\/[a-zA-Z0-9._/-]+$/.test(value) &&
|
|
16
|
+
!value.split('/').some(part => part === '.' || part === '..' || !part);
|
|
17
|
+
const inside = (root, target) => {
|
|
18
|
+
const relative = path.relative(root, target);
|
|
19
|
+
return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
async function ownerDirectory(directory, privateDirectory = true) {
|
|
23
|
+
const info = await lstat(directory);
|
|
24
|
+
if (!info.isDirectory() || info.uid !== process.getuid() ||
|
|
25
|
+
(info.mode & (privateDirectory ? 0o077 : 0o022)) !== 0) throw denied();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function sourceFile(filename, maxBytes = 2 * 1024 * 1024, minBytes = 1) {
|
|
29
|
+
const file = await open(filename, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
30
|
+
try {
|
|
31
|
+
const info = await file.stat();
|
|
32
|
+
if (!info.isFile() || info.uid !== process.getuid() || info.nlink !== 1 ||
|
|
33
|
+
(info.mode & 0o022) !== 0 || info.size < minBytes || info.size > maxBytes) throw denied();
|
|
34
|
+
return await file.readFile();
|
|
35
|
+
} finally { await file.close(); }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function writeObject(objects, bytes) {
|
|
39
|
+
const hash = sha(bytes);
|
|
40
|
+
const filename = path.join(objects, hash);
|
|
41
|
+
try {
|
|
42
|
+
const file = await open(filename, constants.O_WRONLY | constants.O_CREAT |
|
|
43
|
+
constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
44
|
+
try { await file.writeFile(bytes); await file.sync(); }
|
|
45
|
+
finally { await file.close(); }
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (error.code !== 'EEXIST' || sha(await sourceFile(filename, 2 * 1024 * 1024, 0)) !== hash) throw error;
|
|
48
|
+
}
|
|
49
|
+
return hash;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function collectWorktree(worktree, prefix, add) {
|
|
53
|
+
const pending = [''];
|
|
54
|
+
let fileCount = 0;
|
|
55
|
+
let totalBytes = 0;
|
|
56
|
+
while (pending.length) {
|
|
57
|
+
const relative = pending.pop();
|
|
58
|
+
const directory = path.join(worktree, relative);
|
|
59
|
+
await ownerDirectory(directory, relative === '');
|
|
60
|
+
const entries = await opendir(directory);
|
|
61
|
+
for await (const entry of entries) {
|
|
62
|
+
if (entry.name === '.git' && relative === '') continue;
|
|
63
|
+
if (entry.name === '.' || entry.name === '..' || entry.name.includes('/') || entry.name.includes('\\')) throw denied();
|
|
64
|
+
const name = relative ? `${relative}/${entry.name}` : entry.name;
|
|
65
|
+
const filename = path.join(worktree, name);
|
|
66
|
+
const info = await lstat(filename);
|
|
67
|
+
if (info.isDirectory()) { pending.push(name); continue; }
|
|
68
|
+
if (!info.isFile() || ++fileCount > 5000) throw denied();
|
|
69
|
+
const bytes = await sourceFile(filename, 2 * 1024 * 1024, 0);
|
|
70
|
+
totalBytes += bytes.length;
|
|
71
|
+
if (totalBytes > 16 * 1024 * 1024) throw denied();
|
|
72
|
+
await add(`${prefix}/${name}`, bytes);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Trusted-controller seam. A snapshot is diagnostic until separately rechecked
|
|
78
|
+
* against the pinned corpus and signed by an isolated verifier identity. */
|
|
79
|
+
export async function captureHeldOutResultSnapshot({ corpusRoot, workspaceRoot, trustRoot,
|
|
80
|
+
expectedCorpusHash, expectedCaseIds } = {}) {
|
|
81
|
+
if (!['darwin', 'linux'].includes(process.platform) || typeof process.getuid !== 'function' ||
|
|
82
|
+
![corpusRoot, workspaceRoot, trustRoot].every(value => typeof value === 'string' && path.isAbsolute(value))) throw denied();
|
|
83
|
+
let staging;
|
|
84
|
+
try {
|
|
85
|
+
const [corpus, workspace, trust, temporary] = await Promise.all([
|
|
86
|
+
realpath(corpusRoot), realpath(workspaceRoot), realpath(trustRoot), realpath(os.tmpdir()),
|
|
87
|
+
]);
|
|
88
|
+
if (inside(workspace, trust) || inside(trust, workspace) || !inside(trust, corpus)) throw denied();
|
|
89
|
+
await ownerDirectory(trust);
|
|
90
|
+
await ownerDirectory(corpus);
|
|
91
|
+
const receipts = path.join(corpus, 'receipts');
|
|
92
|
+
await ownerDirectory(receipts);
|
|
93
|
+
const reportBytes = await sourceFile(path.join(receipts, 'benchmark-report.json'));
|
|
94
|
+
const saved = JSON.parse(reportBytes.toString('utf8'));
|
|
95
|
+
const runs = saved?.report?.runs;
|
|
96
|
+
if (!Array.isArray(runs) || runs.length < 12 || runs.length > 3000 ||
|
|
97
|
+
saved.report.repetitions !== 3 || !/^[a-f0-9]{64}$/.test(saved.corpusHash ?? '') ||
|
|
98
|
+
(expectedCorpusHash && saved.corpusHash !== expectedCorpusHash)) throw denied();
|
|
99
|
+
if (expectedCaseIds && (!Array.isArray(expectedCaseIds) ||
|
|
100
|
+
runs.length !== expectedCaseIds.length * 3 ||
|
|
101
|
+
new Set(runs.map(item => item?.caseId)).size !== expectedCaseIds.length ||
|
|
102
|
+
runs.some(item => !expectedCaseIds.includes(item?.caseId)))) throw denied();
|
|
103
|
+
const results = path.join(trust, 'results');
|
|
104
|
+
await mkdir(results, { mode: 0o700, recursive: true });
|
|
105
|
+
await ownerDirectory(results);
|
|
106
|
+
staging = path.join(results, `.staging-${randomUUID()}`);
|
|
107
|
+
await mkdir(staging, { mode: 0o700 });
|
|
108
|
+
const objects = path.join(staging, 'objects');
|
|
109
|
+
await mkdir(objects, { mode: 0o700 });
|
|
110
|
+
const entries = [];
|
|
111
|
+
const names = new Set();
|
|
112
|
+
let capturedBytes = 0;
|
|
113
|
+
const add = async (name, bytes) => {
|
|
114
|
+
if (names.has(name)) throw denied();
|
|
115
|
+
capturedBytes += bytes.length;
|
|
116
|
+
if (capturedBytes > 128 * 1024 * 1024) throw denied();
|
|
117
|
+
names.add(name);
|
|
118
|
+
entries.push({ name, sha256: await writeObject(objects, bytes) });
|
|
119
|
+
};
|
|
120
|
+
await add('receipts/benchmark-report.json', reportBytes);
|
|
121
|
+
const labels = new Set();
|
|
122
|
+
const worktrees = new Set();
|
|
123
|
+
for (const run of runs) {
|
|
124
|
+
if (!safeName(run?.caseId) || ![1, 2, 3].includes(run?.run)) throw denied();
|
|
125
|
+
const label = `${run.caseId}-${run.run}`;
|
|
126
|
+
if (labels.has(label)) throw denied();
|
|
127
|
+
labels.add(label);
|
|
128
|
+
const executionBytes = await sourceFile(path.join(receipts, `${label}.execution.json`));
|
|
129
|
+
const verificationBytes = await sourceFile(path.join(receipts, `${label}.verification.json`));
|
|
130
|
+
const execution = JSON.parse(executionBytes.toString('utf8'));
|
|
131
|
+
if (!path.isAbsolute(execution?.worktree ?? '')) throw denied();
|
|
132
|
+
const worktree = await realpath(execution.worktree);
|
|
133
|
+
if (!inside(temporary, worktree) || inside(workspace, worktree) || inside(trust, worktree) ||
|
|
134
|
+
worktrees.has(worktree)) throw denied();
|
|
135
|
+
worktrees.add(worktree);
|
|
136
|
+
await ownerDirectory(worktree);
|
|
137
|
+
await add(`receipts/${label}.execution.json`, executionBytes);
|
|
138
|
+
await add(`receipts/${label}.verification.json`, verificationBytes);
|
|
139
|
+
await collectWorktree(worktree, `worktrees/${label}`, add);
|
|
140
|
+
}
|
|
141
|
+
entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
142
|
+
const manifest = { schemaVersion: 1, authority: 'diagnostic-only',
|
|
143
|
+
corpusHash: saved.corpusHash, reportSha256: sha(reportBytes), entries };
|
|
144
|
+
const manifestBytes = Buffer.from(JSON.stringify(manifest));
|
|
145
|
+
const snapshotId = sha(manifestBytes);
|
|
146
|
+
const file = await open(path.join(staging, 'manifest.json'), constants.O_WRONLY |
|
|
147
|
+
constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
148
|
+
try { await file.writeFile(manifestBytes); await file.sync(); }
|
|
149
|
+
finally { await file.close(); }
|
|
150
|
+
await rename(staging, path.join(results, snapshotId));
|
|
151
|
+
staging = null;
|
|
152
|
+
await inspectHeldOutResultSnapshot({ trustRoot: trust, workspaceRoot, snapshotId });
|
|
153
|
+
return Object.freeze({ snapshotId, corpusHash: saved.corpusHash,
|
|
154
|
+
reportSha256: manifest.reportSha256, fileCount: entries.length, authority: 'diagnostic-only' });
|
|
155
|
+
} catch { throw denied(); }
|
|
156
|
+
finally { if (staging) await rm(staging, { recursive: true, force: true }); }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Recheck the protected copy before a verifier may consume it. The digest
|
|
160
|
+
* detects corruption; it is not a substitute for a separate signing key. */
|
|
161
|
+
export async function inspectHeldOutResultSnapshot({ trustRoot, workspaceRoot, snapshotId } = {}) {
|
|
162
|
+
if (!/^[a-f0-9]{64}$/.test(snapshotId ?? '') ||
|
|
163
|
+
![trustRoot, workspaceRoot].every(value => typeof value === 'string' && path.isAbsolute(value))) throw denied();
|
|
164
|
+
try {
|
|
165
|
+
const [trust, workspace] = await Promise.all([realpath(trustRoot), realpath(workspaceRoot)]);
|
|
166
|
+
if (inside(workspace, trust) || inside(trust, workspace)) throw denied();
|
|
167
|
+
const results = path.join(trust, 'results');
|
|
168
|
+
const folder = path.join(results, snapshotId);
|
|
169
|
+
const objects = path.join(folder, 'objects');
|
|
170
|
+
for (const directory of [trust, results, folder, objects]) await ownerDirectory(directory);
|
|
171
|
+
const manifestBytes = await sourceFile(path.join(folder, 'manifest.json'), 4 * 1024 * 1024);
|
|
172
|
+
if (sha(manifestBytes) !== snapshotId) throw denied();
|
|
173
|
+
const manifest = JSON.parse(manifestBytes.toString('utf8'));
|
|
174
|
+
if (manifest?.schemaVersion !== 1 || manifest.authority !== 'diagnostic-only' ||
|
|
175
|
+
!/^[a-f0-9]{64}$/.test(manifest.corpusHash ?? '') ||
|
|
176
|
+
!/^[a-f0-9]{64}$/.test(manifest.reportSha256 ?? '') ||
|
|
177
|
+
!Array.isArray(manifest.entries) || manifest.entries.length < 37 ||
|
|
178
|
+
manifest.entries.length > 15000) throw denied();
|
|
179
|
+
const names = new Set();
|
|
180
|
+
let totalBytes = 0;
|
|
181
|
+
for (const entry of manifest.entries) {
|
|
182
|
+
if (!safeEntry(entry?.name) || names.has(entry.name) ||
|
|
183
|
+
!/^[a-f0-9]{64}$/.test(entry.sha256 ?? '')) throw denied();
|
|
184
|
+
names.add(entry.name);
|
|
185
|
+
const bytes = await sourceFile(path.join(objects, entry.sha256), 2 * 1024 * 1024, 0);
|
|
186
|
+
totalBytes += bytes.length;
|
|
187
|
+
if (totalBytes > 128 * 1024 * 1024 || sha(bytes) !== entry.sha256) throw denied();
|
|
188
|
+
}
|
|
189
|
+
const report = manifest.entries.find(entry => entry.name === 'receipts/benchmark-report.json');
|
|
190
|
+
if (report?.sha256 !== manifest.reportSha256) throw denied();
|
|
191
|
+
return Object.freeze({ snapshotId, corpusHash: manifest.corpusHash,
|
|
192
|
+
reportSha256: manifest.reportSha256, fileCount: manifest.entries.length,
|
|
193
|
+
authority: 'diagnostic-only' });
|
|
194
|
+
} catch { throw denied(); }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Materialize verified bytes outside the account trust root for the
|
|
198
|
+
* read-only, offline verifier sandbox. The caller owns cleanup of `root`. */
|
|
199
|
+
export async function materializeHeldOutResultSnapshot({ trustRoot, workspaceRoot, snapshotId } = {}) {
|
|
200
|
+
await inspectHeldOutResultSnapshot({ trustRoot, workspaceRoot, snapshotId });
|
|
201
|
+
let root;
|
|
202
|
+
try {
|
|
203
|
+
const folder = path.join(await realpath(trustRoot), 'results', snapshotId);
|
|
204
|
+
const manifestBytes = await sourceFile(path.join(folder, 'manifest.json'), 4 * 1024 * 1024);
|
|
205
|
+
if (sha(manifestBytes) !== snapshotId) throw denied();
|
|
206
|
+
const manifest = JSON.parse(manifestBytes.toString('utf8'));
|
|
207
|
+
root = await mkdtemp(path.join(os.tmpdir(), 'gofer-heldout-recheck-'));
|
|
208
|
+
for (const entry of manifest.entries) {
|
|
209
|
+
if (!safeEntry(entry?.name) || !/^[a-f0-9]{64}$/.test(entry.sha256 ?? '')) throw denied();
|
|
210
|
+
const bytes = await sourceFile(path.join(folder, 'objects', entry.sha256), 2 * 1024 * 1024, 0);
|
|
211
|
+
if (sha(bytes) !== entry.sha256) throw denied();
|
|
212
|
+
const filename = path.join(root, entry.name);
|
|
213
|
+
if (!inside(root, filename)) throw denied();
|
|
214
|
+
await mkdir(path.dirname(filename), { recursive: true, mode: 0o700 });
|
|
215
|
+
const file = await open(filename, constants.O_WRONLY | constants.O_CREAT |
|
|
216
|
+
constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
|
|
217
|
+
try { await file.writeFile(bytes); }
|
|
218
|
+
finally { await file.close(); }
|
|
219
|
+
}
|
|
220
|
+
await inspectHeldOutResultSnapshot({ trustRoot, workspaceRoot, snapshotId });
|
|
221
|
+
return Object.freeze({ root, snapshotId, corpusHash: manifest.corpusHash,
|
|
222
|
+
reportSha256: manifest.reportSha256 });
|
|
223
|
+
} catch {
|
|
224
|
+
if (root) await rm(root, { recursive: true, force: true });
|
|
225
|
+
throw denied();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** A trusted setup path may pin one captured result without signing it. */
|
|
230
|
+
export async function loadPinnedHeldOutSnapshot({ trustRoot, workspaceRoot, corpusHash } = {}) {
|
|
231
|
+
if (!/^[a-f0-9]{64}$/.test(corpusHash ?? '') ||
|
|
232
|
+
![trustRoot, workspaceRoot].every(value => typeof value === 'string' && path.isAbsolute(value))) throw denied();
|
|
233
|
+
try {
|
|
234
|
+
const trust = await realpath(trustRoot);
|
|
235
|
+
const workspace = await realpath(workspaceRoot);
|
|
236
|
+
if (inside(workspace, trust) || inside(trust, workspace)) throw denied();
|
|
237
|
+
await ownerDirectory(trust);
|
|
238
|
+
const configPath = path.join(trust, 'heldout-results.json');
|
|
239
|
+
const configInfo = await lstat(configPath);
|
|
240
|
+
if (!configInfo.isFile() || configInfo.uid !== process.getuid() ||
|
|
241
|
+
configInfo.nlink !== 1 || (configInfo.mode & 0o077) !== 0) throw denied();
|
|
242
|
+
const config = JSON.parse((await sourceFile(configPath, 4096)).toString('utf8'));
|
|
243
|
+
if (config?.schemaVersion !== 1 || !/^[a-f0-9]{64}$/.test(config.snapshotId ?? '') ||
|
|
244
|
+
config.corpusHash !== corpusHash) throw denied();
|
|
245
|
+
const snapshot = await inspectHeldOutResultSnapshot({ trustRoot: trust, workspaceRoot, snapshotId: config.snapshotId });
|
|
246
|
+
if (snapshot.corpusHash !== corpusHash) throw denied();
|
|
247
|
+
return Object.freeze({ snapshotId: config.snapshotId, trustRoot: trust });
|
|
248
|
+
} catch { throw denied(); }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** The trusted controller must present a durable capture authorization.
|
|
252
|
+
* The command-line surface has no way to inject one and stays fail-closed. */
|
|
253
|
+
export async function captureConfiguredHeldOutResultSnapshot({ workspaceRoot, ledger,
|
|
254
|
+
captureAuthorization } = {}) {
|
|
255
|
+
try {
|
|
256
|
+
if (typeof ledger?.inspectBenchmarkCapture !== 'function' ||
|
|
257
|
+
typeof ledger?.recordBenchmarkSnapshot !== 'function' ||
|
|
258
|
+
!captureAuthorization?.receipt || !captureAuthorization?.workerStopReceipt ||
|
|
259
|
+
!captureAuthorization?.revision || !captureAuthorization?.reportSha256) throw denied();
|
|
260
|
+
const corpus = await loadTrustedHeldOutCorpus({ workspaceRoot });
|
|
261
|
+
const reportSha256 = sha(await sourceFile(path.join(corpus.corpusRoot, 'receipts', 'benchmark-report.json')));
|
|
262
|
+
if (captureAuthorization.corpusHash !== corpus.corpusHash ||
|
|
263
|
+
captureAuthorization.reportSha256 !== reportSha256 ||
|
|
264
|
+
(await ledger.inspectBenchmarkCapture(captureAuthorization))?.valid !== true) throw denied();
|
|
265
|
+
const snapshot = await captureHeldOutResultSnapshot({ corpusRoot: corpus.corpusRoot, workspaceRoot,
|
|
266
|
+
trustRoot: path.dirname(path.dirname(corpus.corpusRoot)), expectedCorpusHash: corpus.corpusHash,
|
|
267
|
+
expectedCaseIds: corpus.cases.map(item => item.id) });
|
|
268
|
+
if (snapshot.reportSha256 !== reportSha256) throw denied();
|
|
269
|
+
const recorded = await ledger.recordBenchmarkSnapshot({ captureReceipt: captureAuthorization.receipt,
|
|
270
|
+
corpusHash: corpus.corpusHash, reportSha256, snapshotId: snapshot.snapshotId });
|
|
271
|
+
if (recorded?.allowed !== true || !recorded?.receipt) throw denied();
|
|
272
|
+
return Object.freeze({ ...snapshot, ledgerCaptureReceipt: captureAuthorization.receipt,
|
|
273
|
+
ledgerSnapshotReceipt: recorded.receipt });
|
|
274
|
+
} catch { throw denied(); }
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
278
|
+
if (process.argv.length !== 4 || process.argv[2] !== '--workspace-root') {
|
|
279
|
+
process.stderr.write('HELDOUT_SNAPSHOT_REQUIRED\n');
|
|
280
|
+
process.exitCode = 1;
|
|
281
|
+
} else {
|
|
282
|
+
captureConfiguredHeldOutResultSnapshot({ workspaceRoot: process.argv[3] }).then(
|
|
283
|
+
result => process.stdout.write(`${JSON.stringify(result)}\n`),
|
|
284
|
+
() => { process.stderr.write('HELDOUT_SNAPSHOT_REQUIRED\n'); process.exitCode = 1; });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/** Recheck retained held-out runs outside the benchmark executor process.
|
|
2
|
+
* This diagnostic cannot issue a trusted benchmark attestation. */
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { constants } from 'node:fs';
|
|
6
|
+
import { lstat, open, opendir, realpath, rm } from 'node:fs/promises';
|
|
7
|
+
import os from 'node:os';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { loadHeldOutCorpus } from './gofer-heldout-corpus.mjs';
|
|
11
|
+
import { loadTrustedHeldOutCorpus } from './gofer-trusted-evaluator.mjs';
|
|
12
|
+
import { inspectHeldOutResultSnapshot, loadPinnedHeldOutSnapshot,
|
|
13
|
+
materializeHeldOutResultSnapshot } from './gofer-heldout-snapshot.mjs';
|
|
14
|
+
|
|
15
|
+
const sha = value => createHash('sha256').update(value).digest('hex');
|
|
16
|
+
const denied = () => new Error('HELDOUT_VERIFIER_REQUIRED');
|
|
17
|
+
const inside = (root, target) => {
|
|
18
|
+
const relative = path.relative(root, target);
|
|
19
|
+
return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));
|
|
20
|
+
};
|
|
21
|
+
const safeName = value => typeof value === 'string' && /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(value);
|
|
22
|
+
const safeRelative = value => typeof value === 'string' && value.length > 0 &&
|
|
23
|
+
!path.posix.isAbsolute(value) && !value.includes('\\') &&
|
|
24
|
+
!value.split('/').some(segment => !segment || segment === '.' || segment === '..');
|
|
25
|
+
const safeScope = value => safeRelative(typeof value === 'string' ? value.replace(/\/$/, '') : value);
|
|
26
|
+
const allowed = (file, scopes) => scopes.some(scope =>
|
|
27
|
+
file === scope.replace(/\/$/, '') || file.startsWith(scope.endsWith('/') ? scope : `${scope}/`));
|
|
28
|
+
|
|
29
|
+
async function readRegular(filePath, maxBytes) {
|
|
30
|
+
const handle = await open(filePath, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
31
|
+
try {
|
|
32
|
+
const info = await handle.stat();
|
|
33
|
+
if (!info.isFile() || info.nlink !== 1 || info.size < 1 || info.size > maxBytes) throw denied();
|
|
34
|
+
return await handle.readFile();
|
|
35
|
+
} finally { await handle.close(); }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function readJson(filePath, maxBytes = 2 * 1024 * 1024) {
|
|
39
|
+
return JSON.parse((await readRegular(filePath, maxBytes)).toString('utf8'));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function snapshot(worktree, input) {
|
|
43
|
+
const sourceFiles = input?.files;
|
|
44
|
+
const scopes = input?.allowedWriteScope;
|
|
45
|
+
if (!sourceFiles || typeof sourceFiles !== 'object' || Array.isArray(sourceFiles) ||
|
|
46
|
+
!Array.isArray(scopes) || !scopes.length || !scopes.every(safeScope) ||
|
|
47
|
+
!Object.keys(sourceFiles).every(safeRelative) ||
|
|
48
|
+
!Object.hasOwn(sourceFiles, 'verify.mjs') || allowed('verify.mjs', scopes)) throw denied();
|
|
49
|
+
const found = new Map();
|
|
50
|
+
const pending = [''];
|
|
51
|
+
let totalBytes = 0;
|
|
52
|
+
while (pending.length) {
|
|
53
|
+
const relative = pending.pop();
|
|
54
|
+
const directory = await opendir(path.join(worktree, relative));
|
|
55
|
+
for await (const entry of directory) {
|
|
56
|
+
const name = relative ? `${relative}/${entry.name}` : entry.name;
|
|
57
|
+
if (name === '.git') continue;
|
|
58
|
+
if (!safeRelative(name)) throw denied();
|
|
59
|
+
const entryPath = path.join(worktree, name);
|
|
60
|
+
const info = await lstat(entryPath);
|
|
61
|
+
if (info.isDirectory()) { pending.push(name); continue; }
|
|
62
|
+
if (!info.isFile() || info.nlink !== 1 || found.size >= 5000) throw denied();
|
|
63
|
+
const bytes = await readRegular(entryPath, 2 * 1024 * 1024);
|
|
64
|
+
totalBytes += bytes.length;
|
|
65
|
+
if (totalBytes > 16 * 1024 * 1024 ||
|
|
66
|
+
(!Object.hasOwn(sourceFiles, name) && !allowed(name, scopes)) ||
|
|
67
|
+
(Object.hasOwn(sourceFiles, name) && !allowed(name, scopes) &&
|
|
68
|
+
bytes.toString('utf8') !== sourceFiles[name])) throw denied();
|
|
69
|
+
found.set(name, sha(bytes));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (Object.keys(sourceFiles).some(name => !found.has(name) && !allowed(name, scopes))) throw denied();
|
|
73
|
+
return sha(JSON.stringify([...found].sort(([left], [right]) => left.localeCompare(right))));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function runSandboxCheck(worktree) {
|
|
77
|
+
if (inside('/Users', path.resolve(process.execPath))) throw denied();
|
|
78
|
+
const profile = '(version 1) (allow default) (deny network*) (deny file-write*) '
|
|
79
|
+
+ '(deny file-read* (subpath "/Users") (subpath "/Volumes"))';
|
|
80
|
+
const result = spawnSync('/usr/bin/sandbox-exec', ['-p', profile, process.execPath, 'verify.mjs'], {
|
|
81
|
+
cwd: worktree, encoding: 'utf8', timeout: 20000, maxBuffer: 65536,
|
|
82
|
+
env: { PATH: '/usr/bin:/bin:/opt/homebrew/bin', HOME: '/nonexistent', NODE_OPTIONS: '' },
|
|
83
|
+
});
|
|
84
|
+
if (result.error || result.signal || !Number.isInteger(result.status) ||
|
|
85
|
+
/EPERM|operation not permitted|sandbox-exec:/i.test(result.stderr ?? '')) throw denied();
|
|
86
|
+
return result.status === 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Shared with the benchmark executor so both use the same sandbox and scope rules.
|
|
90
|
+
export { runSandboxCheck as runHeldOutSandboxCheck, snapshot as snapshotHeldOutWorktree };
|
|
91
|
+
|
|
92
|
+
/** The report and receipts may be mutable. Only the fresh sandbox result is
|
|
93
|
+
* authoritative for this diagnostic; the output is never routing authority. */
|
|
94
|
+
export async function recheckHeldOutBenchmark({ corpusRoot, workspaceRoot,
|
|
95
|
+
receiptsRoot, worktreesRoot } = {}) {
|
|
96
|
+
if (process.platform !== 'darwin' || !path.isAbsolute(corpusRoot ?? '') ||
|
|
97
|
+
!path.isAbsolute(workspaceRoot ?? '')) throw denied();
|
|
98
|
+
try {
|
|
99
|
+
const corpus = await loadHeldOutCorpus({ corpusRoot, workspaceRoot });
|
|
100
|
+
const root = await realpath(corpusRoot);
|
|
101
|
+
const workspace = await realpath(workspaceRoot);
|
|
102
|
+
const temporary = await realpath(os.tmpdir());
|
|
103
|
+
const receipts = receiptsRoot ? await realpath(receiptsRoot) : path.join(root, 'receipts');
|
|
104
|
+
const saved = await readJson(path.join(receipts, 'benchmark-report.json'));
|
|
105
|
+
const report = saved?.report;
|
|
106
|
+
if (saved.corpusHash !== corpus.corpusHash || report?.schemaVersion !== 2 ||
|
|
107
|
+
report.repetitions !== 3 || report.caseCount !== corpus.cases.length ||
|
|
108
|
+
!Array.isArray(report.runs) || report.runs.length !== corpus.cases.length * 3 ||
|
|
109
|
+
report.provenance?.corpusHash !== corpus.corpusHash) throw denied();
|
|
110
|
+
const cases = new Map(corpus.cases.map(item => [item.id, item]));
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
const worktrees = new Set();
|
|
113
|
+
const executions = new Set();
|
|
114
|
+
const verifications = new Set();
|
|
115
|
+
const checks = [];
|
|
116
|
+
let costUsd = 0;
|
|
117
|
+
let durationMs = 0;
|
|
118
|
+
for (const run of report.runs) {
|
|
119
|
+
if (!safeName(run?.caseId) || ![1, 2, 3].includes(run.run) ||
|
|
120
|
+
!cases.has(run.caseId) || seen.has(`${run.caseId}:${run.run}`)) throw denied();
|
|
121
|
+
seen.add(`${run.caseId}:${run.run}`);
|
|
122
|
+
const benchmarkCase = cases.get(run.caseId);
|
|
123
|
+
if (run.modelId !== report.provenance?.modelId) throw denied();
|
|
124
|
+
if (run.inputHash !== sha(JSON.stringify({ caseId: run.caseId, input: benchmarkCase.input }))) throw denied();
|
|
125
|
+
const label = `${run.caseId}-${run.run}`;
|
|
126
|
+
const execution = await readJson(path.join(receipts, `${label}.execution.json`));
|
|
127
|
+
const verification = await readJson(path.join(receipts, `${label}.verification.json`));
|
|
128
|
+
const { executionReceipt, ...payload } = execution;
|
|
129
|
+
if (sha(JSON.stringify(payload)) !== executionReceipt || executionReceipt !== run.receipt ||
|
|
130
|
+
execution.id !== run.caseId || execution.run !== run.run ||
|
|
131
|
+
verification.caseId !== run.caseId || verification.run !== run.run ||
|
|
132
|
+
verification.inputHash !== run.inputHash ||
|
|
133
|
+
verification.executionReceipt !== run.receipt ||
|
|
134
|
+
verification.receipt !== run.verifierReceipt ||
|
|
135
|
+
verification.passed !== run.functionalVerified ||
|
|
136
|
+
execution.native?.isolation !== 'git-worktree+local-os-sandbox') throw denied();
|
|
137
|
+
if (!path.isAbsolute(execution.worktree ?? '')) throw denied();
|
|
138
|
+
const worktree = await realpath(worktreesRoot ? path.join(worktreesRoot, label) : execution.worktree);
|
|
139
|
+
const info = await lstat(worktree);
|
|
140
|
+
if (!inside(temporary, worktree) || inside(workspace, worktree) || inside(root, worktree) ||
|
|
141
|
+
!info.isDirectory() || info.uid !== process.getuid() || (info.mode & 0o077) !== 0 ||
|
|
142
|
+
worktrees.has(worktree) || executions.has(executionReceipt) ||
|
|
143
|
+
verifications.has(verification.receipt) ||
|
|
144
|
+
!Number.isFinite(run.costUsd) || run.costUsd < 0 ||
|
|
145
|
+
!Number.isFinite(run.durationMs) || run.durationMs < 0) throw denied();
|
|
146
|
+
worktrees.add(worktree);
|
|
147
|
+
executions.add(executionReceipt);
|
|
148
|
+
verifications.add(verification.receipt);
|
|
149
|
+
costUsd += run.costUsd;
|
|
150
|
+
durationMs += run.durationMs;
|
|
151
|
+
const before = await snapshot(worktree, benchmarkCase.input);
|
|
152
|
+
const passed = runSandboxCheck(worktree);
|
|
153
|
+
const after = await snapshot(worktree, benchmarkCase.input);
|
|
154
|
+
if (before !== after || passed !== run.functionalVerified) throw denied();
|
|
155
|
+
checks.push(Object.freeze({ caseId: run.caseId, run: run.run, passed,
|
|
156
|
+
executionReceipt, verifierReceipt: verification.receipt, snapshotHash: after }));
|
|
157
|
+
}
|
|
158
|
+
const functionalPasses = checks.filter(item => item.passed).length;
|
|
159
|
+
if (report.functionalPasses !== functionalPasses || report.functionalRuns !== checks.length ||
|
|
160
|
+
report.status !== (functionalPasses === checks.length ? 'pass' : 'fail') ||
|
|
161
|
+
Math.abs(report.reliability - functionalPasses / checks.length) > 1e-12 ||
|
|
162
|
+
Math.abs(report.costUsd - costUsd) > 1e-9 || report.durationMs !== durationMs) throw denied();
|
|
163
|
+
return Object.freeze({ schemaVersion: 1, authority: 'diagnostic-only',
|
|
164
|
+
corpusHash: corpus.corpusHash, reportHash: sha(JSON.stringify(report)),
|
|
165
|
+
checks: Object.freeze(checks), functionalPasses,
|
|
166
|
+
functionalRuns: checks.length });
|
|
167
|
+
} catch { throw denied(); }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Recheck only the captured bytes. The original worktrees may have changed
|
|
171
|
+
* or disappeared; their retained receipt paths are never executed. */
|
|
172
|
+
export async function recheckHeldOutSnapshot({ corpusRoot, workspaceRoot, trustRoot, snapshotId } = {}) {
|
|
173
|
+
let materialized;
|
|
174
|
+
try {
|
|
175
|
+
const before = await inspectHeldOutResultSnapshot({ trustRoot, workspaceRoot, snapshotId });
|
|
176
|
+
const corpus = await loadHeldOutCorpus({ corpusRoot, workspaceRoot });
|
|
177
|
+
if (before.corpusHash !== corpus.corpusHash) throw denied();
|
|
178
|
+
materialized = await materializeHeldOutResultSnapshot({ trustRoot, workspaceRoot, snapshotId });
|
|
179
|
+
const result = await recheckHeldOutBenchmark({ corpusRoot, workspaceRoot,
|
|
180
|
+
receiptsRoot: path.join(materialized.root, 'receipts'),
|
|
181
|
+
worktreesRoot: path.join(materialized.root, 'worktrees') });
|
|
182
|
+
const after = await inspectHeldOutResultSnapshot({ trustRoot, workspaceRoot, snapshotId });
|
|
183
|
+
if (before.reportSha256 !== after.reportSha256 || before.snapshotId !== after.snapshotId) throw denied();
|
|
184
|
+
return Object.freeze({ ...result, snapshotId, source: 'protected-snapshot' });
|
|
185
|
+
} catch { throw denied(); }
|
|
186
|
+
finally { if (materialized) await rm(materialized.root, { recursive: true, force: true }); }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Production entrypoint: the account trust root chooses the corpus. */
|
|
190
|
+
export async function recheckConfiguredHeldOutBenchmark({ workspaceRoot } = {}) {
|
|
191
|
+
try {
|
|
192
|
+
const corpus = await loadTrustedHeldOutCorpus({ workspaceRoot });
|
|
193
|
+
const trustRoot = path.dirname(path.dirname(corpus.corpusRoot));
|
|
194
|
+
const snapshot = await loadPinnedHeldOutSnapshot({ trustRoot, workspaceRoot,
|
|
195
|
+
corpusHash: corpus.corpusHash });
|
|
196
|
+
return await recheckHeldOutSnapshot({ corpusRoot: corpus.corpusRoot, workspaceRoot,
|
|
197
|
+
trustRoot, snapshotId: snapshot.snapshotId });
|
|
198
|
+
} catch { throw denied(); }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
202
|
+
const workspaceFlag = process.argv.indexOf('--workspace-root');
|
|
203
|
+
if (workspaceFlag !== 2 || process.argv.length !== 4) {
|
|
204
|
+
process.stderr.write('HELDOUT_VERIFIER_REQUIRED\n');
|
|
205
|
+
process.exitCode = 1;
|
|
206
|
+
} else {
|
|
207
|
+
recheckConfiguredHeldOutBenchmark({ workspaceRoot: process.argv[workspaceFlag + 1] }).then(
|
|
208
|
+
result => process.stdout.write(`${JSON.stringify(result)}\n`),
|
|
209
|
+
() => { process.stderr.write('HELDOUT_VERIFIER_REQUIRED\n'); process.exitCode = 1; });
|
|
210
|
+
}
|
|
211
|
+
}
|