@milpa/comal 0.1.7 → 0.2.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.
Files changed (134) hide show
  1. package/README.md +19 -1
  2. package/backend/contracts/remote-commands-v1.json +12 -0
  3. package/governance/chrome-task-v10.json +323 -0
  4. package/governance/chrome-task-v11.json +355 -0
  5. package/governance/chrome-task-v12.json +387 -0
  6. package/governance/chrome-task-v13.json +419 -0
  7. package/governance/chrome-task-v3.json +99 -0
  8. package/governance/chrome-task-v4.json +131 -0
  9. package/governance/chrome-task-v5.json +163 -0
  10. package/governance/chrome-task-v6.json +195 -0
  11. package/governance/chrome-task-v7.json +227 -0
  12. package/governance/chrome-task-v8.json +259 -0
  13. package/governance/chrome-task-v9.json +291 -0
  14. package/governance/source-task-v1.json +1 -0
  15. package/governance/source-task-v2.json +1 -0
  16. package/package.json +14 -1
  17. package/src/agent-recovery-proof.mjs +12 -1
  18. package/src/agent.mjs +94 -30
  19. package/src/audit.mjs +21 -6
  20. package/src/auto-guard.mjs +48 -1
  21. package/src/chrome-candidate-recovery-proof.mjs +47 -0
  22. package/src/chrome-recovery-proof.mjs +6 -2
  23. package/src/chrome-source-syntax.mjs +93 -0
  24. package/src/cli.mjs +158 -54
  25. package/src/codex-provider.mjs +3 -2
  26. package/src/connection-settings.mjs +204 -0
  27. package/src/context-pressure.mjs +2 -1
  28. package/src/durable-agent-runs.mjs +5 -11
  29. package/src/event-channels.mjs +8 -0
  30. package/src/file-recovery-proof.mjs +11 -3
  31. package/src/file-recovery.mjs +24 -0
  32. package/src/gemini-provider.mjs +6 -1
  33. package/src/git-agent-revision.mjs +12 -0
  34. package/src/git-agent-workspaces.mjs +143 -0
  35. package/src/git-integration-lineage.mjs +20 -0
  36. package/src/git-integration-operations.mjs +200 -0
  37. package/src/git-integration-recovery-proof.mjs +70 -0
  38. package/src/git-page-cli-bridge.mjs +61 -0
  39. package/src/git-page-evidence.mjs +30 -0
  40. package/src/git-page-identity.mjs +10 -0
  41. package/src/git-page-proof.mjs +96 -0
  42. package/src/git-page-settlement.mjs +21 -0
  43. package/src/git-page-verification.mjs +134 -0
  44. package/src/git-plumbing.mjs +212 -0
  45. package/src/git-process-worker.mjs +50 -0
  46. package/src/git-source.mjs +110 -0
  47. package/src/git-workspaces.mjs +519 -0
  48. package/src/goals.mjs +13 -7
  49. package/src/house.mjs +246 -20
  50. package/src/i18n/cli-es.mjs +4 -0
  51. package/src/i18n/es.mjs +35 -0
  52. package/src/judges.mjs +27 -11
  53. package/src/mcp-runtime.mjs +15 -3
  54. package/src/model-controls.mjs +11 -1
  55. package/src/model-deadline.mjs +4 -3
  56. package/src/native-refusal.mjs +38 -0
  57. package/src/onboarding.mjs +88 -24
  58. package/src/openai-provider.mjs +14 -7
  59. package/src/operations.mjs +9 -2
  60. package/src/output-recovery.mjs +48 -0
  61. package/src/peer-tools.mjs +18 -8
  62. package/src/permission-controls.mjs +232 -0
  63. package/src/permission-rules.mjs +312 -0
  64. package/src/permissions.mjs +113 -30
  65. package/src/plugin-catalog.mjs +84 -0
  66. package/src/plugin-config.mjs +48 -6
  67. package/src/plugin-controls.mjs +332 -0
  68. package/src/plugin-notices.mjs +88 -0
  69. package/src/plugin-trust.mjs +104 -0
  70. package/src/plugins/chrome-lab/bridge.mjs +38 -12
  71. package/src/plugins/chrome-lab/client.mjs +103 -25
  72. package/src/plugins/chrome-lab/diagnostics.mjs +19 -0
  73. package/src/plugins/chrome-lab/index.mjs +116 -26
  74. package/src/plugins/chrome-lab/project.mjs +30 -2
  75. package/src/plugins/chrome-lab/scoped-input.mjs +135 -0
  76. package/src/plugins/chrome-lab/worker-diagnostics.mjs +51 -0
  77. package/src/plugins/open-relay/actions.mjs +205 -0
  78. package/src/plugins/open-relay/bound.mjs +47 -0
  79. package/src/plugins/open-relay/client.mjs +158 -0
  80. package/src/plugins/open-relay/dossier.mjs +119 -0
  81. package/src/plugins/open-relay/index.mjs +153 -0
  82. package/src/plugins/open-relay/lookup.mjs +45 -0
  83. package/src/plugins/open-relay/notices.mjs +79 -0
  84. package/src/plugins/open-relay/watch.mjs +127 -0
  85. package/src/plugins.mjs +72 -29
  86. package/src/private-fs.mjs +46 -0
  87. package/src/process-lock.mjs +24 -13
  88. package/src/provider-auth.mjs +14 -3
  89. package/src/provider.mjs +11 -2
  90. package/src/read-only-check.mjs +7 -3
  91. package/src/scope-ceiling.mjs +56 -0
  92. package/src/scoped-policy.mjs +39 -0
  93. package/src/secret-login.mjs +68 -0
  94. package/src/session-bus.mjs +41 -7
  95. package/src/session-projections-candidate.mjs +119 -0
  96. package/src/session-projections.mjs +119 -0
  97. package/src/session-read-cache.mjs +23 -3
  98. package/src/sessions.mjs +35 -11
  99. package/src/source-editor-role.mjs +13 -0
  100. package/src/source-ownership.mjs +76 -0
  101. package/src/source-proposal-proof.mjs +55 -0
  102. package/src/source-proposals.mjs +118 -0
  103. package/src/source-requests.mjs +148 -0
  104. package/src/source-task-proof.mjs +24 -0
  105. package/src/source-task-revision-proof.mjs +44 -0
  106. package/src/subagents.mjs +187 -87
  107. package/src/tool-packs.mjs +5 -5
  108. package/src/tools.mjs +82 -15
  109. package/src/trusted-context.mjs +5 -1
  110. package/src/tui/connection-transition.mjs +1 -1
  111. package/src/tui/controller.mjs +8 -4
  112. package/src/tui/discovery.mjs +24 -12
  113. package/src/tui/inbound-blocks.mjs +55 -0
  114. package/src/tui/present.mjs +504 -0
  115. package/src/tui/screen.mjs +49 -13
  116. package/src/windows-private.ps1 +63 -0
  117. package/src/work-assertions.mjs +6 -2
  118. package/src/work-plan.mjs +34 -8
  119. package/src/work-state.mjs +15 -9
  120. package/src/workflows/chrome-page-cases.mjs +156 -0
  121. package/src/workflows/chrome-page-checkpoint-reader.mjs +10 -0
  122. package/src/workflows/chrome-page-diagnostics.mjs +113 -0
  123. package/src/workflows/chrome-page-law.mjs +48 -0
  124. package/src/workflows/chrome-page-recovery-host-v13.mjs +14 -0
  125. package/src/workflows/chrome-page-recovery-v13.mjs +104 -0
  126. package/src/workflows/chrome-page-task.mjs +429 -0
  127. package/src/workflows/chrome-page-verifier.mjs +123 -0
  128. package/src/workflows/chrome-source.mjs +73 -0
  129. package/src/workflows/chrome-supplied-inputs.mjs +43 -0
  130. package/src/workflows/chrome-task-adoption.mjs +122 -0
  131. package/src/workflows/chrome-task.mjs +379 -74
  132. package/src/workflows/source-task-v1.mjs +124 -0
  133. package/src/workflows/source-task-v2.mjs +227 -0
  134. package/src/workflows/source-task.mjs +13 -0
@@ -0,0 +1,20 @@
1
+ import {digest} from './operations.mjs';
2
+ import {validPageRecipeReferences} from './git-page-proof.mjs';
3
+
4
+ const fail=()=>Object.assign(new Error('The original integration revision lineage does not verify.'),{code:'GIT_REVISION_BINDING'});
5
+ // Pure historical binding. Native resource/source validity still belongs to the
6
+ // kernel; these links do not manufacture authority or attest to current files.
7
+ export function integrationLineage(rows,intent){
8
+ const chain=[],seen=new Set();let current=intent;
9
+ while(current){
10
+ const index=rows.findIndex(row=>row.audit?.sha256===current.audit?.sha256&&digest(row)===digest(current)),b=current.binding,k=b?.resolved?.kernel_input;
11
+ if(index<0||seen.has(index)||chain.length>=32||current.type!=='git_integration_intent'||current.source!=='git_integration_host'||current.mechanism!=='comal-git-integration-v1'||current.operation!=='GitIntegrationPrepare'||current.input_digest!==digest(current.input)||current.binding_digest!==digest(b)||current.owner_digest!==digest(b.owner)||b.owner.session_id!==current.session_id||!k||k.request_id!==current.input.request_id||k.target!==current.input.target||digest(k.verification)!==digest(current.input.verification)||!Array.isArray(k.offers)||!Array.isArray(current.input.run_ids))throw fail();
12
+ seen.add(index);chain.unshift(current);const revision=b.resolved.revision;
13
+ if(!validPageRecipeReferences(k.verification,b.resolved.page_recipes))throw fail();
14
+ if(!revision){if(current.input.revision_of||k.offers.length!==current.input.run_ids.length)throw fail();break;}
15
+ const link=revision.origin,parent=rows[link?.row],s=revision.source,previous=parent?.binding?.resolved?.kernel_input;
16
+ if(!Number.isSafeInteger(link?.row)||link.row>=index||parent?.audit?.sha256!==link.digest||parent.binding_digest!==link.binding_digest||parent.owner_digest!==current.owner_digest||parent.cwd!==current.cwd||parent.session_id!==current.session_id||parent.binding.goal_owner!==b.goal_owner||s?.schema!=='comal-source-revision-v1'||s.integration_id!==current.input.revision_of||parent.binding.resolved.integration_id!==s.integration_id||s.owner_digest!==current.owner_digest||current.input.run_ids?.length!==1||current.input.run_ids[0]!==revision.run_id||k.request_id!==current.input.request_id||k.target!==current.input.target||digest(k.verification)!==digest(current.input.verification)||s.target!==k.target||s.expected_target!==k.expected_target||s.base_commit!==k.base_commit||s.verification_digest!==digest(k.verification)||k.target!==previous.target||k.expected_target!==previous.expected_target||k.base_commit!==previous.base_commit||digest(k.verification)!==digest(previous.verification)||!Array.isArray(s.remaining_offers)||digest(k.offers)!==digest([revision.offer,...s.remaining_offers]))throw fail();
17
+ current=parent;
18
+ }
19
+ return chain;
20
+ }
@@ -0,0 +1,200 @@
1
+ import {realpath} from 'node:fs/promises';
2
+ import {digest} from './operations.mjs';
3
+ import {GitWorkspaces,gitWorkspaceResourceId} from './git-workspaces.mjs';
4
+ import {inspectGitSource} from './git-source.mjs';
5
+ import {readGit} from './git-plumbing.mjs';
6
+ import {WorkspaceTools} from './tools.mjs';
7
+ import {capturePolicyCeiling,scopedPolicyStack} from './scoped-policy.mjs';
8
+ import {scopeCeiling,validateScopeCeiling} from './scope-ceiling.mjs';
9
+ import {trustedContext} from './trusted-context.mjs';
10
+ import {auditExecution} from './audit.mjs';
11
+ import {assertionSchema} from './work-assertions.mjs';
12
+ import {readEvidence} from './work-state.mjs';
13
+ import {integrationLineage} from './git-integration-lineage.mjs';
14
+ import {readPageEvidence,pageEvidenceReferences,publicPageResult,publicPageError} from './git-page-evidence.mjs';
15
+ import {hostCodeIdentity} from './git-page-identity.mjs';
16
+ import {isPageVerification,pageVerificationSchema} from './git-page-proof.mjs';
17
+
18
+ export const GIT_INTEGRATION_MECHANISM='comal-git-integration-v1';
19
+ const source='git_integration_host',fail=(message,code='GIT_INTEGRATION_BINDING')=>Object.assign(new Error(message),{code});
20
+ const id={type:'string',pattern:'^[a-f0-9-]{36}$'},token={type:'string',minLength:1,maxLength:120},attempt={...token,description:'Optional deliberate check/apply attempt identity. Reuse for a lost reply; a known failed check requires an explicit new attempt.'};
21
+ // This redundant Unicode-universal pattern preserves the exact string/length
22
+ // contract. Factoring avoids the local GBNF compiler's 2000-repetition boundary;
23
+ // native min/max lengths and every other check constraint remain in place.
24
+ const commandPattern=String.raw`^(?:[^\n]|\n){1,1000}(?:[^\n]|\n){0,1000}$`;
25
+ const commandVerification={type:'object',additionalProperties:false,required:['criterion','checks'],properties:{criterion:{type:'string',minLength:1,maxLength:4000},checks:{type:'array',minItems:1,maxItems:8,items:{type:'object',additionalProperties:false,required:['id','command'],properties:{id:{type:'string',pattern:'^[a-zA-Z0-9_.:-]{1,80}$'},command:{type:'string',minLength:1,maxLength:2000,pattern:commandPattern},timeout:{type:'integer',minimum:1,maximum:120000},assertions:{type:'array',maxItems:8,items:assertionSchema}}}}}};
26
+ const verification=commandVerification;
27
+ const schema=(properties,required=[])=>({type:'object',additionalProperties:false,properties,required});
28
+ const effects={workspace:'write',external:'none',process:'write',authority:'none'};
29
+ export const gitIntegrationDefinitions=[
30
+ {name:'GitIntegrationPrepare',description:`${GIT_INTEGRATION_MECHANISM}. Prepare a combined candidate from 1–3 completed direct-child Git Agent run_ids. House derives exact offers, base and reserved target version. Optional revision_of accepts exactly one native revision editor for that original integration; target/checks must equal the original and its target version/base are never refreshed. Prefer GitIntegrationRevise for this smaller correction call. request_id freezes the intent; reuse ONLY after a lost reply. target is a logical refs/comal/integrations name, never a checkout branch. Declared checks run later through every ancestor/current native permission. Conflicts refuse; no human checkout changes.`,input_schema:schema({request_id:token,target:{type:'string',pattern:'^[a-z0-9][a-z0-9_.-]{0,79}$'},run_ids:{type:'array',minItems:1,maxItems:3,uniqueItems:true,items:id},verification,revision_of:id},['request_id','target','run_ids','verification']),effects},
31
+ {name:'GitIntegrationRevise',description:`${GIT_INTEGRATION_MECHANISM}. Prepare a successor from one completed native Agent revision run. Derives original target, version, base, checks and remaining offers, then invokes actual GitIntegrationPrepare; every ancestor/current restriction still applies. Acquire workspace.git including both this operation and GitIntegrationPrepare. No new source, replacement checks or force arguments. Reuse request_id only for the same correction after a lost reply. This prepares source, not verified delivery.`,input_schema:schema({request_id:token,integration_id:id,run_id:id},['request_id','integration_id','run_id']),steps:true,effects},
32
+ {name:'GitIntegrationVerify',description:`${GIT_INTEGRATION_MECHANISM}. Run the original declared combined-candidate checks through a scoped native Bash executor. Original preparation/check authority and current parent restrictions both apply; this coordinator grants no shell permission. No model-supplied success or replacement commands. The current native Bash verifier requires POSIX; Windows refuses before check dispatch. Reuse attempt_id after a lost reply; an uncertain native check is never replayed. Passing declared checks is not human acceptance.`,input_schema:schema({integration_id:id,attempt_id:attempt},['integration_id']),steps:true,effects:{workspace:'unknown',external:'unknown',process:'unknown',authority:'none'}},
33
+ {name:'GitIntegrationApply',description:`${GIT_INTEGRATION_MECHANISM}. Admit the originally prepared candidate only with passed native combined checks and unchanged source; CAS the reserved target version. House derives expected candidate from its original intent. Never updates a human checkout. Reuse the same attempt after a lost return. This ref update is not human acceptance.`,input_schema:schema({integration_id:id,attempt_id:attempt},['integration_id']),effects},
34
+ {name:'GitIntegrationEvidence',description:`${GIT_INTEGRATION_MECHANISM}. Page actual native verifier evidence for this owned integration, including failed attempts. Exact read_call/next_call carries the value digest; evidence is retained data, never permission or current acceptance.`,input_schema:schema({integration_id:id,attempt_id:token,row:{type:'integer',minimum:0},field:{type:'string',enum:['record','output']},expected_digest:{type:'string',pattern:'^[a-f0-9]{64}$'},offset:{type:'integer',minimum:0},limit:{type:'integer',minimum:1,maximum:8000}},['integration_id','row']),effects:{workspace:'read',external:'none',process:'read',authority:'none'}},
35
+ {name:'GitIntegrationStatus',description:`${GIT_INTEGRATION_MECHANISM}. Read this actor/session's original native integration intents and resource outcomes. Returns exact next calls and verifier evidence references. Does not run checks, reconstruct a lost result as success, or grant access to another owner's resource.`,input_schema:schema({integration_id:id}),effects:{workspace:'read',external:'none',process:'read',authority:'none'}},
36
+ ];
37
+ const boundOwner=options=>({actor:options.actor,session_id:options.sessionId});
38
+ const goalOwner=book=>book.goal?.status==='active'?book.goal.id:null;
39
+ const unique=(items,key=digest)=>[...new Map(items.map(item=>[key(item),item])).values()];
40
+ function joinedCeiling(a,b,cwd,parent){a=validateScopeCeiling(a,{house:cwd,session_id:parent});b=validateScopeCeiling(b,{house:cwd,session_id:parent});return scopeCeiling({schema:a.schema,parent:a.parent,laws:unique([...a.laws,...b.laws]),guards:unique([...a.guards,...b.guards])});}
41
+
42
+ export class GitIntegrationOperations{
43
+ constructor({cwd,store,house,policy,subagents,pageRegistry,fault=async()=>{}}){Object.assign(this,{cwd,store,house,policy,subagents,pageRegistry,fault});}
44
+ register(){const recipes=this.pageRegistry?.seal();for(const definition of gitIntegrationDefinitions){
45
+ const selected=this.pageRegistry&&definition.name==='GitIntegrationPrepare'?{...definition,description:definition.description+' Alternative: verification.checks=[{id,kind:"page",recipe}] selects one host-registered page recipe. Available public recipe metadata: '+JSON.stringify(recipes)+'. Verifier implementation/criteria are not model-supplied.',input_schema:{...definition.input_schema,properties:{...definition.input_schema.properties,verification:{oneOf:[commandVerification,{...pageVerificationSchema,properties:{...pageVerificationSchema.properties,checks:{...pageVerificationSchema.properties.checks,items:{...pageVerificationSchema.properties.checks.items,properties:{...pageVerificationSchema.properties.checks.items.properties,recipe:{...pageVerificationSchema.properties.checks.items.properties.recipe,enum:recipes.map(r=>r.name)}}}}}}]}}}}:definition;
46
+ this.house.registry.add({...selected,...(this.pageRegistry&&definition.name==='GitIntegrationApply'?{effects:{workspace:'unknown',external:'unknown',process:'unknown',authority:'none'},description:definition.description+' Page checks require a fresh original read-only ChromeTest before CAS and authorized owned-resource cleanup afterward; native leaf authority is judged separately.'}:{}),...(this.pageRegistry&&definition.name==='GitIntegrationVerify'?{description:definition.description+' With a registered page check, runs the pre-reserved original ChromePublish/ChromeTest case chain instead of Bash; the named trusted host recipe and original/current native authority govern every child.'}:{}),revision:GIT_INTEGRATION_MECHANISM,run:(input,options)=>this.execute(definition.name,input,options)});
47
+ }return this;}
48
+ async book(options){const book=await this.store.load(options.sessionId);if(!book.journalAudit.verified||book.cwd!==await realpath(this.cwd)||!options.actor?.id)throw fail('A verified native caller session/actor is required.');return book;}
49
+ intent(book,row){if(row?.type!=='git_integration_intent'||row.source!==source||row.mechanism!==GIT_INTEGRATION_MECHANISM||row.session_id!==book.id||row.cwd!==book.cwd||row.input_digest!==digest(row.input)||row.binding_digest!==digest(row.binding)||row.owner_digest!==digest(row.binding.owner)||row.binding.owner.session_id!==book.id)throw fail('The original integration intent does not verify.');return row;}
50
+ async preparation(options,integrationId){const book=await this.book(options),matches=book.rows.filter(row=>row.type==='git_integration_intent'&&row.operation==='GitIntegrationPrepare'&&row.binding?.resolved.integration_id===integrationId&&row.owner_digest===digest(boundOwner(options)));if(matches.length!==1)throw fail('No original integration intent belongs to this actor/session.','GIT_INTEGRATION_NOT_FOUND');return this.intent(book,matches[0]);}
51
+ async lineage(options,intent){return integrationLineage((await this.book(options)).rows,intent);}
52
+ key(name,input){return name==='GitIntegrationPrepare'?name+':'+input.request_id:name+':'+input.integration_id+':'+(input.attempt_id||(name==='GitIntegrationVerify'?'declared-checks':'apply'));}
53
+ async prepareCommand(name,input,options){
54
+ return this.store.workTransaction(options.sessionId,async()=>{
55
+ const book=await this.book(options),owner=boundOwner(options),key=this.key(name,input),prior=book.rows.find(row=>row.type==='git_integration_intent'&&row.command_key===key&&row.owner_digest===digest(owner));let intent;
56
+ if(prior){intent=this.intent(book,prior);if(prior.operation!==name||prior.input_digest!==digest(input))throw fail('This command identity already names another intent. Use a new request/attempt ID.','GIT_REQUEST_CONFLICT');}
57
+ else{
58
+ let resolved;
59
+ if(name==='GitIntegrationPrepare'&&input.revision_of){
60
+ if(input.run_ids.length!==1)throw fail('A successor requires exactly one native revision editor.','GIT_REVISION_BINDING');
61
+ const original=await this.preparation(options,input.revision_of),chain=await this.lineage(options,original);if(chain.length>=32)throw fail('The bounded native revision lineage is full. No new candidate was dispatched.','GIT_REVISION_LIMIT');await this.frame(options,chain);
62
+ const previous=original.binding.resolved.kernel_input;
63
+ if(input.target!==previous.target||digest(input.verification)!==digest(previous.verification))throw fail('A successor cannot replace its original target or declared checks.','GIT_REVISION_CONTRACT');
64
+ const run=await this.subagents.resolve({run_id:input.run_ids[0]},options.sessionId);
65
+ if(run.status!=='completed'||!run.result_ref||run.task.git_revision!==input.revision_of)throw fail('The selected run is not a completed revision of this original integration.','GIT_REVISION_BINDING');
66
+ await this.subagents.gitWorkspaces.validate(run,book);
67
+ const source=run.workspace.revision_source,kernel=new GitWorkspaces({cwd:this.cwd});await kernel.validateRevisionSource(source,{owner,signal:options.signal});
68
+ const child=await this.subagents.childEvidence(run.session_id,book),published=this.subagents.gitWorkspaces.sourceOffer(run,child,run.result_ref);if(!published)throw fail('The selected correction has no native source offer.','GIT_SOURCE_OFFER_REQUIRED');
69
+ const offer={workspace_id:published.workspace_id,offer_id:published.offer_id},integration_id=gitWorkspaceResourceId(this.cwd,owner,{kind:'integration',request_id:input.request_id});
70
+ resolved={integration_id,kernel_input:{request_id:input.request_id,target:previous.target,expected_target:previous.expected_target,base_commit:previous.base_commit,offers:[offer,...source.remaining_offers],verification:previous.verification},revision:{origin:{row:book.rows.findIndex(r=>r.audit.sha256===original.audit.sha256),digest:original.audit.sha256,binding_digest:original.binding_digest},source,offer,run_id:run.run_id},...(original.binding.resolved.page_recipes?{page_recipes:original.binding.resolved.page_recipes}: {})};
71
+ }else if(name==='GitIntegrationPrepare'){
72
+ const kernel=new GitWorkspaces({cwd:this.cwd});await kernel.validateIntegrationTarget(input.target,options.signal);const observed=await inspectGitSource({cwd:this.cwd,signal:options.signal});await kernel.initialize();
73
+ const expected=await kernel.rootRef('refs/comal/integrations/'+input.target,options.signal),base=expected||observed.head;if(!base)throw fail('Integration needs an existing base commit. Commit the initial project before combining source.','GIT_BASE_REQUIRED');
74
+ const offers=[];for(const runId of input.run_ids){const run=await this.subagents.resolve({run_id:runId},options.sessionId);if(run.task.git_revision)throw fail('A revision offer requires the original-contract successor port; ordinary preparation cannot refresh its target/checks.','GIT_REVISION_SUCCESSOR_REQUIRED');const child=await this.subagents.childEvidence(run.session_id,book);if(run.status!=='completed'||!run.workspace||!run.result_ref)throw fail('Every selected run must have a completed native Git source result.','GIT_SOURCE_OFFER_REQUIRED');const offer=this.subagents.gitWorkspaces.sourceOffer(run,child,run.result_ref);if(!offer)throw fail('The selected result has no native source offer.','GIT_SOURCE_OFFER_REQUIRED');offers.push({workspace_id:offer.workspace_id,offer_id:offer.offer_id});}
75
+ const integration_id=gitWorkspaceResourceId(this.cwd,owner,{kind:'integration',request_id:input.request_id});resolved={integration_id,kernel_input:{request_id:input.request_id,target:input.target,expected_target:expected||'absent',base_commit:base,offers,verification:kernel.verificationContract(input.verification)}};
76
+ if(isPageVerification(input.verification)){if(!this.pageRegistry)throw fail('No host page recipe registry is installed.','GIT_PAGE_RECIPE');resolved.page_recipes=this.pageRegistry.references(input.verification);}
77
+ }else{
78
+ const original=await this.preparation(options,input.integration_id);resolved={integration_id:input.integration_id,prepare_digest:original.binding_digest,attempt_id:input.attempt_id||(name==='GitIntegrationVerify'?'declared-checks':'apply')};
79
+ }
80
+ const inherited=trustedContext(book.rows),binding={owner,resolved,authority:capturePolicyCeiling(this.policy,inherited.intentions),ceiling:this.house.captureScopeCeiling(options.sessionId),inherited,goal_owner:goalOwner(book)};
81
+ intent=await this.store.append(book.id,{type:'git_integration_intent',source,mechanism:GIT_INTEGRATION_MECHANISM,cwd:book.cwd,session_id:book.id,actor:options.actor,owner_digest:digest(owner),command_key:key,operation:name,input:structuredClone(input),input_digest:digest(input),binding,binding_digest:digest(binding)});
82
+ }
83
+ const current=await this.store.load(book.id),existing=current.rows.filter(row=>row.type==='git_integration_command_prepared'&&row.invocation_id===options.invocation_id),marker={type:'git_integration_command_prepared',source,mechanism:GIT_INTEGRATION_MECHANISM,invocation_id:options.invocation_id,session_id:book.id,actor:options.actor,operation:name,input_digest:digest(input),intent_ref:current.rows.findIndex(row=>row.audit.sha256===intent.audit.sha256),intent_digest:intent.audit.sha256};
84
+ if(existing.length>1||existing.length&&digest({...existing[0],timestamp:undefined,audit:undefined})!==digest(marker))throw fail('The native command invocation already names another original intent.');
85
+ if(!existing.length)await this.store.append(book.id,marker);return intent;
86
+ });
87
+ }
88
+ async frame(options,intents){
89
+ if(typeof options.revalidate!=='function')throw fail('The admitted native effect frame is unavailable.');await options.revalidate();const book=await this.book(options);
90
+ for(const intent of intents){if(intent.owner_digest!==digest(boundOwner(options)))throw fail('Integration owner changed.');if(intent.binding.authority.denied.includes(options.command_name)||intent.binding.authority.mode==='plan'&&['GitIntegrationPrepare','GitIntegrationApply'].includes(options.command_name))throw fail('An original integration permission ceiling refuses '+options.command_name+'. A changed human scope requires a new preparation intent.','HOUSE_REFUSED');if(intent.binding.goal_owner!==goalOwner(book))throw fail('The original integration budget owner changed. No new effect was dispatched under the changed goal.','GIT_GOAL_CHANGED');}
91
+ const ceilings=unique(intents.map(intent=>intent.binding.ceiling));for(let i=0;i<ceilings.length;i+=2)await options.revalidate({ceilings:ceilings.slice(i,i+2)});
92
+ }
93
+ kernel(options,intents){return new GitWorkspaces({cwd:this.cwd,fault:(phase,value)=>this.fault('kernel:'+phase,value),beforeEffect:()=>this.frame(options,intents),verifierIdentity:hostCodeIdentity(this,GitIntegrationOperations),verifierFactory:context=>this.verifier(context,options,intents)});}
94
+ async verifier(context,options,intents){
95
+ const command=intents.at(-1),original=intents.findLast(i=>i.operation==='GitIntegrationPrepare');await this.frame(options,intents);const{cwd,store,session_id:sessionId,owner}=context,kernel=this.kernel(options,intents),state=await kernel.status(context.resource_id,{owner}),prepared=state.completed.find(row=>row.action==='integration_prepare')?.output;
96
+ if(!prepared||prepared.worktree!==cwd||context.resource_id!==original.binding.resolved.integration_id)throw fail('The native verifier belongs to another prepared source resource.');
97
+ const book=await store.load(sessionId);let house,authority;const merged=intents.map(i=>i.binding.ceiling).reduce((a,b)=>joinedCeiling(a,b,this.cwd,options.sessionId));
98
+ if(book.rows.some(row=>row.type==='house_scope_bound')){
99
+ let parent=this.house;
100
+ if(context.page&&command.operation==='GitIntegrationApply'){
101
+ // Host-only constraint carrier: it executes no operation and invents no
102
+ // parent receipt. Existing forks apply these actual original constraints
103
+ // to every child leaf as well as every current root constraint.
104
+ parent=this.house.fork({cwd:this.cwd,record:row=>this.store.append(row.session_id,row),parentSessionId:options.sessionId,sessionId:options.sessionId,ceiling:merged});
105
+ authority={format:'comal-git-page-authority/1',resource_id:context.resource_id,reservation_digest:digest(context.page.reservation),admission_digest:digest(context.page.admission),call_id:context.page.admission.call_id,owner_digest:digest(owner),original_scope_id:book.rows.find(r=>r.type==='house_scope_bound').scope_id,merged_ceiling:merged,captures:intents.map(i=>({operation:i.operation,intent_digest:i.audit.sha256,binding_digest:i.binding_digest,house:i.cwd,session_id:i.session_id,ceiling_digest:i.binding.ceiling.digest,authority_digest:digest(i.binding.authority)}))};
106
+ await store.append(sessionId,{type:'git_page_authority_bound',source:'git_page_host',authority,authority_digest:digest(authority)});
107
+ }
108
+ house=await parent.restoreFork({cwd,store,parentSessionId:options.sessionId,sessionId});
109
+ }
110
+ else{if(book.rows.some(row=>row.type==='house_proposal'))throw fail('A verifier scope cannot be invented around existing effects.');house=this.house.fork({cwd,store,record:row=>store.append(row.session_id,row),parentSessionId:options.sessionId,sessionId,ceiling:intents.map(i=>i.binding.ceiling).reduce((a,b)=>joinedCeiling(a,b,this.cwd,options.sessionId))});}
111
+ const check=async()=>{
112
+ await this.frame(options,intents);const live=await kernel.status(context.resource_id,{owner});if(live.lifecycle!=='retained')throw fail('The original integration resource was retired.','GIT_RESOURCE_RETIRED');
113
+ const head=(await readGit(cwd,['rev-parse','--verify','HEAD'])).stdout.toString().trim(),branch=(await readGit(cwd,['rev-parse','--symbolic-full-name','HEAD'])).stdout.toString().trim(),common=await realpath((await readGit(cwd,['rev-parse','--path-format=absolute','--git-common-dir'])).stdout.toString().trim());
114
+ if(head!==prepared.candidate_commit||branch!=='refs/heads/comal/work/'+context.resource_id||common!==live.spec.repository.common_directory)throw fail('The original verifier source identity changed.','GIT_VERIFICATION_SOURCE_CHANGED');
115
+ if(context.page){const source=await inspectGitSource({cwd,signal:context.signal});if(source.dirty||source.source_identity!==prepared.source_identity)throw fail('The original page candidate changed.','GIT_VERIFICATION_SOURCE_CHANGED');}
116
+ await this.frame(options,intents);return true;
117
+ };
118
+ house.guard('git-integration-source',check,{revision:GIT_INTEGRATION_MECHANISM,configuration_digest:digest({resource:context.resource_id,candidate:prepared.candidate_commit,prepare:original.binding_digest,command:command.binding_digest,...(intents.length>2?{lineage:intents.map(i=>i.binding_digest)}:{})})});
119
+ const policy=scopedPolicyStack({parent:this.policy,bindings:intents.map(i=>i.binding),cwd,onDecision:row=>store.append(sessionId,row)});
120
+ const authorize=policy.authorize.bind(policy);policy.authorize=async(name,input,authorization)=>intents.some(intent=>intent.binding.authority.denied.includes(name))?{allowed:false,reason:'The original saved integration permission ceiling denies '+name+'. Changing current rules does not replace that intent.',source:'original_integration_ceiling'}:authorize(name,input,authorization);
121
+ const tools=new WorkspaceTools({cwd,store,house,policy,sessionId,actor:owner.actor,onPermission:row=>store.append(sessionId,{type:'permission',...row}),frontier:check});
122
+ if(context.page){if(!this.pageRegistry)throw fail('The original page registry is unavailable.','GIT_PAGE_RECIPE');this.pageRegistry.assert(original.binding.resolved.page_recipes,original.input.verification);const page=await this.pageRegistry.adapter({context,tools,house,policy,...context.page});return{store,tools,session_id:sessionId,page:{...page,authority}};}
123
+ return{store,tools,session_id:sessionId};
124
+ }
125
+ async verificationBook(integrationId,attemptId,options){
126
+ const original=await this.preparation(options,integrationId),kernel=new GitWorkspaces({cwd:this.cwd});await kernel.initialize();const context=await kernel.context(integrationId),{book}=await kernel.load(context,boundOwner(options)),prepared=book.rows.find(row=>row.type==='git_action_returned'&&row.action==='integration_prepare')?.output,descriptor=book.rows.findLast(row=>row.type==='git_action_phase'&&row.action==='integration_verify'&&row.phase==='verification_session'&&(!attemptId||row.attempt===attemptId));
127
+ if(!prepared||!descriptor)throw fail('No native verifier session exists for this original attempt.','GIT_VERIFIER_NOT_FOUND');const native=await kernel.nativeVerificationSession(descriptor.value,prepared.worktree),audit=auditExecution(native);
128
+ if(!audit.journal.verified||audit.violations.length||descriptor.value.binding.owner_digest!==original.owner_digest||descriptor.value.binding.resource_id!==integrationId||descriptor.value.binding.candidate_commit!==prepared.candidate_commit)throw fail('Native verifier evidence does not belong to this original source/owner.');
129
+ return{kernel,book,native,attempt_id:descriptor.attempt,page:isPageVerification(original.input.verification)};
130
+ }
131
+ async evidence(input,options){const{native,attempt_id,page}=await this.verificationBook(input.integration_id,input.attempt_id,options),value=page?readPageEvidence(native,input):readEvidence(native,input);if(value.next_call)value.next_call={name:'GitIntegrationEvidence',input:{...value.next_call.input,integration_id:input.integration_id,attempt_id}};if(value.next_call)delete value.next_call.input.session_id;return{...value,integration_id:input.integration_id,attempt_id};}
132
+ async status(input,options){
133
+ const book=await this.book(options),owner=boundOwner(options),intents=book.rows.filter(row=>row.type==='git_integration_intent'&&row.operation==='GitIntegrationPrepare'&&row.owner_digest===digest(owner)&&(!input.integration_id||row.binding.resolved.integration_id===input.integration_id));if(input.integration_id&&!intents.length)throw fail('No original integration belongs to this actor/session.','GIT_INTEGRATION_NOT_FOUND');
134
+ const integrations=[];for(const row of intents){const intent=this.intent(book,row),integrationId=intent.binding.resolved.integration_id,kernel=new GitWorkspaces({cwd:this.cwd});let state;try{state=await kernel.status(integrationId,{owner});}catch(error){if(error.code!=='ENOENT'&&error.code!=='GIT_RESOURCE_UNINITIALIZED')throw error;}
135
+ let conflicts=[],failedPrepare;if(state){const resource=await kernel.context(integrationId),nativeResource=await kernel.load(resource,owner);conflicts=nativeResource.book.rows.filter(row=>row.type==='git_action_phase'&&row.action==='integration_prepare'&&row.phase.startsWith('conflict:')).map(row=>row.value);failedPrepare=nativeResource.book.rows.findLast(row=>row.type==='git_action_rejected'&&row.action==='integration_prepare');}
136
+ const prepared=state?.completed.find(row=>row.action==='integration_prepare')?.output,applied=state?.completed.findLast(row=>row.action==='integration_apply')?.output;let nativeState,latestCheck,evidence=[];
137
+ if(prepared){try{nativeState=await this.verificationBook(integrationId,undefined,options);latestCheck=nativeState.book.rows.findLast(row=>['git_action_returned','git_action_rejected'].includes(row.type)&&row.action==='integration_verify'&&row.attempt===nativeState.attempt_id);evidence=nativeState.native.rows.flatMap((receipt,index)=>receipt.type==='house_receipt'||receipt.type==='house_started'&&!nativeState.native.rows.some(row=>row.type==='house_receipt'&&row.invocation_id===receipt.invocation_id)?[{operation:receipt.operation,status:receipt.status||'unsettled_not_success',read_call:{name:'GitIntegrationEvidence',input:{integration_id:integrationId,attempt_id:nativeState.attempt_id,row:index,field:receipt.type==='house_receipt'?'output':'record',expected_digest:readEvidence(nativeState.native,{row:index,field:receipt.type==='house_receipt'?'output':'record'}).digest}}}]:[]);}catch(error){if(error.code!=='GIT_VERIFIER_NOT_FOUND')throw error;}}
138
+ if(isPageVerification(intent.input.verification)&&nativeState)evidence=pageEvidenceReferences(nativeState.native).map(({row,field,expected_digest,...entry})=>({...entry,read_call:{name:'GitIntegrationEvidence',input:{integration_id:integrationId,attempt_id:nativeState.attempt_id,row,field,expected_digest}}}));
139
+ const verified=latestCheck?.type==='git_action_returned'?latestCheck.output:undefined,pending=state?.pending||[];let next;
140
+ if(pending.length){const p=pending[0],operation={integration_prepare:'GitIntegrationPrepare',integration_verify:'GitIntegrationVerify',integration_apply:'GitIntegrationApply'}[p.action],saved=book.rows.find(row=>row.type==='git_integration_intent'&&row.operation===operation&&row.owner_digest===intent.owner_digest&&row.binding?.resolved.integration_id===integrationId&&(operation==='GitIntegrationPrepare'||row.binding.resolved.attempt_id===p.attempt));next=saved?{name:operation,input:this.intent(book,saved).input}:null;}
141
+ else next=!prepared?{name:'GitIntegrationPrepare',input:intent.input}:!verified?{name:'GitIntegrationVerify',input:{integration_id:integrationId,...(latestCheck?{attempt_id:'declared-checks-'+(state.completed.filter(r=>r.action==='integration_verify').length+state.rejected.filter(r=>r.action==='integration_verify').length+1)}:{})}}:!applied?{name:'GitIntegrationApply',input:{integration_id:integrationId,...(state.rejected.some(row=>row.action==='integration_apply')?{attempt_id:'apply-'+(state.rejected.filter(row=>row.action==='integration_apply').length+1)}:{})}}:null;
142
+ let continuation;
143
+ if(!prepared&&!pending.length&&state?.lifecycle!=='retired_preserved'&&failedPrepare?.code==='GIT_MERGE_CONFLICT'){
144
+ // A typed error alone is not correction source. Reuse the same original
145
+ // tree/parents/failure proof that the native revision editor requires.
146
+ const revision=await kernel.revisionSource(integrationId,{owner,signal:options.signal});
147
+ if(revision.kind!=='merge_conflict'||revision.failure_ref!==failedPrepare.audit.sha256)throw fail('The original merge correction no longer matches its native conflict.','GIT_REVISION_SOURCE_INVALID');
148
+ continuation={status:'source_correction_required',reason:'merge_conflict',original_attempt_id:failedPrepare.attempt,failure_ref:revision.failure_ref,source_ref:revision.source_ref,next_action:'Use Agent with isolation:"git", work_tracking:"parent" and git_revision:"'+integrationId+'" to resolve the original conflict, then GitIntegrationRevise. Original target, base, checks and remaining offers are retained; original/current authority is checked by those operations.',scope:'original_native_merge_conflict_not_current_acceptance_or_authority'};next=null;
149
+ }
150
+ if(isPageVerification(intent.input.verification)&&!verified&&!pending.length&&state?.lifecycle!=='retired_preserved'&&latestCheck?.type==='git_action_rejected'&&latestCheck.evidence?.scope==='native_page_needs_correction'){
151
+ // Use the same original reservation/Case/Behavior/receipt proof required
152
+ // by the revision route. A code or error message alone is not this fact.
153
+ const revision=await kernel.revisionSource(integrationId,{owner,signal:options.signal});
154
+ if(revision.failure_ref!==latestCheck.audit.sha256)throw fail('The original page correction no longer matches its native failure.','GIT_REVISION_SOURCE_INVALID');
155
+ continuation={status:'source_correction_required',original_attempt_id:latestCheck.attempt,receipt_id:latestCheck.evidence.invocation_id,receipt_digest:latestCheck.evidence.receipt_digest,next_action:'Use Agent with isolation:"git", work_tracking:"parent" and git_revision:"'+integrationId+'" to correct source, then GitIntegrationRevise. Original/current authority and resource state are checked by those operations.',scope:'original_failed_page_check_not_current_acceptance_or_cleanup_authority'};next=null;
156
+ }
157
+ if(state?.lifecycle==='retired_preserved'||!prepared&&state?.rejected.some(row=>row.action==='integration_prepare')||process.platform==='win32'&&!isPageVerification(intent.input.verification)&&next?.name==='GitIntegrationVerify')next=null;
158
+ integrations.push({integration_id:integrationId,target:intent.input.target,lifecycle:state?.lifecycle||'intent_saved',candidate:prepared?.candidate_commit||null,verification:verified?.status||(latestCheck?'failed_declared_checks':'not_passed'),applied:applied?.applied_commit||null,pending,rejected:state?.rejected||[],conflicts,verification_executor:{available:isPageVerification(intent.input.verification)||process.platform!=='win32',kind:isPageVerification(intent.input.verification)?'host_reserved_native_page':'native_posix_bash',...(process.platform==='win32'&&!isPageVerification(intent.input.verification)?{reason:'The current native Bash paths are not supported on Windows. No verification check was dispatched.'}:{})},next_call:next,...(continuation?{continuation}:{}),evidence,asked:'unjudged',scope:'native_source_and_declared_checks_not_human_acceptance'});
159
+ }return{integrations};
160
+ }
161
+ async execute(name,input,options){
162
+ options={...options,command_name:name};
163
+ if(name==='GitIntegrationStatus')return JSON.stringify(await this.status(input,options));
164
+ if(name==='GitIntegrationEvidence')return JSON.stringify(await this.evidence(input,options));
165
+ if(name==='GitIntegrationRevise'){
166
+ const original=await this.preparation(options,input.integration_id),chain=await this.lineage(options,original),k=original.binding.resolved.kernel_input;await this.frame(options,chain);
167
+ if(typeof options.invoke!=='function')throw fail('A revision must dispatch the admitted native Prepare operation.');
168
+ const prepare_input={request_id:input.request_id,target:k.target,run_ids:[input.run_id],verification:k.verification,revision_of:input.integration_id},book=await this.book(options);
169
+ await options.recordWork({type:'git_revision_call_prepared',source,mechanism:GIT_INTEGRATION_MECHANISM,input:structuredClone(input),input_digest:digest(input),prepare_input,origin:{row:book.rows.findIndex(r=>r.audit.sha256===original.audit.sha256),digest:original.audit.sha256,binding_digest:original.binding_digest}});
170
+ await this.fault('revision_call_prepared',{input,options});const result=await options.invoke('GitIntegrationPrepare',prepare_input);await this.fault('revision_child_returned',{input,result,options});await this.frame(options,chain);return result;
171
+ }
172
+ const command=await this.prepareCommand(name,input,options),original=name==='GitIntegrationPrepare'?command:await this.preparation(options,input.integration_id),chain=await this.lineage(options,original),intents=name==='GitIntegrationPrepare'?chain:[...chain,command];
173
+ if(isPageVerification(original.input.verification)){
174
+ if(!this.pageRegistry)throw fail('The original page registry is unavailable.','GIT_PAGE_RECIPE');this.pageRegistry.assert(original.binding.resolved.page_recipes,original.input.verification);
175
+ if(name==='GitIntegrationApply'){const probe=this.kernel(options,intents),state=await probe.status(input.integration_id,{owner:boundOwner(options)}),verified=state.completed.findLast(r=>r.action==='integration_verify');if(verified){const saved=(await this.book(options)).rows.find(r=>r.type==='git_integration_intent'&&r.operation==='GitIntegrationVerify'&&r.owner_digest===original.owner_digest&&r.binding.resolved.integration_id===input.integration_id&&r.binding.resolved.attempt_id===verified.attempt);if(!saved)throw fail('Original page verification authority is unavailable.');intents.splice(intents.length-1,0,this.intent(await this.book(options),saved));}}
176
+ }
177
+ const resolved=command.binding.resolved,release=isPageVerification(original.input.verification)?this.pageRegistry.acquire(resolved.integration_id):()=>{};let result;
178
+ try{
179
+ await this.fault('intent_saved',{command,options});await this.frame(options,intents);const kernel=this.kernel(options,intents),owner=boundOwner(options);
180
+ try{
181
+ if(name==='GitIntegrationPrepare')result=resolved.revision?await kernel.prepareRevision({request_id:resolved.kernel_input.request_id,source:resolved.revision.source,offer:resolved.revision.offer},{owner,signal:options.signal,pageRecipes:isPageVerification(original.input.verification)?this.pageRegistry.assert(resolved.page_recipes,original.input.verification):undefined}):await kernel.prepareIntegration(resolved.kernel_input,{owner,signal:options.signal,pageRecipes:isPageVerification(original.input.verification)?this.pageRegistry.assert(resolved.page_recipes,original.input.verification):undefined});
182
+ else if(name==='GitIntegrationVerify'){if(process.platform==='win32'&&!isPageVerification(original.input.verification))throw fail('The current native Bash verifier requires POSIX. No check was dispatched; native Windows shell verification needs its own supported port.','GIT_NATIVE_PLATFORM_UNSUPPORTED');result=await kernel.verifyIntegration(resolved.integration_id,{attempt_id:resolved.attempt_id},{owner,signal:options.signal});}
183
+ else{const state=await kernel.status(resolved.integration_id,{owner}),prepared=state.completed.find(row=>row.action==='integration_prepare')?.output;if(!prepared)throw fail('The original combined candidate has not settled.');result=await kernel.applyIntegration(resolved.integration_id,{attempt_id:resolved.attempt_id,expected_candidate:prepared.candidate_commit},{owner,signal:options.signal});
184
+ if(isPageVerification(original.input.verification)){const checked=state.completed.findLast(r=>r.action==='integration_verify');await this.pageRegistry.close(input.integration_id,checked.attempt);const proof=await this.verificationBook(input.integration_id,checked.attempt,options);if(!proof.native.rows.some(r=>r.type==='git_page_cleanup'&&r.source==='git_page_host'&&r.reservation_digest===digest(checked.output.page.reservation)&&r.owned_absent))throw fail('Applied ref is recorded, but original native cleanup has not been observed. No cleanup effect is replayed.','GIT_PAGE_CLEANUP_UNCERTAIN');}
185
+ }
186
+ }catch(error){
187
+ if(name==='GitIntegrationVerify'&&isPageVerification(original.input.verification)&&error.git_verification_failure)try{await this.pageRegistry.close(input.integration_id,resolved.attempt_id);}catch(cleanup){error.page_cleanup_error={code:cleanup.code||'GIT_PAGE_CLEANUP',message:cleanup.message};}
188
+ if(result?.applied_commit)error.git_applied_result=structuredClone(result);throw error;
189
+ }
190
+ await this.fault('kernel_returned',{command,result,options});await this.frame(options,intents);const visible=isPageVerification(original.input.verification)?publicPageResult(result):result;options.observe?.({kind:'git_integration_result',integration_id:visible.integration_id,operation:name,output_digest:digest(visible),scope:visible.scope});return JSON.stringify(visible);
191
+ }catch(error){throw isPageVerification(original.input.verification)?publicPageError(error):error;}finally{release();}
192
+ }
193
+ }
194
+
195
+ // Startup-only host entrypoint. Select this instead of default legacy Git
196
+ // registration, after recipes/native runtime factories have been registered.
197
+ export function installGitPageIntegration({workspace,subagents,registry,fault}){
198
+ if(!(workspace instanceof WorkspaceTools)||!workspace.store||!(registry?.seal instanceof Function)||!subagents)throw fail('Host page integration requires original WorkspaceTools, direct-child manager and recipe registry.','GIT_PAGE_RECIPE');
199
+ return new GitIntegrationOperations({cwd:workspace.cwd,store:workspace.store,house:workspace.house,policy:workspace.policy,subagents,pageRegistry:registry,...(fault?{fault}: {})}).register();
200
+ }
@@ -0,0 +1,70 @@
1
+ import {digest} from './operations.mjs';
2
+ import {integrationLineage} from './git-integration-lineage.mjs';
3
+ const mechanism='comal-git-integration-v1',source='git_integration_host',names=new Set(['GitIntegrationPrepare','GitIntegrationVerify','GitIntegrationApply','GitIntegrationRevise']);
4
+ export function nativeGitIntegration(rows,p){const c=rows.findLast(row=>row.type==='house_contract'&&row.contract_digest===p.contract_digest)?.contract;return names.has(p.operation)&&c?.name===p.operation&&c.origin===null&&c.revision===mechanism&&c.permission===p.operation&&c.steps===['GitIntegrationVerify','GitIntegrationRevise'].includes(p.operation)&&c.description.includes(mechanism)&&c.effects.authority==='none';}
5
+ function revisionCall(rows,p){
6
+ const markers=rows.filter(r=>r.type==='git_revision_call_prepared'&&r.invocation_id===p.invocation_id),children=rows.filter(r=>r.type==='house_proposal'&&r.parent_invocation_id===p.invocation_id);
7
+ if(!markers.length&&!children.length)return{undispatched:true};if(markers.length!==1||children.length>1)return null;
8
+ const marker=markers[0],origin=rows[marker.origin?.row],k=origin?.binding?.resolved?.kernel_input,input=marker.input;
9
+ if(marker.source!==source||marker.mechanism!==mechanism||marker.session_id!==p.session_id||digest(marker.actor)!==digest(p.actor)||marker.input_digest!==p.arguments_digest||digest(input)!==p.arguments_digest||!origin||marker.origin.row>=rows.indexOf(marker)||origin.audit.sha256!==marker.origin.digest||origin.binding_digest!==marker.origin.binding_digest||origin.owner_digest!==digest({actor:p.actor,session_id:p.session_id})||origin.binding.resolved.integration_id!==input.integration_id||digest(marker.prepare_input)!==digest({request_id:input.request_id,target:k.target,run_ids:[input.run_id],verification:k.verification,revision_of:input.integration_id}))return null;
10
+ const chain=integrationLineage(rows,origin),child=children[0];if(child&&(child.operation!=='GitIntegrationPrepare'||child.arguments_digest!==digest(marker.prepare_input)||!nativeGitIntegration(rows,child)||child.session_id!==p.session_id||child.house!==p.house||digest(child.actor)!==digest(p.actor)||rows.indexOf(child)<rows.indexOf(marker)))return null;
11
+ return{marker,origin,chain,child};
12
+ }
13
+ export function gitIntegrationRecoveryPlan(...args){try{return recoveryPlan(...args);}catch{return null;}}
14
+ function recoveryPlan(rows,p,index=rows.length){
15
+ const prefix=rows.slice(0,index);if(!nativeGitIntegration(prefix,p))return null;
16
+ if(p.operation==='GitIntegrationRevise'){
17
+ const call=revisionCall(prefix,p);if(!call)return null;
18
+ if(call.child&&!prefix.some(r=>r.type==='house_receipt'&&r.invocation_id===call.child.invocation_id))return null;
19
+ return{kind:'git_integration_revision_call',code:'HOUSE_RECOVERY_CONTINUE',effects_observed:'original_revision_child_outcome_not_wrapper_return',error:JSON.stringify({code:'HOUSE_RECOVERY_CONTINUE',operation:p.operation,original_handler_return:'unavailable',...(call.marker?{next_calls:[{name:p.operation,input:call.marker.input}]}:{}),scope:'recorded_composition_not_current_delivery',next_action:call.undispatched?'No native child was dispatched by this wrapper; re-propose its original input under current authority.':'Re-propose the original revision call under current authority. The exact Prepare intent reconciles its original candidate; no check, file effect or ref update was replayed during recovery.',asked:'unjudged'})};
20
+ }
21
+ const markers=prefix.filter(row=>row.type==='git_integration_command_prepared'&&row.invocation_id===p.invocation_id);if(markers.length>1)return null;
22
+ if(!markers.length)return{kind:'git_integration_not_dispatched',code:'HOUSE_RECOVERY_NOT_DISPATCHED',effects_observed:'no_native_git_dispatch_from_this_handler',error:JSON.stringify({code:'HOUSE_RECOVERY_NOT_DISPATCHED',operation:p.operation,executed:false,scope:'this_handler_only_not_prior_attempts',next_action:'No durable command marker exists, so this handler did not dispatch Git. Re-propose its original call under current authority; do not infer that prior attempts or other handlers had no effects.',asked:'unjudged'})};
23
+ const marker=markers[0],intent=prefix[marker.intent_ref];
24
+ if(marker.source!==source||marker.mechanism!==mechanism||marker.session_id!==p.session_id||marker.operation!==p.operation||digest(marker.actor)!==digest(p.actor)||marker.input_digest!==p.arguments_digest||!Number.isSafeInteger(marker.intent_ref)||marker.intent_ref>=prefix.indexOf(marker)||intent?.type!=='git_integration_intent'||intent.source!==source||intent.mechanism!==mechanism||intent.audit?.sha256!==marker.intent_digest||intent.operation!==p.operation||intent.input_digest!==p.arguments_digest||digest(intent.input)!==p.arguments_digest||intent.binding_digest!==digest(intent.binding)||intent.owner_digest!==digest({actor:p.actor,session_id:p.session_id})||digest(intent.binding.owner)!==intent.owner_digest||intent.session_id!==p.session_id||intent.cwd!==p.house)return null;
25
+ const resolved=intent.binding.resolved;
26
+ if(p.operation==='GitIntegrationPrepare'){
27
+ const k=resolved?.kernel_input;if(!k||k.request_id!==intent.input.request_id||k.target!==intent.input.target||digest(k.verification)!==digest(intent.input.verification)||!Array.isArray(k.offers)||!resolved.revision&&k.offers.length!==intent.input.run_ids.length)return null;
28
+ integrationLineage(prefix,intent);
29
+ }else{
30
+ const original=prefix.find(row=>row.type==='git_integration_intent'&&row.source===source&&row.mechanism===mechanism&&row.operation==='GitIntegrationPrepare'&&row.owner_digest===intent.owner_digest&&row.binding?.resolved.integration_id===intent.input.integration_id);
31
+ if(!original||original.binding_digest!==digest(original.binding)||resolved.prepare_digest!==original.binding_digest||resolved.integration_id!==intent.input.integration_id||resolved.attempt_id!==(intent.input.attempt_id||(p.operation==='GitIntegrationVerify'?'declared-checks':'apply')))return null;
32
+ integrationLineage(prefix,original);
33
+ }
34
+ return{kind:'git_integration_intent',code:'HOUSE_RECOVERY_CONTINUE',effects_observed:'registered_git_intent_not_verified_delivery',error:JSON.stringify({code:'HOUSE_RECOVERY_CONTINUE',operation:p.operation,original_handler_return:'unavailable',integration_id:resolved.integration_id,next_calls:[{name:p.operation,input:intent.input}],scope:'same_original_intent_not_current_verification_or_human_acceptance',next_action:'Continue the exact call under current authority. The host retains original base, target version, offers, checks and authority. Native kernel actions reconcile their original IDs; an uncertain check is never replayed.',asked:'unjudged'})};
35
+ }
36
+ export function gitIntegrationRecoveryValid(rows,index,row){const p=rows[row.recovery?.proposal_ref],plan=p&&gitIntegrationRecoveryPlan(rows,p,index);return Boolean(plan&&row.recovery.kind===plan.kind&&row.recovery.plan_digest===digest(plan)&&row.status==='refused'&&row.code===plan.code&&row.effects_observed===plan.effects_observed&&row.error===plan.error&&row.observations?.length===0&&row.asked==='unjudged');}
37
+
38
+ function auditConstraints(prefix,receipt,intents,fault){
39
+ const latestGoal=prefix.findLast(row=>row.type==='goal')?.goal,goalOwner=latestGoal?.status==='active'?latestGoal.id:null;
40
+ const decisions=prefix.filter(row=>row.type==='house_effect_constraint'&&row.source==='scope_host'&&row.invocation_id===receipt.invocation_id&&row.operation===receipt.operation&&row.arguments_digest===receipt.arguments_digest&&row.session_id===receipt.session_id&&row.house===receipt.house&&digest(row.actor)===digest(receipt.actor));
41
+ for(const prepared of intents){
42
+ if(prepared.binding.authority?.denied?.includes(receipt.operation)||prepared.binding.authority?.mode==='plan'&&['GitIntegrationPrepare','GitIntegrationApply'].includes(receipt.operation))fault(receipt,'git_original_permission_ceiling_refused');
43
+ if(prepared.binding.goal_owner!==goalOwner)fault(receipt,'git_original_goal_owner_changed');
44
+ const ceiling=prepared.binding.ceiling;if(!ceiling){fault(receipt,'git_original_constraint_descriptor_missing');continue;}
45
+ const expected=[...ceiling.guards.map(identity=>({kind:'guard',identity})),...ceiling.laws.flatMap(law=>law.plan.gates.filter(gate=>gate.tier==='enforced'&&(gate.operations.includes('*')||gate.operations.includes(receipt.operation))).map(gate=>({kind:'law',identity:{house:law.house,session_id:law.session_id,activation_id:law.activation_id,plan_hash:law.plan.plan_hash,mechanism_stamp:law.mechanism_stamp,gate_id:gate.id}})))];
46
+ for(const constraint of expected)if(!decisions.some(row=>row.ceiling_digest===ceiling.digest&&row.kind===constraint.kind&&digest(row.identity)===digest(constraint.identity)&&row.allowed===true))fault(receipt,'git_original_constraint_missing');
47
+ if(decisions.some(row=>row.ceiling_digest===ceiling.digest&&row.allowed===false))fault(receipt,'git_original_constraint_refused_before_return');
48
+ }
49
+ }
50
+
51
+ // Historical semantic checks only. Native source/process/ref attestation still
52
+ // belongs to the linked kernel/verifier books, not a success flag in this book.
53
+ export function auditGitIntegrationIntents(rows){
54
+ const violations=[],fault=(row,reason)=>violations.push({invocation_id:row.invocation_id,operation:row.operation,reason});
55
+ for(let index=0;index<rows.length;index++){
56
+ const receipt=rows[index];if(receipt.type!=='house_receipt'||receipt.status!=='returned'||!names.has(receipt.operation)||!nativeGitIntegration(rows.slice(0,index),receipt))continue;
57
+ try{
58
+ const prefix=rows.slice(0,index),proposal=prefix.find(row=>row.type==='house_proposal'&&row.invocation_id===receipt.invocation_id),plan=proposal&&gitIntegrationRecoveryPlan(rows,proposal,index);
59
+ if(receipt.operation==='GitIntegrationRevise'){
60
+ const call=proposal&&revisionCall(prefix,proposal),child=call?.child&&prefix.find(r=>r.type==='house_receipt'&&r.invocation_id===call.child.invocation_id);
61
+ if(!plan||!call?.marker||child?.status!=='returned'||child.output!==receipt.output||child.output_digest!==receipt.output_digest)fault(receipt,'git_revision_composition_binding_invalid');else auditConstraints(prefix,receipt,call.chain,fault);
62
+ continue;
63
+ }
64
+ if(!plan||plan.kind!=='git_integration_intent'){fault(receipt,'git_integration_intent_missing_or_invalid');continue;}
65
+ const marker=prefix.find(row=>row.type==='git_integration_command_prepared'&&row.invocation_id===receipt.invocation_id),intent=prefix[marker.intent_ref],original=intent.operation==='GitIntegrationPrepare'?intent:prefix.find(row=>row.type==='git_integration_intent'&&row.operation==='GitIntegrationPrepare'&&row.owner_digest===intent.owner_digest&&row.binding?.resolved.integration_id===intent.binding.resolved.integration_id),chain=integrationLineage(prefix,original),intents=intent===original?chain:[...chain,intent];
66
+ auditConstraints(prefix,receipt,intents,fault);
67
+ try{const output=JSON.parse(receipt.output),observation=receipt.observations?.find(row=>row.kind==='git_integration_result');if(output.integration_id!==original.binding.resolved.integration_id||observation?.integration_id!==output.integration_id||observation.operation!==receipt.operation||observation.output_digest!==digest(output)||observation.scope!==output.scope)fault(receipt,'git_native_result_binding_invalid');}catch{fault(receipt,'git_native_result_binding_invalid');} }catch{fault(receipt,'git_integration_intent_malformed');}
68
+ }
69
+ return{scope:'recorded_native_intent_constraints_not_live_resource_attestation',violations};
70
+ }
@@ -0,0 +1,61 @@
1
+ // Startup-only trusted Git page recipe and candidate runtime adapter.
2
+ import {readFile,lstat,realpath} from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import {createHash} from 'node:crypto';
5
+ import {digest,immutable} from './operations.mjs';
6
+ import {normalizeTask,savedChromeTask} from './workflows/chrome-task.mjs';
7
+ import {savedSourceTask} from './source-task-proof.mjs';
8
+ import {compileChromePageVerifier} from './workflows/chrome-page-verifier.mjs';
9
+ import {GitPageRegistry} from './git-page-verification.mjs';
10
+ import {installGitPageIntegration} from './git-integration-operations.mjs';
11
+ import {PluginRuntime} from './plugins.mjs';
12
+ import {House} from './house.mjs';
13
+ import {SessionStore} from './sessions.mjs';
14
+ import {createChromeLabPlugin} from './plugins/chrome-lab/index.mjs';
15
+ import {ChromeLabClient} from './plugins/chrome-lab/client.mjs';
16
+ const sha=b=>createHash('sha256').update(b).digest('hex');
17
+ const fail=message=>Object.assign(new Error(message),{code:'GIT_PAGE_CLI_SELECTION'});
18
+ const keys=(value,expected)=>{if(!value||typeof value!=='object'||Array.isArray(value)||Object.keys(value).sort().join()!==[...expected].sort().join())throw fail('Unsupported recipe-selection fields.');};
19
+ async function regular(file,max=262144){const s=await lstat(file);if(!s.isFile()||s.isSymbolicLink()||s.nlink!==1||s.size>max)throw fail('Recipe inputs must be bounded regular files, without symlinks/hardlinks.');const actual=await realpath(file);if(actual!==path.resolve(file))throw fail('Recipe input path must contain no symlink.');return readFile(file);}
20
+ export function assertGitPageCliEntry({selected,book={rows:[]},entryMode,explicitSourceTask,explicitChromeTask}){
21
+ const saved=book.rows.findLast(r=>r.type==='git_page_cli_recipe_selected'&&r.source==='git_page_cli_host');
22
+ if(!selected){if(saved)throw fail('Resume requires the same explicit trusted recipe selection.');return;}
23
+ if(entryMode!=='raw')throw fail('Page-recipe selection requires explicit --chrome-workflow raw.');
24
+ if(explicitSourceTask||explicitChromeTask||savedSourceTask(book)||savedChromeTask(book))throw fail('Page recipes cannot replace accepted or saved source/Chrome workflows.');
25
+ }
26
+ // One resource-owned client, installed afresh in each candidate House. Dependencies
27
+ // are host ports for offline tests, never selection/model fields.
28
+ export function gitPageCliRuntimeFactory({config,assertCurrent,Runtime=PluginRuntime,pluginFactory=createChromeLabPlugin,Client=ChromeLabClient}){
29
+ return async function candidateRuntime({cwd,resource_id,attempt_id,owner}){
30
+ await assertCurrent();const originalCwd=await realpath(cwd),runtimes=[],houses=new WeakSet();let client,closed=false;
31
+ return{async install({cwd,house,store,sessionId,signal}){
32
+ signal?.throwIfAborted();if(closed)throw fail('The original candidate transport is closed; no restart or effect replay.');
33
+ if(await realpath(cwd)!==originalCwd||houses.has(house))throw fail('Candidate runtime binding changed or was already installed.');
34
+ if(!(house instanceof House)||!(store instanceof SessionStore)||await realpath(house.cwd)!==originalCwd||await realpath(store.cwd)!==originalCwd)throw fail('Candidate House/store identity differs from the original workspace.');
35
+ const native=await store.load(sessionId),header=native.rows[0];if(!native.journalAudit.verified||header.type!=='session'||header.id!==sessionId||await realpath(header.cwd)!==originalCwd)throw fail('Original candidate session header or journal identity is invalid.');
36
+ await assertCurrent();const runtime=new Runtime({cwd:originalCwd,house,services:{'comal.workspace':Object.freeze({cwd:originalCwd})}});
37
+ runtime.register(pluginFactory({clientFactory:options=>client ||= new Client(options)}),{config});
38
+ try{await runtime.enable('chrome-lab');}catch(error){await runtime.close();throw error;}
39
+ runtimes.push(runtime);houses.add(house);
40
+ await store.append(sessionId,{type:'git_page_cli_native_runtime',source:'git_page_cli_host',cwd:originalCwd,resource_id,attempt_id,owner_digest:digest(owner)});
41
+ },async close(){closed=true;let error;for(const runtime of runtimes){try{await runtime.close();}catch(e){error ||= e;}}if(error)throw error;}};
42
+ };
43
+ }
44
+ export async function prepareGitPageCliBridge({selection,cwd,trusted=false,book={rows:[]},entryMode,explicitSourceTask,explicitChromeTask}){
45
+ assertGitPageCliEntry({selected:!!selection,book,entryMode,explicitSourceTask,explicitChromeTask});if(!selection)return null;
46
+ if(!trusted)throw fail('Explicit --trust-plugins is required for the selected host page recipe.');
47
+ const file=path.resolve(cwd,selection),selectionBytes=await regular(file,16384),value=JSON.parse(selectionBytes);
48
+ keys(value,['format','name','revision','purpose','task','verifier','fixture','chrome_config']);if(value.format!=='comal-git-page-cli-recipe/1')throw fail('Unknown startup recipe format.');
49
+ const captures=[{file,sha256:sha(selectionBytes)}],data={};
50
+ for(const name of['task','verifier','fixture','chrome_config']){keys(value[name],['path','sha256']);if(typeof value[name].path!=='string'||!value[name].path||!/^([a-f0-9]{64})$/.test(value[name].sha256))throw fail('Each selected input requires an exact path and SHA256.');const target=path.resolve(path.dirname(file),value[name].path),bytes=await regular(target);if(sha(bytes)!==value[name].sha256)throw fail('Selected '+name+' bytes changed.');captures.push({file:target,sha256:sha(bytes)});data[name]=bytes;}
51
+ const task=normalizeTask(JSON.parse(data.task)),config=immutable(JSON.parse(data.chrome_config));if(task.surface!=='page'||!task.verification?.cases?.length)throw fail('An explicit ordered page-case task is required.');
52
+ const verifier=compileChromePageVerifier(data.verifier.toString(),{trusted:true,file:captures[2].file});
53
+ if(task.verification.implementation_sha256!==verifier.descriptor.implementation_sha256||task.verification.contract_digest!==verifier.descriptor.contract_digest)throw fail('Selected verifier descriptor differs from the task.');
54
+ const assertCurrent=async()=>{for(const item of captures)if(sha(await regular(item.file))!==item.sha256)throw fail('An original selected input changed; no new native activation.');};
55
+ const implementation=sha(await readFile(new URL(import.meta.url))),configuration_digest=digest({captures,config,implementation}),registry=new GitPageRegistry();
56
+ registry.register({name:value.name,revision:value.revision,purpose:value.purpose,configuration_digest,fixture_sha256:sha(data.fixture),task,verifier,runtimeFactory:gitPageCliRuntimeFactory({config,assertCurrent})});
57
+ const catalog=registry.catalog(),binding=immutable({format:'comal-git-page-cli-binding/1',selection_sha256:sha(selectionBytes),configuration_digest,catalog});
58
+ const saved=book.rows.findLast(r=>r.type==='git_page_cli_recipe_selected'&&r.source==='git_page_cli_host');if(saved&&digest(saved.binding)!==digest(binding))throw fail('The original startup recipe binding changed.');
59
+ let installed=false;
60
+ return Object.freeze({catalog,binding,assertWorkflow({book,task,sourceTask}){assertGitPageCliEntry({selected:true,book,entryMode:'raw',explicitSourceTask:sourceTask,explicitChromeTask:task});},async install({workspace,subagents}){if(installed)throw fail('Startup recipe registration already occurred.');await assertCurrent();const operations=installGitPageIntegration({workspace,subagents,registry});installed=true;if(!saved)await workspace.store.append(typeof workspace.recoverySessionId==='function'?workspace.recoverySessionId():workspace.recoverySessionId,{type:'git_page_cli_recipe_selected',source:'git_page_cli_host',binding});return operations;},async close(){await registry.closeAll({cleanup:false});}});
61
+ }
@@ -0,0 +1,30 @@
1
+ // Keep trusted recipe criteria, callback contexts and source in the host audit.
2
+ // The model sees the SAME returned coordinator result, or allowlisted metadata
3
+ // pointing to an original failed receipt. Neither projection grants authority.
4
+ import {digest} from './operations.mjs';
5
+ import {readEvidence} from './work-state.mjs';
6
+ const fail=()=>Object.assign(new Error('This page evidence row is private host implementation/history. Use an advertised public coordinator result or failure-metadata read_call.'),{code:'GIT_PAGE_PRIVATE_EVIDENCE'});
7
+ export function readPageEvidence(native,input){
8
+ const row=native.rows[input.row];
9
+ if(row?.type==='house_receipt'&&row.operation==='ChromeTest'&&row.status==='returned'&&!row.output_truncated&&input.field==='output')return readEvidence(native,input);
10
+ if(row?.type!=='house_receipt'||row.status==='returned'||(input.field||'record')!=='record')throw fail();
11
+ const projected={type:'git_page_public_receipt',projection:'comal-git-page-public-evidence/1',operation:row.operation,invocation_id:row.invocation_id,status:row.status,error_code:row.error_code||row.code||null,effects_observed:row.effects_observed??null,original_receipt_sha256:row.audit.sha256};
12
+ const rows=[...native.rows];rows[input.row]=projected;return {...readEvidence({...native,rows},input),projection:'comal-git-page-public-evidence/1',original_receipt_sha256:row.audit.sha256};
13
+ }
14
+ export function pageEvidenceReferences(native){return native.rows.flatMap((row,index)=>{
15
+ const field=row.type==='house_receipt'&&row.operation==='ChromeTest'&&row.status==='returned'&&!row.output_truncated?'output':row.type==='house_receipt'&&row.status!=='returned'?'record':null;
16
+ if(!field)return[];return[{operation:row.operation,status:row.status,row:index,field,expected_digest:readPageEvidence(native,{row:index,field}).digest}];
17
+ });}
18
+
19
+ export function publicPageResult(result){
20
+ if(!result.page)return result;const {page,native_journal,...visible}=result;
21
+ return {...visible,receipts:result.receipts.map(({check_id,call_id,invocation_id,receipt_digest,output_digest,source_digest,task_digest,binding})=>({check_id,call_id,invocation_id,receipt_digest,output_digest,source_digest,task_digest,binding})),page:{format:'comal-git-page-result/1',recipe:{name:page.recipe.name,revision:page.recipe.revision,digest:page.recipe.digest},task_digest:page.reservation.task_digest,reservation_digest:digest(page.reservation),complete_cases:page.task.verification.cases.length,observation:page.public_result}};
22
+ }
23
+ export function publicPageError(error){
24
+ const code=typeof error.code==='string'&&/^(?:GIT_|CHROME_|HOUSE_|GOVERNANCE_|AUDIT_)[A-Z_]+$/.test(error.code)?error.code:'GIT_PAGE_HOST_FAILURE';
25
+ const details={code,...(error.git_verification_failure?.scope==='native_page_needs_correction'?{scope:'original_public_page_coordinator_result',observation:error.git_verification_failure.public_result,receipt_id:error.git_verification_failure.invocation_id,receipt_digest:error.git_verification_failure.receipt_digest}:{}),...(error.git_applied_result?{applied_result:publicPageResult(error.git_applied_result)}:{}),...(error.page_cleanup_error?{cleanup:{code:error.page_cleanup_error.code||'GIT_PAGE_CLEANUP_UNCERTAIN'}}:{})};
26
+ const message=error.git_applied_result?'The reserved ref was applied; owned cleanup has not completed.':code==='GIT_NATIVE_CHECK_FAILED'?'The original page check needs source correction through the original-contract revision route.':'Page integration did not complete. Original host audit is retained; uncertain effects are not replayed.';
27
+ // Generic resident error transport forwards message. Include only the same
28
+ // bounded public result here as well as details, without changing that transport.
29
+ return Object.assign(new Error(message+' '+JSON.stringify(details),{cause:error}),{code,details,...(error.git_applied_result?{git_applied_result:details.applied_result}:{})});
30
+ }
@@ -0,0 +1,10 @@
1
+ // Identify actually installed host methods as well as their original lexical
2
+ // class body. This is loaded-code identity, not a sandbox or closure attestation.
3
+ import {digest} from './operations.mjs';
4
+ export function hostCodeIdentity(instance,Type){
5
+ const methods=Object.keys(Object.getOwnPropertyDescriptors(Type.prototype)).sort().map(name=>{
6
+ const own=Object.getOwnPropertyDescriptor(instance,name),base=Object.getOwnPropertyDescriptor(Type.prototype,name),entry=own||base;
7
+ return [name,...['value','get','set'].map(key=>typeof entry[key]==='function'?Function.prototype.toString.call(entry[key]):null)];
8
+ });
9
+ return digest({class_body:Function.prototype.toString.call(Type),methods});
10
+ }
@@ -0,0 +1,96 @@
1
+ // Native journal proof for pre-reserved Git page checks. Never accepts a model's
2
+ // success flag or a retrospective source-hash match as verification.
3
+ import {createHash} from 'node:crypto';
4
+ import {auditExecution} from './audit.mjs';
5
+ import {digest} from './operations.mjs';
6
+ import {pageReceiptLaw} from './workflows/chrome-page-law.mjs';
7
+
8
+ const fail=message=>Object.assign(new Error(message),{code:'GIT_PAGE_EVIDENCE'});
9
+ export const pageCaseRevision=value=>digest({format:'comal-git-page-case/1',resource_id:value.resource_id,attempt_id:value.attempt_id,check_id:value.check_id,candidate_commit:value.candidate_commit,source_identity:value.source_identity,recipe_digest:value.recipe_digest,host_implementation:value.host_implementation,kernel_identity:value.kernel_identity});
10
+ const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
11
+ export const isPageVerification=verification=>verification?.checks?.length===1&&verification.checks[0].kind==='page';
12
+ export const pageCheckSchema={type:'object',additionalProperties:false,required:['id','kind','recipe'],properties:{id:{type:'string',pattern:'^[a-zA-Z0-9_.:-]{1,80}$'},kind:{const:'page'},recipe:{type:'string',pattern:'^[a-z][a-z0-9_-]{0,79}$'}}};
13
+ export const pageVerificationSchema={type:'object',additionalProperties:false,required:['criterion','checks'],properties:{criterion:{type:'string',minLength:1,maxLength:4000},checks:{type:'array',minItems:1,maxItems:1,items:pageCheckSchema}}};
14
+ export function validPageRecipes(verification,recipes){
15
+ if(!isPageVerification(verification))return recipes===undefined;
16
+ if(!Array.isArray(recipes)||recipes.length!==1||!recipes[0]||typeof recipes[0]!=='object')return false;const {digest:claimed,...body}=recipes[0];
17
+ return /^[a-f0-9]{64}$/.test(recipes[0].fixture_sha256||'')&&recipes[0].name===verification.checks[0].recipe&&claimed===digest(body)&&recipes[0].task?.surface==='page'&&Array.isArray(recipes[0].task.verification?.cases)&&recipes[0].task.verification.cases.length>0&&digest(recipes[0].verifier)===digest({implementation_sha256:recipes[0].task.verification.implementation_sha256,contract_digest:recipes[0].task.verification.contract_digest});
18
+ }
19
+ export function nativePrefix(native,count=native.rows.length){
20
+ const lines=native.bytes.toString('utf8').split(/(?<=\n)/);if(!Number.isSafeInteger(count)||count<1||count>native.rows.length)throw fail('Invalid native verification prefix.');
21
+ return {rows:count,sha256:sha(Buffer.from(lines.slice(0,count).join('')))};
22
+ }
23
+ export function assertNativePrefix(native,prefix){if(!prefix||digest(nativePrefix(native,prefix.rows))!==digest(prefix))throw fail('Original immutable native verification prefix changed.');}
24
+ function receipt(native,callId,name){
25
+ const proposals=native.rows.filter(r=>r.type==='house_proposal'&&r.call_id===callId),p=proposals[0];
26
+ const rows=p&&native.rows.filter(r=>r.type==='house_receipt'&&r.invocation_id===p.invocation_id),r=rows?.[0];
27
+ const contract=r&&native.rows.find(c=>c.type==='house_contract'&&c.contract_digest===r.contract_digest)?.contract;
28
+ if(proposals.length!==1||rows?.length!==1||p.operation!==name||r.status!=='returned'||r.output_truncated||r.output_digest!==digest(r.output)||r.arguments_digest!==digest({})||contract?.origin!==null||contract.name!==name||!contract.steps)throw fail('Original returned '+name+' owner is unavailable.');
29
+ return {proposal:p,receipt:r,value:JSON.parse(r.output)};
30
+ }
31
+ export function pageReservation(native,reservation){
32
+ if(reservation.case_revision!==pageCaseRevision(reservation)||!/^[a-f0-9]{64}$/.test(reservation.kernel_identity||''))throw fail('Original page implementation/Case reservation changed.');
33
+ const matches=native.rows.filter(r=>r.type==='git_page_check_reserved');
34
+ if(matches.length!==1||matches[0].source!=='git_page_host'||digest(matches[0].reservation)!==digest(reservation)||matches[0].reservation_digest!==digest(reservation))throw fail('Original page-check reservation changed.');
35
+ return native.rows.indexOf(matches[0]);
36
+ }
37
+ export function assertPageChain(native,{reservation,task,sourceDigest,callId,final=false,afterRows=0,authority,knownFailure=false}){
38
+ const audit=auditExecution(native);
39
+ if(!audit.journal.verified||audit.violations.length||audit.unresolved.length)throw fail('Native page journal has invalid or unresolved effects.');
40
+ const accepted=native.rows.filter(r=>r.type==='chrome_task_accepted'&&r.source==='workflow_host');if(accepted.length!==1||accepted[0].task_digest!==digest(task)||digest(accepted[0].task)!==digest(task))throw fail('Original accepted page task changed.');
41
+ const birth=pageReservation(native,reservation),test=receipt(native,callId,'ChromeTest'),start=native.rows.indexOf(test.proposal);
42
+ if(start<=birth||start<afterRows||digest(native.rows[0].git_verification)!==digest(reservation.native_binding))throw fail('Page check was not born in its reserved native session.');
43
+ if(native.rows.slice(0,birth).some(r=>r.type==='house_proposal'&&r.operation.startsWith('plugin__chrome-lab__')))throw fail('A prior browser observation cannot be retrospectively reserved.');
44
+ const published=receipt(native,reservation.publish_call_id,'ChromePublish');
45
+ if(native.rows.indexOf(published.proposal)<=birth||native.rows.indexOf(published.receipt)>=start)throw fail('Publication was not owned before the original test.');
46
+ const bindings=native.rows.filter(r=>r.type==='git_page_publication_bound');
47
+ if(bindings.length!==1||bindings[0].source!=='git_page_host'||bindings[0].reservation_digest!==digest(reservation)||bindings[0].source_digest!==sourceDigest||native.rows.indexOf(bindings[0])>=start)throw fail('Original candidate publication binding is unavailable.');
48
+ const deployment=native.rows.filter(r=>r.type==='house_receipt'&&r.operation==='plugin__chrome-lab__Deploy'&&r.parent_invocation_id===published.receipt.invocation_id);
49
+ if(deployment.length!==1||deployment[0].status!=='returned'||deployment[0].output_truncated)throw fail('Original publication has no exact returned native Deploy.');
50
+ const deployed=JSON.parse(deployment[0].output),physical=bindings[0].publication;
51
+ if(deployed.project!==task.project||deployed.attempt_id!==physical.attempt_id||deployed.extension_id!==physical.extension_id||deployed.file_manifest_sha256!==physical.file_manifest_sha256||physical.file_manifest_sha256!==reservation.file_manifest_sha256)throw fail('Actual deployed candidate manifest changed.');
52
+ const value=test.value,context=value.binding;
53
+ if(value.status!==(knownFailure?'needs_correction':'verified')||value.scope!=='declared_page_behavior'||value.task_digest!==digest(task)||value.source_digest!==sourceDigest||!context?.identity?.context_unique_id||context.target_id!==bindings[0].binding.target_id||context.project!==task.project||context.url!==bindings[0].binding.url)throw fail('The exact page/source check did not pass.');
54
+ // Case start records identify their owner; its native proposal identifies the
55
+ // original ChromeTest parent (the record need not duplicate that parent).
56
+ const owned=native.rows.filter(r=>r.type==='chrome_page_case_started'&&native.rows.some(p=>p.type==='house_proposal'&&p.invocation_id===r.owner_id&&p.parent_invocation_id===test.receipt.invocation_id));
57
+ if(knownFailure&&(final||owned.length<1||owned.length>task.verification.cases.length))throw fail('Original semantic failure has no bounded ordered case prefix.');
58
+ const expected=knownFailure?task.verification.cases.slice(0,owned.length).map((id,index)=>({id,index,phase:'run'})):final?[{id:'$final',index:task.verification.cases.length,phase:'validate_final'}]:[...task.verification.cases.map((id,index)=>({id,index,phase:'run'})),{id:'$final',index:task.verification.cases.length,phase:'validate_final'}];
59
+ if(owned.length!==expected.length)throw fail('The original complete ordered page case chain is missing.');
60
+ for(const [index,entry] of owned.entries()){
61
+ const e=expected[index],owner=native.rows.find(r=>r.type==='house_receipt'&&r.invocation_id===entry.owner_id),p=native.rows.find(r=>r.type==='house_proposal'&&r.invocation_id===entry.owner_id);
62
+ const contract=native.rows.find(r=>r.type==='house_contract'&&r.contract_digest===owner?.contract_digest)?.contract;
63
+ if(contract?.name!=='ChromePageCase'||contract.origin!==null||contract.revision!==reservation.case_revision||digest(contract)!==owner.contract_digest||owner.contract_digest!==p?.contract_digest)throw fail('Original reserved Case implementation contract changed.');
64
+ const lawMarker=native.rows.find(r=>r.type==='chrome_page_effective_law_bound'&&r.invocation_id===test.proposal.invocation_id),local=lawMarker&&native.rows.find(r=>r.type==='governance_activated'&&r.plan?.plan_hash===lawMarker.local_plan_hash);
65
+ if(!local||local.plan.plan_hash!==accepted[0].governance_plan_hash)throw fail('Original effective page corpus is unavailable.');
66
+ const receiptLaw=(book,id)=>pageReceiptLaw(book,id,{cwd:native.cwd,sessionId:native.id,taskDigest:digest(task),plan:local.plan,scoped:true});
67
+ const ownerLaw=receiptLaw(native,entry.owner_id);
68
+ if(ownerLaw.plan_hash!==owner.governance_plan_hash)throw fail('Original case effective law changed.');
69
+ if(entry.input?.phase!==e.phase||entry.case?.id!==e.id||entry.case?.index!==e.index||p?.arguments_digest!==digest({phase:e.phase,index:e.index})||owner?.operation!=='ChromePageCase'||owner?.status!=='returned'||owner.output_truncated||owner.output_digest!==digest(owner.output))throw fail('Page case owner/order changed.');
70
+ const ownerValue=JSON.parse(owner.output),b=native.rows.find(r=>r.type==='house_receipt'&&r.invocation_id===ownerValue.native_receipt_id),observed=native.rows.filter(r=>r.type==='chrome_page_case_observed'&&r.owner_id===entry.owner_id);
71
+ if(!b||b.parent_invocation_id!==owner.invocation_id||b.operation!=='ChromePageBehavior'||b.status!=='returned'||b.output_truncated||b.output_digest!==digest(b.output)||observed.length!==1||observed[0].receipt_id!==b.invocation_id)throw fail('Original case Behavior/observation owner is missing.');
72
+ receiptLaw(native,b.invocation_id);
73
+ const output=JSON.parse(b.output);
74
+ if(ownerValue.status!==output.status||ownerValue.phase!==e.phase||digest(ownerValue.case)!==digest(entry.case)||output.context.case?.index!==e.index||digest(output.context.verification)!==digest(task.verification)||output.status!==(knownFailure&&index===owned.length-1?'needs_correction':'verified')||output.phase!==e.phase||output.context_digest!==digest(output.context)||output.context.task_digest!==digest(task)||output.context.source_digest!==sourceDigest||digest(output.verifier)!==digest({implementation_sha256:task.verification.implementation_sha256,contract_digest:task.verification.contract_digest})||output.context.case?.id!==e.id||!Array.isArray(output.evidence)||!output.evidence.length||new Set(output.evidence).size!==output.evidence.length)throw fail('Page Behavior source/contract/evidence changed.');
75
+ for(const id of output.evidence){const child=native.rows.find(r=>r.type==='house_receipt'&&r.invocation_id===id);if(!child||child.parent_invocation_id!==b.invocation_id||child.status!=='returned'||child.output_truncated||child.output_digest!==digest(child.output))throw fail('Original returned native child is unavailable.');}
76
+ }
77
+ if(knownFailure&&(value.receipt_id!==JSON.parse(native.rows.find(r=>r.type==='house_receipt'&&r.invocation_id===owned.at(-1).owner_id).output).native_receipt_id||value.case?.id!==expected.at(-1).id))throw fail('Original failed test does not identify its actual failed case.');
78
+ if(final){
79
+ const reserved=native.rows.filter(r=>r.type==='git_page_apply_reserved'&&r.admission?.call_id===callId);
80
+ if(reserved.length!==1||reserved[0].source!=='git_page_host'||reserved[0].admission_digest!==digest(reserved[0].admission)||reserved[0].admission.reservation_digest!==digest(reservation)||native.rows.indexOf(reserved[0])>=start)throw fail('The fresh read-only admission was not reserved before execution.');
81
+ const markers=native.rows.filter(r=>r.type==='git_page_authority_bound'&&r.authority?.call_id===callId),marker=markers[0];
82
+ if(markers.length!==1||marker.source!=='git_page_host'||marker.authority_digest!==digest(authority)||digest(marker.authority)!==digest(authority)||authority.format!=='comal-git-page-authority/1'||authority.resource_id!==reservation.resource_id||authority.reservation_digest!==digest(reservation)||authority.admission_digest!==reserved[0].admission_digest||authority.owner_digest!==reservation.owner_digest||native.rows.indexOf(marker)>=start||digest(authority.captures.map(c=>c.operation))!==digest(['GitIntegrationPrepare','GitIntegrationVerify','GitIntegrationApply'])&&(!authority.captures.some(c=>c.operation==='GitIntegrationPrepare')||authority.captures.at(-2)?.operation!=='GitIntegrationVerify'||authority.captures.at(-1)?.operation!=='GitIntegrationApply'))throw fail('Fresh Apply lacks its recorded original per-leaf authority captures.');
83
+ const later=native.rows.slice(afterRows).filter(r=>r.type==='house_proposal');
84
+ const allowed=new Set(['ChromeTest','ChromePageCase','ChromePageBehavior','plugin__chrome-lab__Targets','plugin__chrome-lab__Contexts','plugin__chrome-lab__Assert','plugin__chrome-lab__Deployment','plugin__chrome-lab__Console','plugin__chrome-lab__InspectValue']);
85
+ if(later.some(r=>!allowed.has(r.operation))||native.rows.slice(afterRows).some(r=>r.type==='chrome_page_case_started'&&r.input?.phase==='run'))throw fail('Apply admission performed something other than original read-only final validation.');
86
+ }
87
+ return {call_id:callId,invocation_id:test.receipt.invocation_id,receipt_digest:test.receipt.audit.sha256,output_digest:test.receipt.output_digest,source_digest:sourceDigest,binding:context,task_digest:digest(task),...(authority?{authority_digest:digest(authority)}:{}),prefix:nativePrefix(native)};
88
+ }
89
+
90
+ export function pageProofImplementation(){return digest([isPageVerification,pageCaseRevision,nativePrefix,assertNativePrefix,receipt,pageReservation,assertPageChain,validPageRecipes,fail].map(fn=>Function.prototype.toString.call(fn)));}
91
+ export function pageRecipeReference(recipe){const body={format:'comal-git-page-recipe-reference/1',name:recipe.name,revision:recipe.revision,descriptor_digest:recipe.digest,task_digest:digest(recipe.task),verifier_digest:digest(recipe.verifier),implementation_digest:digest(recipe.implementation)};return{...body,digest:digest(body)};}
92
+ export function validPageRecipeReferences(verification,references){
93
+ if(!isPageVerification(verification))return references===undefined;
94
+ if(!Array.isArray(references)||references.length!==1||!references[0])return false;const{digest:claimed,...body}=references[0];
95
+ return body.format==='comal-git-page-recipe-reference/1'&&body.name===verification.checks[0].recipe&&typeof body.revision==='string'&&Object.keys(body).length===7&&['descriptor_digest','task_digest','verifier_digest','implementation_digest'].every(key=>/^[a-f0-9]{64}$/.test(body[key]||''))&&claimed===digest(body);
96
+ }