@cratylus/canon 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/README.md +24 -31
  2. package/dist/agents/nico.js +1 -1
  3. package/dist/agents/nico.js.map +1 -1
  4. package/dist/compose.d.ts +4 -4
  5. package/dist/compose.js +14 -16
  6. package/dist/compose.js.map +1 -1
  7. package/dist/contracts.js +1 -1
  8. package/dist/contracts.js.map +1 -1
  9. package/dist/dimensions/autonomy/management-by-exception.js +1 -1
  10. package/dist/dimensions/autonomy/management-by-exception.js.map +1 -1
  11. package/dist/dimensions/communication-style/bottom-line-up-front.js +1 -1
  12. package/dist/dimensions/communication-style/bottom-line-up-front.js.map +1 -1
  13. package/dist/dimensions/communication-style/layman.js +1 -1
  14. package/dist/dimensions/communication-style/layman.js.map +1 -1
  15. package/dist/dimensions/deliverable/read-back.js +1 -1
  16. package/dist/dimensions/deliverable/read-back.js.map +1 -1
  17. package/dist/dimensions/expertise/semantic-addressing.js +1 -1
  18. package/dist/dimensions/expertise/semantic-addressing.js.map +1 -1
  19. package/dist/dimensions/guardrails/append-only.js +1 -1
  20. package/dist/dimensions/guardrails/append-only.js.map +1 -1
  21. package/dist/dimensions/guardrails/fixed-acceptance-criteria.js +1 -1
  22. package/dist/dimensions/guardrails/fixed-acceptance-criteria.js.map +1 -1
  23. package/dist/dimensions/guardrails/honesty.js +1 -1
  24. package/dist/dimensions/guardrails/honesty.js.map +1 -1
  25. package/dist/dimensions/guardrails/input-untrusted.js +1 -1
  26. package/dist/dimensions/guardrails/input-untrusted.js.map +1 -1
  27. package/dist/dimensions/guardrails/never-repairs.js +1 -1
  28. package/dist/dimensions/guardrails/never-repairs.js.map +1 -1
  29. package/dist/dimensions/guardrails/no-implementation.js +1 -1
  30. package/dist/dimensions/guardrails/no-implementation.js.map +1 -1
  31. package/dist/dimensions/guardrails/privacy.js +1 -1
  32. package/dist/dimensions/guardrails/privacy.js.map +1 -1
  33. package/dist/dimensions/memory/reconstitution.js +1 -1
  34. package/dist/dimensions/memory/reconstitution.js.map +1 -1
  35. package/dist/dimensions/memory/source-of-truth.js +1 -1
  36. package/dist/dimensions/memory/source-of-truth.js.map +1 -1
  37. package/dist/dimensions/procedure/orchestrator-workers.js +1 -1
  38. package/dist/dimensions/procedure/orchestrator-workers.js.map +1 -1
  39. package/dist/dimensions/procedure/reconstitution.js +1 -1
  40. package/dist/dimensions/procedure/reconstitution.js.map +1 -1
  41. package/dist/dimensions/role/architect.js +1 -1
  42. package/dist/dimensions/role/architect.js.map +1 -1
  43. package/dist/dimensions/role/builder.js +1 -1
  44. package/dist/dimensions/role/builder.js.map +1 -1
  45. package/dist/dimensions/role/check-writer.js +1 -1
  46. package/dist/dimensions/role/check-writer.js.map +1 -1
  47. package/dist/dimensions/role/lead.js +1 -1
  48. package/dist/dimensions/role/lead.js.map +1 -1
  49. package/dist/dimensions/role/reviewer.js +1 -1
  50. package/dist/dimensions/role/reviewer.js.map +1 -1
  51. package/dist/dimensions/stance/cold-decode-oracle.js +1 -1
  52. package/dist/dimensions/stance/cold-decode-oracle.js.map +1 -1
  53. package/dist/dimensions/stance/conceptual-elevation.js +1 -1
  54. package/dist/dimensions/stance/conceptual-elevation.js.map +1 -1
  55. package/dist/dimensions/stance/decisive.js +1 -1
  56. package/dist/dimensions/stance/decisive.js.map +1 -1
  57. package/dist/dimensions/stance/zero-trust.js +1 -1
  58. package/dist/dimensions/stance/zero-trust.js.map +1 -1
  59. package/dist/genus/prime-principle.d.ts +4 -3
  60. package/dist/genus/prime-principle.js +3 -2
  61. package/dist/genus/prime-principle.js.map +1 -1
  62. package/dist/index.d.ts +3 -3
  63. package/dist/index.js +10 -11
  64. package/dist/index.js.map +1 -1
  65. package/dist/manifest.d.ts +8 -8
  66. package/dist/manifest.js +16 -22
  67. package/dist/manifest.js.map +1 -1
  68. package/dist/operator-lexicon.d.ts +1 -12
  69. package/dist/operator-lexicon.js +3 -33
  70. package/dist/operator-lexicon.js.map +1 -1
  71. package/dist/practices.d.ts +1 -2
  72. package/dist/practices.js +1 -5
  73. package/dist/practices.js.map +1 -1
  74. package/dist/roles/architect.js +7 -25
  75. package/dist/roles/architect.js.map +1 -1
  76. package/dist/roles/lead.js +2 -10
  77. package/dist/roles/lead.js.map +1 -1
  78. package/dist/roles/reviewer.js +6 -6
  79. package/dist/roles/reviewer.js.map +1 -1
  80. package/dist/rules/commit-discipline.d.ts +3 -0
  81. package/dist/rules/commit-discipline.js +26 -0
  82. package/dist/rules/commit-discipline.js.map +1 -0
  83. package/dist/rules/conventional-commits.d.ts +3 -0
  84. package/dist/rules/conventional-commits.js +38 -0
  85. package/dist/rules/conventional-commits.js.map +1 -0
  86. package/dist/rules/established-libraries.d.ts +3 -0
  87. package/dist/rules/established-libraries.js +20 -0
  88. package/dist/rules/established-libraries.js.map +1 -0
  89. package/dist/rules/gitflow.d.ts +3 -0
  90. package/dist/rules/gitflow.js +32 -0
  91. package/dist/rules/gitflow.js.map +1 -0
  92. package/dist/rules/green-field.d.ts +3 -0
  93. package/dist/rules/green-field.js +20 -0
  94. package/dist/rules/green-field.js.map +1 -0
  95. package/dist/rulesets.d.ts +2 -0
  96. package/dist/rulesets.js +17 -0
  97. package/dist/rulesets.js.map +1 -0
  98. package/dist/skills/conceptualize/skill.js +2 -3
  99. package/dist/skills/conceptualize/skill.js.map +1 -1
  100. package/dist/skills/create-skill/skill.js +1 -1
  101. package/dist/skills/elicit/skill.js +2 -3
  102. package/dist/skills/elicit/skill.js.map +1 -1
  103. package/dist/skills/exemplify/skill.js +3 -4
  104. package/dist/skills/exemplify/skill.js.map +1 -1
  105. package/dist/skills/materialize/skill.js +2 -3
  106. package/dist/skills/materialize/skill.js.map +1 -1
  107. package/dist/skills/probe/skill.js +24 -25
  108. package/dist/skills/probe/skill.js.map +1 -1
  109. package/dist/skills/signify/skill.js +21 -20
  110. package/dist/skills/signify/skill.js.map +1 -1
  111. package/package.json +5 -6
  112. package/dist/genus/persona.d.ts +0 -8
  113. package/dist/genus/persona.js +0 -30
  114. package/dist/genus/persona.js.map +0 -1
  115. package/dist/hook-cells.d.ts +0 -3
  116. package/dist/hook-cells.js +0 -14
  117. package/dist/hook-cells.js.map +0 -1
  118. package/dist/hooks/deploy-drift-notice.d.ts +0 -2
  119. package/dist/hooks/deploy-drift-notice.js +0 -431
  120. package/dist/hooks/deploy-drift-notice.js.map +0 -1
  121. package/dist/rules/repo-preamble.d.ts +0 -2
  122. package/dist/rules/repo-preamble.js +0 -49
  123. package/dist/rules/repo-preamble.js.map +0 -1
  124. package/dist/skills/introspect/skill.d.ts +0 -2
  125. package/dist/skills/introspect/skill.js +0 -55
  126. package/dist/skills/introspect/skill.js.map +0 -1
@@ -8,63 +8,64 @@ import { primePrinciple } from '../../genus/prime-principle.js';
8
8
  // signification cells each claimed to be composed from the other three, and no one of
9
9
  // them could be read, taken or deployed alone.
10
10
  //
11
- // The relation those edges named is CITATION, not dependency: `symbol-altitude`'s
12
- // LEDGER records these cells RESTATING each other's signs in different registers
13
- // (prose here, signature there), which is what a REFERENCE row means. A citation
11
+ // The relation those edges named is CITATION, not dependency: these cells RESTATE each
12
+ // other's signs in different registers (prose here, signature there). A citation
14
13
  // belongs inside the block, at the sign that borrows it (`@ <cell>`), where it needs
15
14
  // no import and cannot cycle. Composition is the caller's: an agent pipes these cells
16
15
  // in whatever order the work wants.
17
16
  export const signify = {
18
17
  name: 'signify',
19
- description: `use this skill to name a concept — assign each its canonical anchor so one name ⇔ one concept, discovered by cold decode against the model's priors, never coined, for the corpus's core agent dimension names and values alone; stage 2 of exemplify, independently invocable.`,
18
+ description: `use this skill to name a concept — assign each its canonical anchor so one name ⇔ one concept within its bounded context, discovered by cold decode against the model's priors, never coined, for the corpus's core agent dimension names and values alone; stage 2 of exemplify, independently invocable.`,
20
19
  formalBlock: `C ≜ the concept lattice
21
20
  D ≜ the identity-criterion atoms a concept circumscribes
22
21
  prim(c) ≜ c has no factorization into other concepts
23
22
  Names ≜ the shared symbol space
24
23
  <_lex : total order on Names
25
24
  concept-record ≜ ⟨gloss, anchor, factorization⟩
26
- fired : Names → ℘(D)
27
- dec : Names ⇀ ℘(D)
28
- circ(n,c) ⇔ fired(n) = D(c)
25
+ κ : C → bounded-context ⟨the context c is read in · part of its sign's address⟩
26
+ fired : Names × bounded-context → ℘(D) ⟨fired(n, κ) : what n invokes read in κ⟩
27
+ dec : Names × bounded-context ⇀ ℘(D)
28
+ circ(n,c) ⇔ fired(n, κ(c)) = D(c)
29
29
  |n| ≜ LLM-native description-length
30
+ residue(n,c) ≜ residue @ workflow ⟨c named n · n read in κ(c)⟩
30
31
  σ* : C → Names
31
- σ*(c) ≜ argmin_{n : circ(n,c)} ⟨|n|, n⟩
32
+ σ*(c) ≜ argmin_{n} ⟨residue(n,c), |n|, n⟩ ⟨the smallest residue in κ(c) · |n| then n break a tie⟩
32
33
  mint : C ⇀ Names
33
- mint(c) ≜ ∘ M : M ⊆ { n : fired(n) ≠ ∅ } ∧ circ(∘ M, c)
34
+ mint(c) ≜ ∘ M : M ⊆ { n : fired(n, κ(c)) ≠ ∅ } ∧ circ(∘ M, c)
34
35
  Art ≜ the corpus's own authored surfaces ⟨prose ≡ identifier ≡ path⟩
35
- α : C ↣ Names ⟨dom(α) ≜ the concepts the corpus's core agent dimension names and values name · every other term is the industry's own, and no consumer surface is held to α⟩
36
+ α : C ↣ Names ⟨injective within each κ, ¬ across · dom(α) ≜ the concepts the corpus's core agent dimension names and values name · every other term is the industry's own, and no consumer surface is held to α⟩
36
37
  register(a) ≜ the register a's body is observably authored in ⟨σ* ∨ human⟩
37
38
  verbatim(a) ≜ a ships whole, byte-exact : settled σ*, never re-derived at projection
38
39
 
39
40
  ∀ c ∈ dom(α) : α(c) ≜ σ*(c)
40
- Anchors ≜ { α(c) | c ∈ dom(α) } ⟨the assigned-anchor set⟩
41
- dom(dec) = { α(p) | prim(p) } ; dec(α(p)) = fired(α(p))
41
+ Anchors ≜ { ⟨α(c), κ(c)⟩ | c ∈ dom(α) } ⟨the assigned-anchor set, each anchor with its context⟩
42
+ dom(dec) = { ⟨α(p), κ(p)⟩ | prim(p) } ; dec(α(p), κ(p)) = fired(α(p), κ(p))
42
43
  { n : circ(n,c) } = ∅ ⇒ σ*(c) = mint(c)
43
44
 
44
45
  name(k) fills anchor(k) ≜ α(gloss(k)) ; gloss(k) preserved ; factorization(k) untouched
45
46
 
46
- α(cᵢ) = α(cⱼ) ⇒ D(cᵢ) = D(cⱼ)
47
+ α(cᵢ) = α(cⱼ) ∧ κ(cᵢ) = κ(cⱼ) ⇒ D(cᵢ) = D(cⱼ)
47
48
 
48
- coalesce(cᵢ, cⱼ) ⇔ α(cᵢ) = α(cⱼ) ∧ D(cᵢ) = D(cⱼ)
49
- cᵢ <_N cⱼ ⇔ α(cᵢ) <_lex α(cⱼ)
49
+ coalesce(cᵢ, cⱼ) ⇔ α(cᵢ) = α(cⱼ) ∧ κ(cᵢ) = κ(cⱼ) ∧ D(cᵢ) = D(cⱼ)
50
+ cᵢ <_N cⱼ ⇔ α(cᵢ) <_lex α(cⱼ) ∨ (α(cᵢ) = α(cⱼ) ∧ κ(cᵢ) <_lex κ(cⱼ)) ⟨a tie on the anchor broken by context name⟩
50
51
  ≺ ≜ shortlex over (C, <_N)
51
52
 
52
- c ∉ dom(α) ⇒ c ∉ Anchors :
53
+ c ∉ dom(α) ⇒ ∄ n : ⟨n, κ(c)⟩ ∈ Anchors assigned c :
53
54
  c is no core agent dimension name or value ⇒ c is the industry's own term, used as the industry understands it ⇒ exclude, logged
54
55
  { n : circ(n,c) } = ∅ ∧ ∄ mint(c) ⇒ exclude, logged
55
- ∃ cᵢ, cⱼ : α(cᵢ) = α(cⱼ) ∧ D(cᵢ) ≠ D(cⱼ) ⇒ the partition was wrong ↦ conceptualize
56
+ ∃ cᵢ, cⱼ : α(cᵢ) = α(cⱼ) ∧ κ(cᵢ) = κ(cⱼ) ∧ D(cᵢ) ≠ D(cⱼ) ⇒ the partition was wrong ↦ conceptualize
56
57
  ⟨α(cᵢ), α(cⱼ) taken at DIFFERENT altitudes is still this collision⟩
57
58
 
58
59
  ∃ d : { n : ¬circ(n,c) } all fail on d ALONE ⇒ d ∉ D(c) ∧ ∃ cᵢ ≠ c : d ∈ D(cᵢ) ⟨the property hangs off the wrong noun⟩
59
60
  ↦ conceptualize ⟨re-partition, then cᵢ names EASILY ; a losing STREAK on one d proves misattribution, ¬ that σ*(c) is scarce⟩
60
61
 
61
- ¬circ(n, cᵢ) ⇒ evidence about fired(n), ¬ about cᵢ ⟨fired : Names → ℘(D) reads n ALONE⟩
62
+ ¬circ(n, cᵢ) ⇒ evidence about fired(n, κ(cᵢ)), ¬ about cᵢ ⟨fired reads n with κ's name and subject only : ∄ sibling · ∄ menu⟩
62
63
  ∴ a rejection BINDS n ⟨n freed from cᵢ is ¬ thereby fit for cⱼ ; circ(n, cⱼ) re-established independently ∨ n stays rejected⟩
63
64
 
64
- residue(c) ≜ { d ∈ D(c) | d ∉ fired(α(c)) } ∪ { d ∈ fired(α(c)) | d ∉ D(c) }
65
+ residue(c) ≜ residue(α(c), c)
65
66
  ∀ a ∈ Art : ∀ c carried by a : c enters a's body as ⟨α(c), residue(c)⟩ ⟨d ∈ residue(c) ∩ D(c) stated ; d ∈ residue(c) ∧ d ∉ D(c) stated denied⟩
66
67
  residue(c) = ∅ ⇒ c enters as α(c) alone
67
- minimal(a) ⇔ ∄ c carried by a : a re-states fired(α(c))
68
+ minimal(a) ⇔ ∄ c carried by a : a re-states fired(α(c), κ(c))
68
69
 
69
70
  conform(a) ⇔ register(a) = σ*
70
71
  verbatim(a) ⇒ register(a) = σ*`,
@@ -1 +1 @@
1
- {"version":3,"file":"skill.js","sourceRoot":"","sources":["../../../src/skills/signify/skill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAGhE,4CAA4C;AAC5C,EAAE;AACF,0GAA0G;AAC1G,gFAAgF;AAChF,qFAAqF;AACrF,mFAAmF;AACnF,sFAAsF;AACtF,+CAA+C;AAC/C,EAAE;AACF,kFAAkF;AAClF,iFAAiF;AACjF,iFAAiF;AACjF,qFAAqF;AACrF,sFAAsF;AACtF,oCAAoC;AAEpC,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,iRAAiR;IAC9R,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAkDmC;IAChD,WAAW,EAAE,GAAG,EAAE,CAAC,EAAE;IACrB,QAAQ,EAAE,cAAc;CACzB,CAAC"}
1
+ {"version":3,"file":"skill.js","sourceRoot":"","sources":["../../../src/skills/signify/skill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAGhE,4CAA4C;AAC5C,EAAE;AACF,0GAA0G;AAC1G,gFAAgF;AAChF,qFAAqF;AACrF,mFAAmF;AACnF,sFAAsF;AACtF,+CAA+C;AAC/C,EAAE;AACF,uFAAuF;AACvF,iFAAiF;AACjF,qFAAqF;AACrF,sFAAsF;AACtF,oCAAoC;AAEpC,MAAM,CAAC,MAAM,OAAO,GAAU;IAC5B,IAAI,EAAE,SAAS;IACf,WAAW,EAAE,4SAA4S;IACzT,WAAW,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+BAoDmC;IAChD,WAAW,EAAE,GAAG,EAAE,CAAC,EAAE;IACrB,QAAQ,EAAE,cAAc;CACzB,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cratylus/canon",
3
- "version": "0.19.0",
4
- "description": "The canonical corpus of discovered optimal-signifiers — agents, skills, dimensions and rules as typed TS source. Markdown is a projection of these modules.",
3
+ "version": "0.20.0",
4
+ "description": "The canonical corpus of discovered optimal-signifiers, each in its bounded context — agents, skills, dimensions and rules as typed TS source. Markdown is a projection of these modules.",
5
5
  "license": "MIT",
6
6
  "engines": {
7
7
  "node": ">=22"
@@ -18,11 +18,11 @@
18
18
  ],
19
19
  "//test": "`test` builds FIRST because `test/runtime-shim.test.ts` drives the shipped `cratylus project` over the repository's own `cratylus.config.ts`, and that config extends `@cratylus/canon` — the PACKAGE, i.e. this package's `dist`. turbo's `test.dependsOn: [^build]` builds this package's DEPENDENCIES, never this package, so a cold clone would run the consumer path against a dist that does not exist.",
20
20
  "dependencies": {
21
- "@cratylus/schema": "0.11.0"
21
+ "@cratylus/schema": "0.12.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@cratylus/forge": "0.17.1",
25
- "@cratylus/runtime": "0.17.0",
24
+ "@cratylus/forge": "0.18.0",
25
+ "@cratylus/runtime": "0.18.0",
26
26
  "@cratylus/tooling": "0.0.0",
27
27
  "@types/node": "^22.0.0",
28
28
  "tsx": "^4.19.0",
@@ -56,7 +56,6 @@
56
56
  "build": "pnpm run clean && tsc -p tsconfig.build.json",
57
57
  "clean": "rm -rf dist",
58
58
  "dev": "tsc -p tsconfig.build.json --watch --preserveWatchOutput",
59
- "project:targets": "tsx tooling/project-targets-cli.ts",
60
59
  "scaffold": "tsx tooling/scaffold-cli.ts",
61
60
  "test": "vitest run",
62
61
  "typecheck": "tsc --noEmit",
@@ -1,8 +0,0 @@
1
- /**
2
- * The per-turn persona persistence protocol: the standing countermeasure to
3
- * persona drift (the assigned stance decaying across a long conversation),
4
- * re-grounding the stance at the top of every turn so a later turn is no less the
5
- * agent's own than the first. Its one home; composition carries it on every agent
6
- * (`Agent.persistence`), and the drift tells it names are the drift concept's.
7
- */
8
- export declare const personaProtocol = "## Protocol\n\n**persona drift** \u225C decay of the Provenance-declared stance, across a long conversation, toward the passive/deferential/generic default; its deferential face = sycophancy. Provenance fires once, at reconstitution -- left there it decays.\n\nCountermeasure: re-ground the Provenance stance at the top of **every** turn, before reading the operator's message -- per turn, never once.\n\nIts tells (each = persona drift, never diligence):\n\n- **answerable question** -- asking what it could answer itself;\n- **hand-back** -- a held conclusion returned as a question; an owned decision pushed to the operator;\n- **unrecommended options** -- options offered without a recommendation;\n- **transcribing** -- the operator's wording or bespoke terms copied into the artifact; extract + serve the intent beneath them instead;\n- **descending** -- taking up work the stance keeps it above, in place of the concepts it holds;\n- **prescribing** -- telling a delegate procedure it already knows; an assignment carries intent, concepts and acceptance only;\n- **holding open** -- leaving an owned question open until someone asks; decide what you hold, on sight;\n- **procedure past objective** -- the ritual outliving what it serves: once its objective is met, any further step is persona drift; report the outcome and stop.\n\nThe tells concern only decisions the agent owns: waiting at a hold point for a sign-off it does not hold is never persona drift.\n\nCheck: before ending a turn, read its acts against these tells. Cure = this per-turn re-grounding, never a new rule in memory. The stance is session-length-invariant: a later turn is no less the agent's own than the first.\n\nAgent-to-agent traffic (dispatch prompts \u00B7 subagent returns) is dense and signifier-carrying by construction, never long-form human prose. The reader is the LLM by construction, never inferred; a human-facing deliverable carried within is an explicit human-project, specified at the point of intention.";
@@ -1,30 +0,0 @@
1
- /**
2
- * The per-turn persona persistence protocol: the standing countermeasure to
3
- * persona drift (the assigned stance decaying across a long conversation),
4
- * re-grounding the stance at the top of every turn so a later turn is no less the
5
- * agent's own than the first. Its one home; composition carries it on every agent
6
- * (`Agent.persistence`), and the drift tells it names are the drift concept's.
7
- */
8
- export const personaProtocol = `## Protocol
9
-
10
- **persona drift** ≜ decay of the Provenance-declared stance, across a long conversation, toward the passive/deferential/generic default; its deferential face = sycophancy. Provenance fires once, at reconstitution -- left there it decays.
11
-
12
- Countermeasure: re-ground the Provenance stance at the top of **every** turn, before reading the operator's message -- per turn, never once.
13
-
14
- Its tells (each = persona drift, never diligence):
15
-
16
- - **answerable question** -- asking what it could answer itself;
17
- - **hand-back** -- a held conclusion returned as a question; an owned decision pushed to the operator;
18
- - **unrecommended options** -- options offered without a recommendation;
19
- - **transcribing** -- the operator's wording or bespoke terms copied into the artifact; extract + serve the intent beneath them instead;
20
- - **descending** -- taking up work the stance keeps it above, in place of the concepts it holds;
21
- - **prescribing** -- telling a delegate procedure it already knows; an assignment carries intent, concepts and acceptance only;
22
- - **holding open** -- leaving an owned question open until someone asks; decide what you hold, on sight;
23
- - **procedure past objective** -- the ritual outliving what it serves: once its objective is met, any further step is persona drift; report the outcome and stop.
24
-
25
- The tells concern only decisions the agent owns: waiting at a hold point for a sign-off it does not hold is never persona drift.
26
-
27
- Check: before ending a turn, read its acts against these tells. Cure = this per-turn re-grounding, never a new rule in memory. The stance is session-length-invariant: a later turn is no less the agent's own than the first.
28
-
29
- Agent-to-agent traffic (dispatch prompts · subagent returns) is dense and signifier-carrying by construction, never long-form human prose. The reader is the LLM by construction, never inferred; a human-facing deliverable carried within is an explicit human-project, specified at the point of intention.`;
30
- //# sourceMappingURL=persona.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"persona.js","sourceRoot":"","sources":["../../src/genus/persona.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;+SAqBgR,CAAC"}
@@ -1,3 +0,0 @@
1
- import type { HookCell } from './manifest.js';
2
- /** The harness-substrate hook cells forge projects into the host config + hooks/<id>/. */
3
- export declare const harnessHookCells: readonly HookCell[];
@@ -1,14 +0,0 @@
1
- // First-class hook cells that forge projects from the canon catalog. Source truth
2
- // lives in `src/hooks/*.ts`; this module is the composition root naming the
3
- // concrete cells. Forge selects the adapter, serializes the hook configuration,
4
- // and stages each cell's VERBATIM worker payload under `hooks/<id>/`.
5
- //
6
- // The cell shapes are the schema's type kernel (`@cratylus/schema`); this
7
- // module is the composition root that NAMES the concrete cells and nothing more.
8
- // It used to also LIFT them to the `Hook` IR here, which forced a command string to
9
- // exist before any adapter was chosen. The lift is the projector's, per adapter,
10
- // from `HarnessAdapter.hookCommand`.
11
- import { deployDriftNotice } from './hooks/deploy-drift-notice.js';
12
- /** The harness-substrate hook cells forge projects into the host config + hooks/<id>/. */
13
- export const harnessHookCells = [deployDriftNotice];
14
- //# sourceMappingURL=hook-cells.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hook-cells.js","sourceRoot":"","sources":["../src/hook-cells.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,4EAA4E;AAC5E,gFAAgF;AAChF,sEAAsE;AACtE,EAAE;AACF,0EAA0E;AAC1E,iFAAiF;AACjF,oFAAoF;AACpF,iFAAiF;AACjF,qCAAqC;AAErC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AAGnE,0FAA0F;AAC1F,MAAM,CAAC,MAAM,gBAAgB,GAAwB,CAAC,iBAAiB,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- import type { HookCell } from '../manifest.js';
2
- export declare const deployDriftNotice: HookCell;
@@ -1,431 +0,0 @@
1
- // deploy-drift-notice — the advisory that answers, before the agent acts, whether
2
- // the doctrine THIS HOST runs is the doctrine the corpus renders.
3
- //
4
- // THE INCIDENT. `~/.omp/agent/agents/<name>.md` and the render tree diverged. An agent
5
- // edited the corpus for a full session under a superseded first principle, and every
6
- // gate in the repository stayed green the entire time — because every gate that
7
- // existed was a claim about how a target is PRODUCED, and none was a claim about what
8
- // a host is RUNNING. The comparison that would have caught it landed with
9
- // `deploy --check`: reachable, correct, and speaking only when asked. A check that
10
- // must be remembered is a check that will be forgotten at exactly the moment it
11
- // matters — the session where the doctrine is already wrong. This cell is what asks.
12
- //
13
- // SILENCE WHEN CLEAN IS THE LOAD-BEARING HALF, not a courtesy. An advisory that fires
14
- // every session trains its reader to skip it, and a skipped advisory is worse than an
15
- // absent one: it occupies the slot where a real warning would have been read. So the
16
- // in-sync path emits nothing at all, and the cost of that path is what makes the
17
- // silence affordable (below).
18
- //
19
- // IT RELAYS A REPORT, IT DOES NOT COMPUTE ONE. `deploy --check` already produces the
20
- // only thing worth saying — per artifact, the rendered lines this host is MISSING and
21
- // the superseded lines it is STILL RUNNING. "3 files differ" does not tell an agent
22
- // its own first principle is stale; the axiom does. Recomputing any of that here would
23
- // put a second home under the one claim that must never fork, and the second home
24
- // would be a shell script.
25
- //
26
- // WHAT IT MAY NOT DO: refuse. `ARCHITECTURE`'s fidelity ladder — a shortfall degrades
27
- // and warns, it never refuses and never widens. A stale deployment must not block a
28
- // session; it must be impossible to start one without being told.
29
- //
30
- // IT ASKS AGAIN, BECAUSE THE HOST GOES STALE MID-SESSION. Bound to `session.start`
31
- // alone, the cell answered a question that expires: a projection landing during the
32
- // session it was authored in left the rest of that session running the superseded
33
- // copy with nothing further to say. That is not hypothetical and it is not rare —
34
- // it is the ORDINARY shape of work here, where a session edits a cell, projects it,
35
- // and keeps going under the version it just replaced. The 16-hour-old projection of
36
- // an agent's own governing cell was this; so was an authored skill edit that landed under a
37
- // running session hours later.
38
- //
39
- // `prompt.submit` IS THE RE-ARM, and it is the same question at a finer grain rather
40
- // than a second mechanism. Both moments put the notice before the agent acts on an
41
- // instruction, where the false premise can be corrected. `turn.end` is the wrong end
42
- // of the turn: it speaks after the acting is done. `file.edit.post` was rejected as
43
- // a category error — a cell EDIT is not drift; drift begins when the render tree moves,
44
- // which happens under a shell command.
45
- //
46
- // A RE-ARM IS ONLY AS GOOD AS ITS SILENCE, and this one is where the silence had to be
47
- // re-earned. Fired every prompt, an unchanged verdict would repeat the same advisory
48
- // until the reader stopped seeing it — the exact failure the in-sync path was built to
49
- // avoid, re-entering through the door held open for the fix. So the trigger is a CHANGE
50
- // in the verdict, never a verdict that is merely non-empty: the worker records the last
51
- // verdict this session was told (keyed by ⟨harness, session⟩, carrying ⟨corpus, tree⟩ so
52
- // the same session moved to another checkout asks a fresh question) and speaks only when
53
- // the new one differs. Said once, a standing drift is then silent until it changes or is
54
- // repaired. THE CLEAN VERDICT IS RECORDED TOO, and that is what makes a REAPPEARING drift
55
- // audible: without it, drift → deploy → the identical drift again would match the stale
56
- // record and say nothing.
57
- //
58
- // NO SESSION IDENTITY MEANS NO SUPPRESSION, deliberately, in that direction. When the
59
- // payload carries no usable `session_id` or the state cannot be written, every run
60
- // speaks. A repeated advisory under genuine drift is noise the operator can end with one
61
- // command; a suppressed one is silence, and this cell's own residue says silence is
62
- // borrowed from the in-sync path and may never be handed to any other.
63
- //
64
- // COST, MEASURED (2026-08-05, darwin/arm64, warm page cache, 39 rendered artifacts
65
- // against a deployed `.claude` of ~40 files):
66
- // clean path, end to end 45 ms (10 ms shell discovery + ~35 ms comparator)
67
- // drift path, end to end 60 ms (the report is longer, and is printed)
68
- //
69
- // RE-MEASURED after the three repairs above, and reported as an A/B rather than as a
70
- // new absolute, because an absolute is a claim about the rig as much as the worker.
71
- // Same corpus, same real deployment, best-of-7, the PREVIOUS worker and this one run
72
- // alternately in one session:
73
- // previous worker 56 ms clean · 58 ms drift
74
- // this worker 56 ms clean · 56 ms drift
75
- // The change is free, and the whole spread between 45 and 56 belongs to the harness
76
- // the numbers were taken through (a `sh` under a `bash` under a timestamping
77
- // `python3`), not to anything the worker does. It could not have been otherwise: the
78
- // three facts are SUBSTITUTED AT PROJECTION, so the resolved worker does the same
79
- // three variable assignments it always did, and the one thing that got cheaper is the
80
- // non-zero path, which no longer forks `printf | tail | grep` to recover a verdict.
81
- //
82
- // RE-MEASURED AGAIN for the re-arm, same rig, same A/B discipline (best-of-7, the two
83
- // workers run alternately; the clean leg against a fixture host deployed from this
84
- // corpus's own render tree, so it is a real in-sync verdict rather than an early exit):
85
- // previous worker 62 ms clean · 62 ms drift
86
- // this worker 60 ms clean · 64 ms drift
87
- // Per run it is free — the state costs one `cat` and one small write, which is under
88
- // this rig's noise. THE COST THAT ACTUALLY CHANGED IS FREQUENCY, and saying so is the
89
- // point of measuring: the hook now runs once per PROMPT rather than once per session,
90
- // so a 40-prompt session pays ~2.4 s of wall clock it did not pay before, spread across
91
- // 40 moments where the agent is already waiting on a model. That is the price of not
92
- // running superseded doctrine for the other 39 of them, and it is the trade this cell
93
- // exists to take. The per-run number is what keeps it payable, which is why the bound
94
- // is held by a test rather than remembered.
95
- //
96
- // A FULL BYTE COMPARISON of every rendered artifact is therefore what runs — no
97
- // digest, and above all NO SAMPLE. A sample that skips the ten skills carrying the
98
- // prime principle reports "in sync", which is the exact failure this cell exists to end. The
99
- // `deploy-drift-notice` suite re-measures the clean path and fails on a regression, so
100
- // the number above is a held bound rather than a remembered one.
101
- //
102
- // THE CELL NAMES A CAPABILITY, NOT A PATH, and it now ASKS for everything it used to
103
- // infer. TWO LAWS BIND A CELL. The first: a cell must not restate a location the
104
- // projector already computes. The harder companion: a cell must not GUESS what the
105
- // projector could have told it. Three guesses lived here and all three are gone:
106
- //
107
- // 1. THE COMPARATOR'S NAME was a shell literal, because the forge CLI's name had no
108
- // compile-time home the way `CLI_BIN` does. It has one now — derived, not
109
- // declared: `forge/src/bin-name.ts` reads the `bin` key npm itself obeys, so
110
- // there is one authored spelling in the package and the cell names the fact
111
- // (`deploy-bin`) rather than repeating it.
112
- // 2. THE HARNESS was assumed to be claude. The tree is still identified by SHAPE —
113
- // `--out` is an operator's choice and must never be assumed — but the shape it
114
- // matches is now THIS harness's, because the projector substitutes this
115
- // harness's hooks-file name (`harness-hooks-file`) and its own name
116
- // (`harness-name`, passed to the comparator so it audits the right home). A
117
- // omp session reports on the omp deployment. The pair lives on
118
- // `HarnessAdapter` and reaches here through projection, which is the only stage
119
- // that knows which adapter it is rendering for — the alternative was a
120
- // shell-side copy of the harness registry, a second home for adapter knowledge.
121
- // 3. THE VERDICT was recovered by matching the report's closing line. `deploy
122
- // --check` exited 1 both when the host was stale and when the check itself
123
- // failed, so the exit status could not separate them and the worker read the
124
- // TEXT — coupling a hook worker to the comparator's output FORMAT because its
125
- // exit CONTRACT could not carry the distinction. The contract carries it now
126
- // (`forge/src/deploy/check-exit.ts`: 0 in sync · 1 drift · 2 no verdict), and
127
- // the one code that is forge's choice rather than POSIX's arrives as a fact.
128
- //
129
- // EVERY HOOK WORKER ON EVERY HARNESS HAD (2). This cell is only the first whose job
130
- // made it visible, because it is the first that has to LOOK at the deployed tree
131
- // rather than merely run in it — and the repair is on the shared channel, so the next
132
- // one asks by name instead of rediscovering the blindness.
133
- //
134
- // The `workers[].content` is the TEMPLATE the committed worker at `targetPath`
135
- // regenerates from — resolved bytes, byte-locked by `test/hook-rule-boundary.test.ts`.
136
- // The committed resolution is the OMP one (`COMMITTED_TARGET_HARNESS`), because a
137
- // template with an adapter-relative fact has one resolution per harness and
138
- // `targetPath` names one file; every DEPLOYED copy is resolved for the harness that
139
- // session runs. EDIT THE CELL AND REGENERATE (`pnpm canon:project:targets`) — never
140
- // the committed `.sh` alone.
141
- export const deployDriftNotice = {
142
- id: 'deploy-drift-notice',
143
- residue: 'advisory ↾ session.start ∧ prompt.submit ⟨re-arm ∴ mid-session projection ⇏ stale ∀remaining-turns⟩ · deployed ≢ rendered ⇒ emit ⟨superseded-lines ∧ missing-lines ; ¬count · ¬digest · ¬sample⟩ ↾ verdict ≢ last-told ⟨trigger ≜ verdict-change · ¬verdict-non-empty ∴ re-arm ⇏ re-notify · memory ↾ ⟨harness · session⟩ ∧ ⟨corpus · tree⟩ · in-sync recorded ∴ recurrence audible · ∄session-id ⇒ ¬suppress ⟨noise ≻ borrowed-silence⟩⟩ · deployed ≡ rendered ⇒ ∅ ⟨silence-when-clean MANDATORY ⟨fires ∀session ⇒ reader-skips ⇒ worse-than-absent⟩⟩ · verdict ↦ comparator ⟨corpus-owned · ¬face-computed · ¬reimplemented⟩ · corpus ↦ walk-up ⟨cwd · corpus-marker⟩ ⟨∄ ⇒ ∅ ⟨¬in-scope ∴ ¬wrong⟩⟩ · tree ↦ shape ⟨agents ∧ skills ∧ THIS-harness-hooks-file ; ¬named-path⟩ · harness ↦ projection ⟨¬inferred ∴ session-own-deployment⟩ · stale ⊻ ¬ran ↦ exit-code ⟨¬report-text ∴ format ⊥ verdict⟩ · reached ∧ ∄verdict ⇒ SAY-SO ⟨silence ≡ in-sync ∴ ¬borrowable⟩ · ¬block · exit-0 ∀error',
144
- substrate: 'harness',
145
- events: ['session.start', 'prompt.submit'],
146
- entry: 'deploy-drift-notice.sh',
147
- timeout: 10,
148
- refs: [],
149
- speech: [
150
- {
151
- id: 'drift',
152
- text: 'DEPLOY DRIFT — this host runs a SUPERSEDED projection of the canon. In the report below, a `-` line is doctrine THIS SESSION is operating under and the corpus no longer says; a `+` line is what the corpus says and this host never received. Read every `-` line as a false premise, not as background. `pnpm canon:deploy` converges the host.',
153
- },
154
- {
155
- id: 'no-verdict',
156
- // `%s` is the worker's printf arg for the comparator it actually invoked. A
157
- // BLIND line naming nothing sends the reader hunting for what failed.
158
- text: 'DEPLOY DRIFT — the comparator (`%s`) returned no verdict, so whether this host runs the current canon is UNKNOWN. This is not an in-sync report; nothing here establishes that the doctrine in force is the corpus.',
159
- },
160
- {
161
- id: 'no-comparator',
162
- text: 'DEPLOY DRIFT — this corpus is in scope and its deploy comparator (`%s`) is not installed here, so whether this host runs the current canon is UNKNOWN. Install the workspace dependencies to make the question answerable; until then this is not an in-sync report.',
163
- },
164
- ],
165
- workers: [
166
- {
167
- filename: 'deploy-drift-notice.sh',
168
- targetPath: 'packages/canon/targets/guardrail/deploy-drift-notice.sh',
169
- executable: true,
170
- content: `#!/usr/bin/env sh
171
- # deploy-drift-notice — an ADVISORY, at session start and again before every
172
- # prompt. It asks the corpus's own deploy tool whether the tree this host RUNS is
173
- # the tree the corpus RENDERS, and speaks only when the answer CHANGES to "no".
174
- #
175
- # WHY (the incident): a deployed agent definition and the render tree diverged, an
176
- # agent ran a superseded first principle for a whole session, and every gate in the
177
- # repository stayed green throughout. The comparison was already reachable —
178
- # \`deploy --check\` — and nothing ran it, so the drift stayed silent by default.
179
- #
180
- # WHY AGAIN (the second incident): bound to session start alone, it answered a
181
- # question that expires. A projection landing MID-session left the rest of that
182
- # session running the superseded copy with nothing further to say — which is the
183
- # ordinary shape of work here, not an edge case. \`prompt.submit\` re-asks it in the
184
- # same position that made the first answer worth having: before the agent acts.
185
- #
186
- # CONTRACT:
187
- # - SILENT WHEN IN SYNC. An advisory that fires every session trains its reader
188
- # to skip it, and is then worse than absent.
189
- # - SPEAKS ON A CHANGE, NOT ON A STATE. Re-armed per prompt, a standing drift
190
- # would otherwise repeat until it stopped being read — the same failure, back
191
- # through the door the re-arm opened. The last verdict this session was told is
192
- # recorded; an identical one is silent.
193
- # - ADVISORY ONLY. Prints to stdout and exits 0. It never emits
194
- # {"decision":"block"} — a stale deployment must not block a session.
195
- # - NEVER SAMPLES. The comparison is the tool's own: whole tree, byte for byte.
196
- # A sample that skips the ten skills carrying the prime principle reports "in sync".
197
- # - NOT A COUNT. It relays the tool's report, which carries the superseded lines
198
- # still running and the rendered lines missing.
199
- #
200
- # NOTHING BELOW IS INFERRED THAT PROJECTION COULD STATE. The four values this
201
- # worker cannot compute for itself are SUBSTITUTED into it when the cell is
202
- # rendered — the comparator's name, this harness's name and hooks file, and the
203
- # exit code the comparator returns for drift. A copy of any of them, spelled here,
204
- # would be a second home in a file no compiler reads.
205
- #
206
- # COST: the cell header carries the measurement and its A/B. Not repeated here — a
207
- # number in two places is a number that will disagree with itself. What is worth
208
- # saying at this grain: the discovery below is a small fraction of it, and the rest
209
- # is the comparator reading both trees whole, which is the point.
210
- #
211
- # INPUT : SessionStart / UserPromptSubmit hook JSON on stdin (cwd, session_id, ...).
212
- # OUTPUT : a CHANGED drift verdict -> advisory + the report; a changed unanswerable
213
- # one -> a BLIND line; in sync, or unchanged since this session was told -> none.
214
-
215
- set -eu
216
-
217
- # Neither a session start nor a prompt may be broken by an advisory. Any unexpected
218
- # error -> silence.
219
- trap 'exit 0' EXIT
220
-
221
- # ── WHAT PROJECTION TOLD US ────────────────────────────────────────────────────
222
- # The corpus's own deploy tool. ONE authored spelling exists in the whole
223
- # repository — \`packages/forge/package.json\`'s \`bin\` key, the one npm obeys — and
224
- # \`forge/src/bin-name.ts\` derives from it, so this line is that key, relayed.
225
- DEPLOY_TOOL={{fact:deploy-bin}}
226
-
227
- # WHICH HARNESS THIS PROJECTION IS FOR. A hook worker is handed no identity by the
228
- # session it runs in, so a worker that must LOOK at a deployed tree had to guess —
229
- # and guessed claude, which made an omp session report on a sibling deployment.
230
- # These two are the adapter's own \`name\` and \`hooksFile\`, substituted at
231
- # projection: the omp render of this cell carries omp's.
232
- HARNESS={{fact:harness-name}}
233
- HARNESS_HOOKS_FILE={{fact:harness-hooks-file}}
234
-
235
- # The status the comparator returns when it FOUND drift. \`0\` means success to
236
- # POSIX and needs no telling; this one is the tool's own choice, so it is told.
237
- DRIFT_RC={{fact:deploy-check-drift-code}}
238
-
239
- # ── THE PAYLOAD, DRAINED ONCE ──────────────────────────────────────────────────
240
- # Two fields are needed and stdin can only be read once, so both come off ONE jq —
241
- # the same single fork the cwd lookup already cost, not a second one. \`.cwd\` says
242
- # which corpus; \`.session_id\` says who is being told, which is what lets a re-armed
243
- # advisory stay quiet about a verdict it has already delivered.
244
- input="$(cat 2>/dev/null || true)"
245
- cwd=""
246
- sid=""
247
- if command -v jq >/dev/null 2>&1 && [ -n "$input" ]; then
248
- fields="$(printf '%s' "$input" | jq -r '.cwd // "", .session_id // ""' 2>/dev/null || true)"
249
- {
250
- read -r cwd || true
251
- read -r sid || true
252
- } <<EOF
253
- $fields
254
- EOF
255
- fi
256
-
257
- # ── WHERE ARE WE? the corpus, by its own marker ────────────────────────────────
258
- # \`cratylus.config.ts\` is the file \`deploy\` itself reads to learn which corpus it is
259
- # operating on. Walking up for it asks the same question the tool asks, instead of
260
- # inventing a second convention for "a checkout of the canon".
261
- start="\${CRATYLUS_CORPUS:-$cwd}"
262
- [ -n "$start" ] || start="\${CLAUDE_PROJECT_DIR:-$PWD}"
263
-
264
- root=""
265
- d="$start"
266
- while [ -n "$d" ] && [ "$d" != "/" ]; do
267
- if [ -f "$d/cratylus.config.ts" ]; then
268
- root="$d"
269
- break
270
- fi
271
- d="\${d%/*}"
272
- done
273
- # NO CORPUS IN SCOPE IS SILENCE, and it is not the "clean" silence above. A session
274
- # outside every checkout has no rendered doctrine to be compared against, so there is
275
- # nothing here this hook could be right or wrong about — and speaking would fire it on
276
- # every unrelated session, which is the one failure that makes an advisory worthless.
277
- [ -n "$root" ] || exit 0
278
-
279
- # ── WHICH TREE? this harness's render tree, by its SHAPE ───────────────────────
280
- # A render tree is recognized by BEING one: \`agents/\` + \`skills/\` + THIS harness's
281
- # own hooks file. That file is what distinguishes this harness's tree from another
282
- # harness's tree sitting beside it, and it is named by projection rather than by
283
- # this script — so an omp render of this worker skips the claude tree and a claude
284
- # render skips the omp one. The \`--out\` dir is an operator's choice, so the tree
285
- # is discovered, never named.
286
- #
287
- # THE GLOB USED TO BE \`.render*\`, WHICH MADE THE PARAGRAPH ABOVE FALSE. The shape
288
- # test only ever ran on candidates a NAME had already selected, so "discovered, never
289
- # named" described the second half of a two-stage filter whose first stage was a
290
- # hard-coded prefix. Any operator honouring the documented freedom — \`--out
291
- # build/corpus\` — got silence, and silence here is indistinguishable from in-sync.
292
- # The rename of the corpus's own tree is what surfaced it; the defect predates it.
293
- #
294
- # So the candidate set is now DOTTED DIRECTORIES at the same depths, plus one level
295
- # beneath each, because a tree that splits per harness puts the shape one deeper
296
- # (\`.cratylus/claude\`, not \`.render-ts\`). Dotted, because that is the convention
297
- # for generated trees (\`.next\`, \`.turbo\`, \`.svelte-kit\`) and because walking every
298
- # directory in a checkout to find one that has \`agents/\` is a cost this hook cannot
299
- # pay on every prompt. The shape test is still what decides; the glob now only bounds
300
- # where to look, and it no longer bounds it to one spelling.
301
- tree="\${CRATYLUS_RENDER_TREE:-}"
302
- if [ -z "$tree" ]; then
303
- for c in \\
304
- "$root"/.*/ "$root"/*/.*/ "$root"/*/*/.*/ \\
305
- "$root"/.*/*/ "$root"/*/.*/*/ "$root"/*/*/.*/*/; do
306
- case "$c" in
307
- */node_modules/*) continue ;;
308
- */.git/*) continue ;;
309
- */./ | */../) continue ;;
310
- esac
311
- c="\${c%/}"
312
- [ -d "$c/agents" ] && [ -d "$c/skills" ] && [ -f "$c/$HARNESS_HOOKS_FILE" ] || continue
313
- tree="$c"
314
- break
315
- done
316
- fi
317
- # A corpus that has rendered nothing for THIS harness has nothing to be compared
318
- # against — the same not-in-scope silence, one step in.
319
- [ -n "$tree" ] || exit 0
320
-
321
- # ── WHAT WAS THIS SESSION ALREADY TOLD? ────────────────────────────────────────
322
- # The re-arm's whole cost is here. Asked before every prompt, an unchanged verdict
323
- # repeated until the reader stopped seeing it would be the in-sync failure re-entering
324
- # through the door opened to fix the stale one. So one small file per session holds the
325
- # last verdict delivered, and the trigger is the DIFFERENCE.
326
- #
327
- # The key is \`⟨harness, session⟩\` and the CONTENT carries \`⟨corpus, tree⟩\`: two
328
- # harnesses in one session audit two hosts and must not overwrite each other's answer,
329
- # and the same session moved to another checkout is asking a different question rather
330
- # than repeating one. A session id that is not a plain token is treated as absent —
331
- # nothing here builds a path out of an unexamined payload field.
332
- state=""
333
- case "$sid" in
334
- '' | *[!A-Za-z0-9._-]*) ;;
335
- *)
336
- stash="\${TMPDIR:-/tmp}/cratylus-deploy-drift"
337
- if [ -d "$stash" ] || mkdir -p "$stash" 2>/dev/null; then
338
- state="$stash/$HARNESS-$sid"
339
- fi
340
- ;;
341
- esac
342
-
343
- # \`changed <verdict>\` — record it as the last thing this session was told, and
344
- # answer whether it DIFFERS from what was recorded before.
345
- #
346
- # NO STATE MEANS EVERY RUN SPEAKS, and the asymmetry is deliberate. Without a session
347
- # id there is nothing to suppress against, and a repeated advisory under real drift is
348
- # noise one \`deploy\` ends, while a suppressed one is SILENCE — which this cell spends
349
- # its whole contract making mean "in sync", and may therefore lend to nothing else.
350
- changed() {
351
- now="$root|$tree|$1"
352
- [ -n "$state" ] || return 0
353
- last=""
354
- [ ! -f "$state" ] || last="$(cat "$state" 2>/dev/null || true)"
355
- printf '%s' "$now" >"$state" 2>/dev/null || true
356
- [ "$last" != "$now" ]
357
- }
358
-
359
- # ── WHAT ASKS? the tool, never a reimplementation ──────────────────────────────
360
- cli="\${CRATYLUS_DEPLOY_CHECK:-}"
361
- if [ -z "$cli" ]; then
362
- if [ -x "$root/node_modules/.bin/$DEPLOY_TOOL" ]; then
363
- cli="$root/node_modules/.bin/$DEPLOY_TOOL"
364
- elif command -v "$DEPLOY_TOOL" >/dev/null 2>&1; then
365
- cli="$DEPLOY_TOOL"
366
- fi
367
- fi
368
- if [ -z "$cli" ]; then
369
- if changed "no-comparator"; then
370
- printf '{{speech:no-comparator}}\\n' "$DEPLOY_TOOL"
371
- fi
372
- exit 0
373
- fi
374
-
375
- # \`--harness\` is what makes the audited HOME this session's. Without it the tool
376
- # resolves its default root, so an omp tree would be compared against the claude
377
- # deployment — a report that is about neither.
378
- # The tool reports and repairs nothing, so it is safe to point at a host mid-work.
379
- set +e
380
- out="$("$cli" deploy --check \\
381
- --harness "$HARNESS" \\
382
- --agents-dir "$tree/agents" \\
383
- --skills-dir "$tree/skills" \\
384
- --hooks-dir "$tree" 2>&1)"
385
- rc=$?
386
- set -e
387
-
388
- # rc 0 IS the tool's verdict that no artifact is stale and none is absent (a foreign
389
- # artifact is reported by it, and is not a defect). The silence is earned here, and
390
- # nowhere else — every other exit takes a branch below.
391
- #
392
- # IT IS RECORDED ANYWAY, and that is what makes a RETURNING drift audible: drift, then
393
- # a deploy, then the identical drift again would match a record that still said "drift"
394
- # and be swallowed. Recording the clean verdict makes the second one a change. Its own
395
- # result is discarded — a host that has just gone from stale to in-sync gets no all-clear
396
- # either, because silence-when-clean is unconditional.
397
- if [ "$rc" -eq 0 ]; then
398
- changed in-sync || true
399
- exit 0
400
- fi
401
-
402
- # WHICH KIND OF NON-ZERO — asked of the EXIT CODE, which is a contract, and not of
403
- # the report's wording, which is a format. Drift has a code of its own; everything
404
- # else that is not success is the check failing to produce a verdict, including the
405
- # codes this worker was never told about (a crash, a signal, a missing file). The
406
- # two demand opposite responses: relaying a broken tool as drift fabricates a
407
- # verdict, and relaying it as silence is the bypass-by-omission this cell exists to
408
- # end.
409
- #
410
- # THE REPORT IS PART OF THE VERDICT, not decoration on it. Two drifts that differ in
411
- # WHICH doctrine is superseded are two things to be told, so the report's bytes are what
412
- # the session remembers — a key of "drift" alone would announce the first stale artifact
413
- # and swallow every one after it.
414
- if [ "$rc" -eq "$DRIFT_RC" ]; then
415
- if changed "drift
416
- $out"; then
417
- printf '%s\\n' '{{speech:drift}}'
418
- printf '%s\\n' "$out"
419
- fi
420
- else
421
- if changed "no-verdict $rc
422
- $out"; then
423
- printf '{{speech:no-verdict}}\\n' "$cli"
424
- fi
425
- fi
426
- exit 0
427
- `,
428
- },
429
- ],
430
- };
431
- //# sourceMappingURL=deploy-drift-notice.js.map