@timurproko/a1 0.1.8-dev.157 → 0.1.8-dev.214

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 (171) hide show
  1. package/README.md +32 -0
  2. package/bin/cli.js +2 -2
  3. package/bin/sync-pi-tui-proxy.js +1 -1
  4. package/bin/ui.js +18 -3
  5. package/dist/cli/dispatch.js +3 -3
  6. package/dist/cli/packages.js +1 -1
  7. package/dist/cli/version-stats.js +3 -3
  8. package/dist/composition/owned-ui.d.ts +4 -1
  9. package/dist/composition/owned-ui.js +9 -6
  10. package/dist/composition/settings-route-host.js +6 -4
  11. package/dist/contracts/agent-engine/capability-ports.d.ts +2 -2
  12. package/dist/contracts/agent-engine/domain-validation.js +22 -4
  13. package/dist/contracts/agent-engine/domain.d.ts +17 -0
  14. package/dist/contracts/owned-ui/model.d.ts +8 -0
  15. package/dist/contracts/owned-ui/validation.js +11 -0
  16. package/dist/features/launch/development-launch.js +1 -1
  17. package/dist/features/owned-ui/customization.d.ts +2 -0
  18. package/dist/features/owned-ui/customization.js +2 -0
  19. package/dist/features/owned-ui/diagnostics.d.ts +1 -0
  20. package/dist/features/owned-ui/diagnostics.js +1 -0
  21. package/dist/features/owned-ui/index.d.ts +1 -0
  22. package/dist/features/owned-ui/index.js +1 -0
  23. package/dist/features/owned-ui/project-trust-prompt.d.ts +25 -0
  24. package/dist/features/owned-ui/project-trust-prompt.js +149 -0
  25. package/dist/features/owned-ui/settings-app.js +69 -44
  26. package/dist/features/workspace/reducer.d.ts +1 -0
  27. package/dist/features/workspace/reducer.js +1 -0
  28. package/dist/features/workspace/router.d.ts +1 -0
  29. package/dist/features/workspace/router.js +1 -0
  30. package/dist/features/workspace/store.d.ts +1 -0
  31. package/dist/features/workspace/store.js +1 -0
  32. package/dist/foundation/launch-guardian/main.js +1 -1
  33. package/dist/foundation/native-host-protocol/codec.d.ts +2 -0
  34. package/dist/foundation/native-host-protocol/codec.js +2 -0
  35. package/dist/foundation/process-containment/linux-process-inspector.d.ts +1 -0
  36. package/dist/foundation/process-containment/linux-process-inspector.js +1 -0
  37. package/dist/foundation/process-containment/native-guardian-containment.d.ts +1 -0
  38. package/dist/foundation/process-containment/native-guardian-containment.js +1 -0
  39. package/dist/foundation/process-containment/windows-process-inspector.d.ts +1 -0
  40. package/dist/foundation/process-containment/windows-process-inspector.js +1 -0
  41. package/dist/foundation/protocol/client.d.ts +1 -0
  42. package/dist/foundation/protocol/client.js +1 -0
  43. package/dist/foundation/protocol/messages.d.ts +1 -0
  44. package/dist/foundation/protocol/messages.js +1 -0
  45. package/dist/foundation/release/bootstrap.js +6 -6
  46. package/dist/foundation/release/cohort-state.d.ts +1 -0
  47. package/dist/foundation/release/cohort-state.js +1 -0
  48. package/dist/foundation/release/release-gc.js +1 -1
  49. package/dist/foundation/release/release.js +1 -1
  50. package/dist/foundation/release/update-transaction.d.ts +1 -0
  51. package/dist/foundation/release/update-transaction.js +1 -0
  52. package/dist/foundation/release/update.js +12 -12
  53. package/dist/foundation/storage/control-store.d.ts +1 -0
  54. package/dist/foundation/storage/control-store.js +1 -0
  55. package/dist/foundation/structured-agent-runtime/backpressure.d.ts +1 -0
  56. package/dist/foundation/structured-agent-runtime/backpressure.js +1 -0
  57. package/dist/foundation/structured-agent-runtime/commands.d.ts +1 -0
  58. package/dist/foundation/structured-agent-runtime/commands.js +1 -0
  59. package/dist/foundation/structured-agent-runtime/reconnection.d.ts +1 -0
  60. package/dist/foundation/structured-agent-runtime/reconnection.js +1 -0
  61. package/dist/foundation/structured-agent-runtime/state.d.ts +1 -0
  62. package/dist/foundation/structured-agent-runtime/state.js +1 -0
  63. package/dist/foundation/supervision/main.js +1 -1
  64. package/dist/foundation/supervision/server.d.ts +1 -0
  65. package/dist/foundation/supervision/server.js +4 -6
  66. package/dist/integrations/pi/components/conformance.d.ts +1 -0
  67. package/dist/integrations/pi/components/conformance.js +1 -0
  68. package/dist/integrations/pi/components/shell-components.d.ts +1 -0
  69. package/dist/integrations/pi/components/shell-components.js +1 -0
  70. package/dist/integrations/pi/components/shell-editor-autocomplete.js +10 -0
  71. package/dist/integrations/pi/components/shell-footer-status.d.ts +1 -1
  72. package/dist/integrations/pi/components/shell-footer-status.js +19 -13
  73. package/dist/integrations/pi/components/shell-presenters-info.d.ts +36 -0
  74. package/dist/integrations/pi/components/shell-presenters-info.js +78 -0
  75. package/dist/integrations/pi/components/shell-presenters-transcript.d.ts +3 -36
  76. package/dist/integrations/pi/components/shell-presenters-transcript.js +88 -128
  77. package/dist/integrations/pi/components/shell-shared-facade.d.ts +11 -1
  78. package/dist/integrations/pi/components/shell-shared-facade.js +1 -1
  79. package/dist/integrations/pi/engine/adapter.d.ts +22 -4
  80. package/dist/integrations/pi/engine/adapter.js +220 -70
  81. package/dist/integrations/pi/engine/conformance.d.ts +2 -0
  82. package/dist/integrations/pi/engine/conformance.js +2 -0
  83. package/dist/integrations/pi/engine/http-dispatcher.d.ts +4 -0
  84. package/dist/integrations/pi/engine/http-dispatcher.js +25 -0
  85. package/dist/integrations/pi/engine/index.d.ts +3 -0
  86. package/dist/integrations/pi/engine/index.js +3 -0
  87. package/dist/integrations/pi/engine/model-auth-integration.d.ts +1 -0
  88. package/dist/integrations/pi/engine/model-auth-integration.js +1 -0
  89. package/dist/integrations/pi/engine/package-integration.js +1 -1
  90. package/dist/integrations/pi/engine/project-trust-preflight.d.ts +22 -0
  91. package/dist/integrations/pi/engine/project-trust-preflight.js +50 -0
  92. package/dist/integrations/pi/engine/resource-extension-integration.d.ts +1 -0
  93. package/dist/integrations/pi/engine/resource-extension-integration.js +1 -0
  94. package/dist/integrations/pi/engine/runtime-integration.d.ts +16 -1
  95. package/dist/integrations/pi/engine/runtime-integration.js +34 -4
  96. package/dist/integrations/pi/engine/session-integration.d.ts +1 -0
  97. package/dist/integrations/pi/engine/session-integration.js +2 -1
  98. package/dist/integrations/pi/engine/settings-effects.d.ts +66 -0
  99. package/dist/integrations/pi/engine/settings-effects.js +249 -0
  100. package/dist/integrations/pi/engine/settings-integration.d.ts +14 -26
  101. package/dist/integrations/pi/engine/settings-integration.js +102 -108
  102. package/dist/integrations/pi/engine/workflow-controllers.d.ts +2 -1
  103. package/dist/integrations/pi/engine/workflow-controllers.js +1 -0
  104. package/dist/integrations/pi/session-ui/clipboard-image.d.ts +11 -0
  105. package/dist/integrations/pi/session-ui/clipboard-image.js +29 -0
  106. package/dist/integrations/pi/session-ui/index.d.ts +1 -0
  107. package/dist/integrations/pi/session-ui/index.js +1 -0
  108. package/dist/integrations/pi/session-ui/prompt-chips.js +6 -2
  109. package/dist/integrations/pi/session-ui/session-shell-root.d.ts +39 -4
  110. package/dist/integrations/pi/session-ui/session-shell-root.js +215 -80
  111. package/dist/integrations/pi/session-ui/session-shell.d.ts +10 -1
  112. package/dist/integrations/pi/session-ui/session-shell.js +256 -45
  113. package/dist/integrations/pi/session-ui/session-viewport-controller.d.ts +5 -0
  114. package/dist/integrations/pi/session-ui/session-viewport-controller.js +76 -11
  115. package/dist/integrations/pi/session-ui/stream-presentation-coalescer.d.ts +29 -0
  116. package/dist/integrations/pi/session-ui/stream-presentation-coalescer.js +79 -0
  117. package/dist/integrations/pi/session-ui/system-clipboard.d.ts +9 -0
  118. package/dist/integrations/pi/session-ui/system-clipboard.js +38 -9
  119. package/dist/integrations/pi/tui-runtime/adapter.d.ts +6 -0
  120. package/dist/integrations/pi/tui-runtime/adapter.js +166 -8
  121. package/dist/integrations/pi/tui-runtime/contracts.d.ts +25 -0
  122. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +63 -0
  123. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +240 -0
  124. package/dist/integrations/pi/tui-runtime/index.d.ts +2 -0
  125. package/dist/integrations/pi/tui-runtime/index.js +2 -0
  126. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.d.ts +39 -0
  127. package/dist/integrations/pi/tui-runtime/input-presentation-coordinator.js +139 -0
  128. package/dist/native/darwin-arm64/manifest.json +1 -1
  129. package/dist/native/linux-x64/manifest.json +1 -1
  130. package/dist/native/win32-x64/manifest.json +2 -2
  131. package/dist/native/win32-x64/process-guardian.exe +0 -0
  132. package/dist/ui/apps/host.js +5 -9
  133. package/dist/ui/apps/registry.d.ts +1 -0
  134. package/dist/ui/apps/registry.js +1 -0
  135. package/dist/ui/components/dialog-panel.js +5 -3
  136. package/dist/ui/components/frame.d.ts +1 -0
  137. package/dist/ui/components/frame.js +1 -0
  138. package/dist/ui/components/index.d.ts +1 -0
  139. package/dist/ui/components/index.js +1 -0
  140. package/dist/ui/components/line-input.d.ts +1 -6
  141. package/dist/ui/components/line-input.js +4 -9
  142. package/dist/ui/components/list-view.d.ts +0 -6
  143. package/dist/ui/components/list-view.js +14 -7
  144. package/dist/ui/components/progress-status.d.ts +2 -0
  145. package/dist/ui/components/progress-status.js +4 -0
  146. package/dist/ui/components/scrollbar.js +1 -1
  147. package/dist/ui/components/shortcuts.d.ts +1 -0
  148. package/dist/ui/components/shortcuts.js +1 -0
  149. package/dist/ui/components/spans.d.ts +2 -5
  150. package/dist/ui/components/spans.js +28 -8
  151. package/dist/ui/components/surface.d.ts +0 -5
  152. package/dist/ui/components/surface.js +1 -5
  153. package/dist/ui/components/text-selection.d.ts +10 -1
  154. package/dist/ui/components/text-selection.js +96 -29
  155. package/dist/ui/components/text.js +3 -3
  156. package/dist/ui/components/transcript-viewport.d.ts +42 -0
  157. package/dist/ui/components/transcript-viewport.js +287 -39
  158. package/dist/ui/settings/sections.d.ts +6 -19
  159. package/dist/ui/settings/sections.js +17 -15
  160. package/dist/ui/settings/session.d.ts +8 -18
  161. package/dist/ui/settings/session.js +60 -52
  162. package/dist/ui/settings/store.d.ts +1 -0
  163. package/dist/ui/settings/store.js +2 -1
  164. package/docs/architecture/code-documentation.md +77 -0
  165. package/docs/architecture/project-structure.md +15 -1
  166. package/docs/architecture/ui-reference-provenance.md +23 -3
  167. package/docs/ci-release-runbook.md +98 -7
  168. package/docs/manual-owned-ui-checkpoint.md +28 -1
  169. package/docs/manual-terminal-colour-check.md +4 -3
  170. package/docs/repository-governance-live-acceptance.md +77 -0
  171. package/package.json +11 -3
@@ -1,46 +1,36 @@
1
- import type { AgentSettingsPort } from "../../contracts/agent-engine/index.js";
1
+ import type { AgentJsonValue, AgentSettingApplicationBoundary, AgentSettingsPort } from "../../contracts/agent-engine/index.js";
2
2
  import type { OwnedUiSettingValue } from "./declarations.js";
3
3
  import type { OwnedUiSettingsResolution } from "./resolution.js";
4
4
  import { type OwnedUiSettingsBackend, type OwnedUiSettingsSection } from "./sections.js";
5
5
  import type { OwnedUiSettingsStore } from "./store.js";
6
6
  export interface OwnedUiSettingsChangeOutcome {
7
+ readonly status: "applied" | "deferred" | "unavailable" | "failed";
7
8
  readonly applied: boolean;
8
- /** True when the value is stored but only takes effect on the next start. */
9
+ /** Compatibility projection for existing A1 restart-bound declarations. */
9
10
  readonly pendingRestart: boolean;
10
- /** Present when the change could not be persisted; the surface must not show it as saved. */
11
+ readonly application: AgentSettingApplicationBoundary | null;
12
+ readonly storedValue: AgentJsonValue | null;
13
+ readonly effectiveValue: AgentJsonValue | null;
14
+ readonly limitationReason: string | null;
11
15
  readonly failure: string | null;
12
16
  }
13
17
  export interface OwnedUiSettingsSessionOptions {
14
18
  readonly store: OwnedUiSettingsStore;
15
- /** Null when no engine is attached; the Agent section then reports itself unavailable. */
16
19
  readonly agent?: AgentSettingsPort | null;
17
- /**
18
- * Resolves the port at load time instead of construction time, for an engine
19
- * whose runtime is not up yet. Takes precedence over `agent`.
20
- */
21
20
  readonly agentProvider?: () => AgentSettingsPort | null;
22
- /** Agent-owned controls fixed by this product surface and therefore omitted. */
23
21
  readonly hiddenAgentSettingIds?: readonly string[];
24
22
  }
25
23
  export type OwnedUiSettingsListener = (session: OwnedUiSettingsSession) => void;
26
- /**
27
- * Holds the resolved settings for one owned UI session and routes every accepted
28
- * change to the backend that owns it: A1 settings to the A1 document, agent
29
- * settings to the engine settings port. Neither backend ever sees the other's value.
30
- */
24
+ /** Routes every accepted setting change only to its owning backend. */
31
25
  export declare class OwnedUiSettingsSession {
32
26
  #private;
33
27
  constructor(options: OwnedUiSettingsSessionOptions);
34
28
  get resolution(): OwnedUiSettingsResolution;
35
- /** Reads the engine's settings so the Agent section can be built. Never throws. */
36
29
  load(): Promise<void>;
37
30
  sections(): readonly OwnedUiSettingsSection[];
38
- /** Resolved value of an A1 setting, or null when it is not declared. */
39
31
  value(id: string): OwnedUiSettingValue | null;
40
- /** Value stored this session but not yet in effect, for a restart-required setting. */
41
32
  pendingValue(id: string): OwnedUiSettingValue | null;
42
33
  onChange(listener: OwnedUiSettingsListener): () => void;
43
- /** Writes a structured value, which only an engine-backed setting can hold. */
44
34
  changeStructured(backend: OwnedUiSettingsBackend, id: string, value: Readonly<Record<string, unknown>>): Promise<OwnedUiSettingsChangeOutcome>;
45
35
  change(backend: OwnedUiSettingsBackend, id: string, value: OwnedUiSettingValue): Promise<OwnedUiSettingsChangeOutcome>;
46
36
  }
@@ -1,9 +1,6 @@
1
+ import { assertAgentSettingDescriptor } from "../../contracts/agent-engine/index.js";
1
2
  import { buildOwnedUiSettingsSections, findOwnedUiSettingsEntry, } from "./sections.js";
2
- /**
3
- * Holds the resolved settings for one owned UI session and routes every accepted
4
- * change to the backend that owns it: A1 settings to the A1 document, agent
5
- * settings to the engine settings port. Neither backend ever sees the other's value.
6
- */
3
+ /** Routes every accepted setting change only to its owning backend. */
7
4
  export class OwnedUiSettingsSession {
8
5
  #store;
9
6
  #agent;
@@ -23,21 +20,18 @@ export class OwnedUiSettingsSession {
23
20
  get resolution() {
24
21
  return this.#resolution;
25
22
  }
26
- /** Reads the engine's settings so the Agent section can be built. Never throws. */
27
23
  async load() {
28
24
  this.#resolution = this.#store.read();
29
- const agent = this.#agentProvider === null ? this.#agent : this.#agentProvider();
25
+ const agent = this.#currentAgent();
30
26
  this.#agentSnapshot = agent === null ? null : await snapshotOf(agent, this.#hiddenAgentSettingIds);
31
27
  this.#notify();
32
28
  }
33
29
  sections() {
34
30
  return buildOwnedUiSettingsSections({ resolution: this.#resolution, agent: this.#agentSnapshot });
35
31
  }
36
- /** Resolved value of an A1 setting, or null when it is not declared. */
37
32
  value(id) {
38
33
  return this.#resolution.settings.find(setting => setting.declaration.id === id)?.value ?? null;
39
34
  }
40
- /** Value stored this session but not yet in effect, for a restart-required setting. */
41
35
  pendingValue(id) {
42
36
  return this.#pending.get(id) ?? null;
43
37
  }
@@ -45,27 +39,10 @@ export class OwnedUiSettingsSession {
45
39
  this.#listeners.add(listener);
46
40
  return () => this.#listeners.delete(listener);
47
41
  }
48
- /** Writes a structured value, which only an engine-backed setting can hold. */
49
42
  async changeStructured(backend, id, value) {
50
43
  if (backend !== "agent")
51
44
  return failed("only an agent setting holds a structured value");
52
- const agent = this.#agentProvider === null ? this.#agent : this.#agentProvider();
53
- if (agent === null)
54
- return failed("no agent engine is attached");
55
- if (!agent.capabilities.write || !agent.writeSetting) {
56
- return failed("the agent engine does not support changing settings from this surface");
57
- }
58
- try {
59
- await agent.writeSetting(id, value);
60
- if (agent.capabilities.flush && agent.flush)
61
- await agent.flush();
62
- }
63
- catch (error) {
64
- return failed(`${id} could not be written to the agent engine: ${describe(error)}`);
65
- }
66
- this.#agentSnapshot = await snapshotOf(agent, this.#hiddenAgentSettingIds);
67
- this.#notify();
68
- return { applied: true, pendingRestart: false, failure: null };
45
+ return await this.#changeAgentValue(id, value);
69
46
  }
70
47
  async change(backend, id, value) {
71
48
  const entry = findOwnedUiSettingsEntry(this.sections(), id, backend);
@@ -73,7 +50,7 @@ export class OwnedUiSettingsSession {
73
50
  return failed(`unknown ${backend} setting: ${id}`);
74
51
  if (!entry.editable)
75
52
  return failed(`${id} is not editable from this surface`);
76
- return backend === "a1" ? this.#changeOwned(id, value) : await this.#changeAgent(id, value);
53
+ return backend === "a1" ? this.#changeOwned(id, value) : await this.#changeAgentValue(id, value);
77
54
  }
78
55
  #changeOwned(id, value) {
79
56
  const outcome = this.#store.write(this.#resolution, id, value);
@@ -81,35 +58,39 @@ export class OwnedUiSettingsSession {
81
58
  return failed(outcome.failure ?? `${id} could not be stored`);
82
59
  const previous = this.#resolution;
83
60
  this.#resolution = this.#store.read();
84
- const declaration = previous.settings.find(setting => setting.declaration.id === id)?.declaration;
85
- if (declaration?.application === "restart") {
61
+ const previousSetting = previous.settings.find(setting => setting.declaration.id === id);
62
+ if (previousSetting?.declaration.application === "restart") {
86
63
  this.#pending.set(id, value);
87
64
  this.#resolution = previous;
88
65
  this.#notify();
89
- return { applied: false, pendingRestart: true, failure: null };
66
+ return changed("deferred", "next-start", value, previousSetting.value);
90
67
  }
91
68
  this.#pending.delete(id);
92
69
  this.#notify();
93
- return { applied: true, pendingRestart: false, failure: null };
70
+ return changed("applied", "live", value, value);
94
71
  }
95
- async #changeAgent(id, value) {
96
- const agent = this.#agentProvider === null ? this.#agent : this.#agentProvider();
72
+ async #changeAgentValue(id, value) {
73
+ const agent = this.#currentAgent();
97
74
  if (agent === null)
98
75
  return failed("no agent engine is attached");
99
76
  if (!agent.capabilities.write || !agent.writeSetting) {
100
77
  return failed("the agent engine does not support changing settings from this surface");
101
78
  }
79
+ let result;
102
80
  try {
103
- await agent.writeSetting(id, value);
104
- if (agent.capabilities.flush && agent.flush)
105
- await agent.flush();
81
+ // Invariant: the coordinator behind the port owns effect installation, persistence,
82
+ // flush, and rollback. A second surface-level flush would split authority.
83
+ result = await agent.writeSetting(id, value);
106
84
  }
107
85
  catch (error) {
108
86
  return failed(`${id} could not be written to the agent engine: ${describe(error)}`);
109
87
  }
110
88
  this.#agentSnapshot = await snapshotOf(agent, this.#hiddenAgentSettingIds);
111
89
  this.#notify();
112
- return { applied: true, pendingRestart: false, failure: null };
90
+ return fromAgentOutcome(result);
91
+ }
92
+ #currentAgent() {
93
+ return this.#agentProvider === null ? this.#agent : this.#agentProvider();
113
94
  }
114
95
  #notify() {
115
96
  for (const listener of this.#listeners)
@@ -118,29 +99,56 @@ export class OwnedUiSettingsSession {
118
99
  }
119
100
  async function snapshotOf(agent, hidden) {
120
101
  try {
121
- const descriptors = (await agent.listSettings()).filter(descriptor => !hidden.has(descriptor.key));
122
- const values = {};
123
- for (const descriptor of descriptors) {
124
- try {
125
- values[descriptor.key] = await agent.readSetting(descriptor.key) ?? null;
126
- }
127
- catch {
128
- values[descriptor.key] = null;
129
- }
130
- }
102
+ const listed = await agent.listSettings();
103
+ for (const descriptor of listed)
104
+ assertAgentSettingDescriptor(descriptor);
105
+ const writeAdvertised = agent.capabilities.write && typeof agent.writeSetting === "function";
106
+ const descriptors = listed.filter(descriptor => !hidden.has(descriptor.key) && writeAdvertised && descriptor.writable && descriptor.available);
131
107
  return {
132
108
  descriptors,
133
- values,
134
- writeAdvertised: agent.capabilities.write && typeof agent.writeSetting === "function",
109
+ writeAdvertised,
135
110
  failure: null,
136
111
  };
137
112
  }
138
113
  catch (error) {
139
- return { descriptors: [], values: {}, writeAdvertised: false, failure: describe(error) };
114
+ return { descriptors: [], writeAdvertised: false, failure: describe(error) };
140
115
  }
141
116
  }
142
117
  function failed(failure) {
143
- return { applied: false, pendingRestart: false, failure };
118
+ return {
119
+ status: "failed",
120
+ applied: false,
121
+ pendingRestart: false,
122
+ application: null,
123
+ storedValue: null,
124
+ effectiveValue: null,
125
+ limitationReason: null,
126
+ failure,
127
+ };
128
+ }
129
+ function changed(status, application, storedValue, effectiveValue) {
130
+ return {
131
+ status,
132
+ applied: status === "applied",
133
+ pendingRestart: status === "deferred" && application === "next-start",
134
+ application,
135
+ storedValue,
136
+ effectiveValue,
137
+ limitationReason: null,
138
+ failure: null,
139
+ };
140
+ }
141
+ function fromAgentOutcome(result) {
142
+ return {
143
+ status: result.status,
144
+ applied: result.status === "applied",
145
+ pendingRestart: result.status === "deferred" && result.application === "next-start",
146
+ application: result.application,
147
+ storedValue: result.storedValue,
148
+ effectiveValue: result.effectiveValue,
149
+ limitationReason: result.limitationReason,
150
+ failure: result.failure,
151
+ };
144
152
  }
145
153
  function describe(error) {
146
154
  return error instanceof Error ? error.message : String(error);
@@ -13,6 +13,7 @@ export interface OwnedUiSettingsWriteOutcome {
13
13
  /** Present when the write failed; the caller must not report the change as saved. */
14
14
  readonly failure: string | null;
15
15
  }
16
+ /** Resolves and atomically persists one profile's owned settings without discarding unknown keys. */
16
17
  export declare class OwnedUiSettingsStore {
17
18
  #private;
18
19
  constructor(options: OwnedUiSettingsStoreOptions);
@@ -5,6 +5,7 @@ import { OWNED_UI_SETTINGS_MIGRATIONS } from "./migrations.js";
5
5
  import { documentFrom, parseOwnedUiSettingsDocument, resolveOwnedUiSettings, } from "./resolution.js";
6
6
  const PROFILE_ID_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
7
7
  const MAX_DOCUMENT_BYTES = 256 * 1024;
8
+ /** Resolves and atomically persists one profile's owned settings without discarding unknown keys. */
8
9
  export class OwnedUiSettingsStore {
9
10
  #file;
10
11
  #declarations;
@@ -76,7 +77,7 @@ export class OwnedUiSettingsStore {
76
77
  rmSync(temporary, { force: true });
77
78
  }
78
79
  catch {
79
- // A leftover temporary file is inert: read() only ever opens the target path.
80
+ // Security: a leftover temporary file is inert: read() only ever opens the target path.
80
81
  }
81
82
  return { stored: false, failure: `${this.#file} could not be written: ${describe(error)}` };
82
83
  }
@@ -0,0 +1,77 @@
1
+ # Code documentation
2
+
3
+ A1 treats source documentation as a contract, not as a coverage target. Names, types, and decomposition explain ordinary behavior. Comments remain only where removing them would hide a public responsibility, invariant, rationale, security property, platform constraint, compatibility boundary, protocol rule, concurrency rule, performance decision, or provenance fact.
4
+
5
+ ## Public class contracts
6
+
7
+ Every first-party TypeScript class exposed through a production owner's public entry has exactly one JSDoc description at its declaration. The description states what the class owns and includes its material lifecycle or safety boundary when one exists.
8
+
9
+ Do not add JSDoc solely because a function, type, constructor, property, or method is exported. Do not document a class by repeating its name or listing its methods. Private and protected members never use JSDoc.
10
+
11
+ ```ts
12
+ /** Persists complete workspace views and recovery references through the control-store boundary. */
13
+ export class WorkspaceStore {
14
+ ```
15
+
16
+ ## Implementation comments
17
+
18
+ A retained implementation comment starts with the reason it exists:
19
+
20
+ | Intent | Use |
21
+ | --- | --- |
22
+ | `Invariant:` | State or ordering that every implementation path must preserve |
23
+ | `Rationale:` | A non-obvious choice among plausible alternatives |
24
+ | `Security:` | Trust, ownership, cleanup, disclosure, or failure-safety boundary |
25
+ | `Platform:` | Operating-system, terminal, filesystem, or toolchain behavior |
26
+ | `Compatibility:` | Behavior intentionally matching or accepting another version or surface |
27
+ | `Protocol:` | Wire, framing, input, encoding, or externally defined semantic rule |
28
+ | `Concurrency:` | Race, serialization, cancellation, or event-loop ordering |
29
+ | `Performance:` | Bounded work, caching, coalescing, or resource decision |
30
+ | `Provenance:` | Source, generated authority, or adaptation origin |
31
+
32
+ ```ts
33
+ // Security: an uncertain process identity never authorizes termination.
34
+ // Platform: Windows Terminal encodes Ctrl+Backspace as a raw backspace byte.
35
+ // Performance: finalized blocks cache by revision and width; live blocks always render.
36
+ ```
37
+
38
+ A continuation line does not repeat the label. If no intent applies, remove the comment and make the code explain itself.
39
+
40
+ ## Forbidden forms
41
+
42
+ The repository gate rejects:
43
+
44
+ - missing or multiple contracts on owner-public first-party classes;
45
+ - `<summary>` and `@summary` tags;
46
+ - boilerplate class descriptions and method inventories;
47
+ - JSDoc on private or protected members;
48
+ - uncategorized implementation comments;
49
+ - obvious commented-out code or control-flow narration;
50
+ - `TODO` or `FIXME` without an issue number, issue URL, or tracker identifier;
51
+ - `@ts-ignore` and suppression directives without a reason;
52
+ - an unclassified tracked code path or synchronized source without provenance.
53
+
54
+ Follow-up and suppression forms include their authority inline:
55
+
56
+ ```ts
57
+ // TODO(#321): remove the compatibility path after the pinned upgrade.
58
+ // @ts-expect-error -- the shipped JavaScript module has no declaration file
59
+ ```
60
+
61
+ ## Source roles
62
+
63
+ The full style policy applies to first-party production TypeScript and to applicable comments in first-party tests, tooling, and native source. Native source does not receive TypeScript declaration requirements.
64
+
65
+ Synchronized Pi source, native vendor trees, generated source, build output, and runtime artifacts are not rewritten to match first-party style. Their exclusion comes from the repository source-role classifier, not from a list of accepted violations. Synchronized source must remain covered by its source ledger or explicit provenance header.
66
+
67
+ ## Validation
68
+
69
+ Run the focused check with:
70
+
71
+ ```sh
72
+ npm run check:code-documentation
73
+ ```
74
+
75
+ The check scans the complete tracked baseline and reports stable rule identifiers with path, line, column, and declaration symbol when available. It has no accepted-violation count or grandfather file. Fast pull-request validation runs the check, and full release validation inherits it through the fast tier.
76
+
77
+ Focused governance tests use valid and invalid virtual sources to preserve each rule independently from the clean repository-baseline assertion. The gate validates deterministic structure and high-confidence anti-patterns; review remains responsible for the factual accuracy of retained prose.
@@ -86,6 +86,20 @@ The repository has one root `package.json`, `package-lock.json`, TypeScript conf
86
86
 
87
87
  Build output mirrors the production namespaces directly under ignored `dist/`, without an intermediate `src/` directory; release and test evidence belongs in ignored `.artifacts/`; temporary agent work belongs in ignored `.worktrees/` and `.builds/`. Repository tooling is grouped under `scripts/governance`, `scripts/release`, `scripts/pi`, and `scripts/development`; the few root scripts are standalone maintenance or build commands. Package contents are selected by the root manifest. The Rust process guardian and console terminal-host proof live under `native/`; Cargo output is ignored. Third-party terminal parser sources are isolated under `native/terminal-host/vendor/` and are not owned application modules.
88
88
 
89
+ ## Development worktrees
90
+
91
+ Every task worktree must be created at `{working-dir}/.worktrees/<task-id>`, where `{working-dir}` is the agent session's initial working directory. The `.worktrees` directory is inside that working directory, not beside it. For working directory `D:/Git/a1`, `D:/Git/a1/.worktrees/<task-id>` is correct and `D:/Git/a1-<task-id>` is forbidden.
92
+
93
+ From the initial working directory, create and address a task worktree explicitly:
94
+
95
+ ```sh
96
+ git fetch origin develop
97
+ git worktree add --detach .worktrees/<task-id> origin/develop
98
+ git -C .worktrees/<task-id> status
99
+ ```
100
+
101
+ The primary worktree stays on `develop` for integration and must not be used for task edits. Do not edit, move, or remove another session's worktree.
102
+
89
103
  ## Documentation and comments
90
104
 
91
105
  - README explains installation, commands, and current limitations.
@@ -93,7 +107,7 @@ Build output mirrors the production namespaces directly under ignored `dist/`, w
93
107
  - `docs/features` explains maintained user-facing behavior.
94
108
  - Operational and manual procedures live in focused runbooks under `docs`.
95
109
  - OpenSpec and Git carry planning and implementation history.
96
- - Source comments explain only non-obvious rationale, safety/security invariants, platform constraints, or public semantic contracts. Names, types, and decomposition explain normal control flow.
110
+ - Source documentation follows the focused [code-documentation policy](code-documentation.md): owner-public class contracts are consistent, retained implementation comments state their intent, and names, types, and decomposition explain ordinary control flow.
97
111
 
98
112
  ## Terminal capability boundary
99
113
 
@@ -29,7 +29,7 @@ its own `core` facade layer; A1 is a product, so the port adapts imports and kee
29
29
  | `ui-components/list-block.ts` — sticky scroll | `settings/impl.ts` — `stickyHeaderGroup`, `topPaddingRows`, `visibleRowCountAt`, `clampScrollForView` | Same reservation arithmetic and two-pass reveal. |
30
30
  | `ui-components/mouse.ts` | `core/panes/sgr-mouse.ts` | Same SGR decoding and per-call regex reset; A1 emits its own event shape. |
31
31
  | `ui-components/mouse.ts` — tracking sequences | `core/host/pi/providers/host-bridge-surface.ts` | Mouse modes only. A1 does not take the alternate screen, because the Pi TUI owns the screen A1 renders through. |
32
- | `features/owned-ui/settings-app.ts` — layout | `settings/impl.ts` — `settingsValueColumn`, `renderFieldLine`, footer and search rendering | Setting discovery is A1's own section model; presentation follows the reference. |
32
+ | `features/owned-ui/settings-app.ts` — section layout, pointer controls, and scrolling | `settings/impl.ts` — `settingsValueColumn`, block navigation, sticky sections, and pointer hit regions | Setting discovery is A1's own A1/Agent section model. Section navigation and pointer-only numeric controls follow the A1 reference; explicit `/` search, ruled shared input, shortcut-derived hints, hidden description rows, configured wheel cadence, and distinct floating scalar menus are reviewed owned interactions. |
33
33
 
34
34
  ## Ported from the pinned engine
35
35
 
@@ -37,16 +37,36 @@ its own `core` facade layer; A1 is a product, so the port adapts imports and kee
37
37
  | --- | --- | --- |
38
38
  | `pi-engine-adapter/settings-integration.ts` — `SETTING_LABELS` | pinned Pi settings selector | Labels and descriptions transcribed so an owned screen reads as the vanilla route words it. Ids are mapped from the selector kebab-case to the exposed camelCase keys. |
39
39
  | `pi-engine/session-integration.ts` and `pi-components/shell-footer-status.ts` — steering queue | pinned Pi interactive mode `onSubmit` and `updatePendingMessagesDisplay` | Steering/follow-up uses `prompt(..., { streamingBehavior })`, allowing Pi to emit the accepted user row, while remaining steering rows preserve Pi's opening spacer, dim `Steering:` labels, dequeue hint, and order before `Working`. |
40
+ | `ui-components/list-view.ts`, `dialog-panel.ts`, `value-menu.ts`, and `features/owned-ui/settings-app.ts` — setting presentation | pinned Pi `SettingsSelectorComponent`, Pi TUI `SettingsList`, `SelectList`, and `Input` at `0.84.2` | Cursor, selected label/value accents, unselected muted values, the 30-column label cap, dialog styling, notices, and narrow-width geometry retain pinned semantics. Scalar-menu placement and input remain shared, while A1/Agent grouping, pointer steppers, explicit `/` search, ruled shared input, shortcut-derived status hints, suppressed selected descriptions, `scrollbarSpeed`-driven wheel movement, and the dark floating menu with lighter active row and effective-value check mark are declared product-owned differences. Independent row evidence: `test/features/owned-ui/pinned-settings-presentation-parity.test.ts`; owned interaction evidence: `test/features/owned-ui/settings-app.test.ts`, `test/ui/components/value-menu.test.ts`, and `test/composition/settings-route-host.test.ts`. |
41
+ | `features/owned-ui/project-trust-prompt.ts` — pre-resource selector | pinned Pi `cli/startup-ui.ts`, `cli/project-trust.ts`, and `core/project-trust.ts` at commit `914cf1472e715297caa30db4b9535d534a9eb718` | Uses a fixed, dependency-bounded A1 startup selector rather than importing private CLI modules. It preserves selected-option accent, navigation/accept/reject/cancel semantics, fail-closed behavior, raw-mode restoration, clearing, cursor restoration, and parent-screen restoration before diagnostics. |
42
+ | `pi-session-ui/session-shell-root.ts` and `session-shell.ts` — fullscreen exit | pinned Pi `InteractiveMode.formatResumeCommand()` and shutdown output at commit `914cf1472e715297caa30db4b9535d534a9eb718` | Re-renders authoritative transcript components with semantic SGR intact, excludes inline-image control payloads and fullscreen-only chrome, restores the terminal first, then emits pinned dim `To resume this session:` wording with `a1`, compact session id, and conditional quoted `--session-dir`. |
43
+ | `pi-tui-runtime/input-presentation-coordinator.ts` and custom-viewport dock reuse | pinned Pi TUI `TuiBase` input dispatch and immediate-render pending guard at `0.84.2` | Preserves each original terminal delivery and invokes the existing Pi handlers exactly once in order. Bare A1 alone drains finite-grammar text/edit/navigation bursts in one immediate event-loop opportunity so Pi's existing pending guard paints the newest state once; effectful, unknown, protocol, paste, and extension-owned input remains an immediate barrier. Geometry-stable dock frames reuse A1's established transcript viewport, while uncertain geometry and replacement-surface damage still fail closed. Independent evidence is under `test/support/input-responsiveness/`. |
44
+ | `test/features/owned-ui/pi-raw-terminal-parity.ts` — parity normalization | pinned Pi public components/runtime at `0.84.2` | Only synchronized-output envelopes, absolute hyperlink targets, declared product/path substitutions, and nondeterministic timing may normalize. SGR roles/reset boundaries, rows, cursor operations, clearing, restoration, and write order remain authoritative; A1-generated JSON captures are diagnostics and cannot serve as pinned evidence. |
40
45
 
41
46
  ## Deliberate differences
42
47
 
43
- - **No alternate screen.** The reference owns its surface and can switch screens; A1 renders
44
- through the pinned Pi TUI, so an owned screen is a full-viewport overlay instead.
48
+ - **Owned in-session routes are overlays.** The A1 UI reference owns its surface and can switch screens; A1 renders in-session owned routes through the pinned Pi TUI as full-viewport overlays. The pre-resource trust selector is separate and uses a bounded alternate startup surface solely so every completion path can restore the untouched parent terminal before engine activation or a fail-closed diagnostic.
45
49
  - **Colour is a port, not an import.** The reference takes a Pi `Theme` directly. A1 defines
46
50
  `UiTheme` so the component layer never imports a Pi adapter and can be rendered plainly in
47
51
  tests.
48
52
  - **Settings content.** The reference aggregates per-extension settings files; A1 has one
49
53
  section for its own settings and one for the agent's, read through the engine settings port.
54
+ - **Settings scalar-menu contrast.** The shared menu keeps reviewed placement, clipping, and
55
+ input behavior, but A1 uses its prior dark floating panel, lighter white-text active row,
56
+ and independent effective-value check mark because physical review rejected a menu that
57
+ blended into the settings rows.
58
+ - **Spinner-backed progress punctuation.** Bare A1 resolves built-in and extension working
59
+ labels through `ui-components/progress-status.ts`, injected by
60
+ `pi-session-ui/session-shell-root.ts` into the shared `pi-components/shell-footer-status.ts`
61
+ spinner factory. The factory canonicalizes the visible progress marker once, to three ASCII
62
+ periods, without importing across the component-adapter boundary. Engine producers remain
63
+ semantic and the source-synchronized Pi status indicators, installed packages, `a1 pi`, and
64
+ vanilla Pi remain untouched.
65
+ - **Keyboard presentation scheduling.** Pinned components, semantic key handling, and the
66
+ comparison profiles stay unchanged. Bare A1 adds one A1-owned event-loop-turn coordinator
67
+ before its fullscreen root and may omit only superseded visual states after every delivery
68
+ has already been applied in order. It does not debounce by wall clock, combine strings,
69
+ patch Pi TUI, or classify unknown extension/terminal data as safe.
50
70
  - **No viewport prototype or child-tree patches.** `ui/agent-view/user-prompt.ts` and the
51
71
  private-child traversal in `ui/agent-view/render.ts` were analyzed only for behavior. The
52
72
  destination renders source timestamps through an owned transcript adapter and derives sticky
@@ -12,6 +12,33 @@ GitHub Actions is the only automation platform. Three refs are protected:
12
12
  channel. The workflow starts at `03:17 UTC` for nightly development verification,
13
13
  or by explicit dispatch from `npm run develop` or `npm run release`.
14
14
 
15
+ `.github/workflows/documentation-auto-merge.yml` is the only pull-request
16
+ auto-merge authority. It runs trusted policy from `develop`; it never checks out or
17
+ executes a pull request's code with its write token.
18
+
19
+ `.github/workflows/merged-branch-cleanup.yml` owns branch deletion after a human or
20
+ external actor closes a pull request. On the close event it checks out trusted
21
+ default-branch policy and installs no pull-request dependencies. GitHub does not
22
+ emit a new workflow event for a merge authored with the workflow `GITHUB_TOKEN`, so
23
+ Documentation auto-merge also performs the same shared reconciliation synchronously
24
+ after its own validated integration. Both paths delete only an unprotected same-
25
+ repository topic ref whose live object still equals the merged pull request head SHA.
26
+ An absent ref is success; fork, unmerged, advanced, protected, default, release-owned,
27
+ or malformed refs are preserved and reported.
28
+
29
+ `config/github-repository-governance.json` is the reviewed policy for repository
30
+ settings, Actions defaults, security capabilities, environments, complete rulesets,
31
+ protected refs, and workflow authority. Inspection is read-only by default:
32
+
33
+ ```sh
34
+ node scripts/governance/check-github-repository-governance.mjs --check
35
+ ```
36
+
37
+ Applying reviewed mutable drift is a separate maintainer operation requiring
38
+ `--apply --confirm apply-a1-github-governance` and a matching post-apply read.
39
+ Secrets and external npm trusted-publisher configuration are capability checks, not
40
+ serialized credentials.
41
+
15
42
  `develop` is where work lands. `master` records what npm `latest` serves and is an
16
43
  effect of stable publication, not a trigger.
17
44
 
@@ -19,12 +46,67 @@ effect of stable publication, not a trigger.
19
46
 
20
47
  | Trigger | Validation and outcome |
21
48
  | --- | --- |
22
- | Pull request into `develop` | Fast required validation; docs/spec-only changes use strict OpenSpec validation |
49
+ | Pull request into `develop` | Modular fast validation; changed/new source documentation is checked once, and rendering runs as `none`, `smoke`, or `full` from the exact impact |
23
50
  | `npm run develop` | Preview package gates on Windows, Linux, and macOS; an existing numbered preview is an early successful no-op |
24
- | Nightly at `03:17 UTC` | Complete non-physical suite on Windows, Linux, and macOS, every night |
51
+ | Nightly at `03:17 UTC` | One full documentation review plus the complete non-physical suite on Windows, Linux, and macOS, every night |
25
52
  | `npm run release -- ...` | Complete exact-byte stable gates, then npm `latest`, tag, GitHub Release, and `master` |
26
53
  | `.github/workflows/full-regression.yml` | Additional on-demand complete regression without publication authority |
27
54
 
55
+ ## Impact-aware development validation
56
+
57
+ `scripts/release/select-validation-impact.mjs` is the single pull-request selector. It records the complete merge-base-to-head name-status diff, changed/new documentation inputs, rendering dependency reasons, conservative fallbacks, and the exact head SHA in a machine-readable artifact. Missing history, unresolved rendering dependencies, unknown relevant inputs, or classifier failure select full rendering rather than silently skipping it.
58
+
59
+ Ordinary type, architecture, unit/contract, and dist checks always run for code changes. Changed-file documentation and rendering run as independent parallel jobs. Rendered shell/component changes select `smoke`; viewport, scheduler, terminal adapter, evidence harness, package identity, and selector changes select `full`; unrelated changes select `none`. The aggregate accepts a skipped modular job only when the current selector requested the skip.
60
+
61
+ Inspect local committed and worktree impact without running tests:
62
+
63
+ ```sh
64
+ npm run select:validation-impact -- --include-worktree
65
+ ```
66
+
67
+ Check documentation only for modified, added, copied, and renamed-to policy files:
68
+
69
+ ```sh
70
+ npm run check:code-documentation:changed
71
+ ```
72
+
73
+ Run the explicit complete documentation review used by nightly and complete regression:
74
+
75
+ ```sh
76
+ npm run check:code-documentation
77
+ ```
78
+
79
+ Rendering evidence captures each selected producer/mode/workload matrix once and reuses it for semantic, paint, parity, and damage assertions. A deliberate second `streamed-prose` capture remains only for determinism. Workflow summaries separate repository-gate timing from runner setup; wall-clock values diagnose runner/cache variance, while selected scopes, matrix captures, producer launches, documentation file counts, and full-scan counts are structural gates.
80
+
81
+ ## Pull request integration
82
+
83
+ `Development validation required` remains the merge gate for every pull request.
84
+ Documentation auto-merge reads the complete GitHub changed-file response and arms
85
+ squash auto-merge for an eligible pull request while required validation is pending;
86
+ branch protection prevents integration until `Development validation required`
87
+ succeeds. If validation finishes before auto-merge can be armed and GitHub already
88
+ reports the pull request clean, the reconciler squash-merges only the validated head
89
+ SHA. Every current and renamed-from path must be under `openspec/**`, under
90
+ `docs/**`, or exactly the root `README.md`. Eligible pull requests must use a
91
+ non-draft branch in this repository and target `develop`.
92
+
93
+ The exact allowlist covers maintained OpenSpec, architecture, feature, manual,
94
+ runbook, and root README documentation. Other root Markdown files, `LICENSE`,
95
+ `.gitignore`, source, tests, scripts, workflows, configuration, generated baselines,
96
+ and mixed changes do not auto-merge. The guard also runs when a pull request changes
97
+ or auto-merge is manually enabled; if any path is outside the allowlist, it disables
98
+ auto-merge while leaving the pull request available for a later manual merge. Every
99
+ docs-only change runs lightweight generated-governance consistency, so archiving an
100
+ inventoried OpenSpec occurrence fails that pull request rather than a later code pull
101
+ request. A legitimate generated baseline update remains outside the allowlist and
102
+ follows the manually accepted mixed/code path.
103
+
104
+ A specification request lands as an OpenSpec-only pull request. Implementation
105
+ starts only after that specification merges and the maintainer explicitly requests
106
+ it, in a fresh worktree and pull request based on updated `origin/develop`. Every
107
+ code/operational pull request remains open after CI until the maintainer validates it
108
+ locally and explicitly authorizes manual integration.
109
+
28
110
  ## Numbered development previews
29
111
 
30
112
  A merge or push to `develop` does not publish by itself. To request a deliberate
@@ -42,8 +124,7 @@ already exists it reports that version without dispatching package work. Otherwi
42
124
  it dispatches GitHub Actions, waits, and reports the published version. It never
43
125
  builds or uploads npm bytes from the workstation.
44
126
 
45
- Nightly resolves the same current `origin/develop` source. It always runs complete
46
- verification even if source has not changed. For a new number it packs once and
127
+ Nightly resolves the same current `origin/develop` source. It runs one platform-independent full documentation review before the platform matrix, and the matrix records that prerequisite instead of repeating the scan four times. It always runs complete verification even if source has not changed. For a new number it packs once and
47
128
  runs the suite against that final-version tarball before publication. For an
48
129
  existing number it downloads the exact npm tarball and runs package/update gates
49
130
  against those registry bytes; publication is then a successful no-op.
@@ -92,6 +173,14 @@ Rules that do not bend:
92
173
  ## When something fails
93
174
 
94
175
  - **PR validation fails:** fix the code and push; do not mark a failed tier optional.
176
+ - **Documentation auto-merge fails:** leave the pull request open, inspect its exact
177
+ changed-file classification, docs-sensitive inventory, and workflow permissions,
178
+ and never broaden the allowlist to make one pull request pass.
179
+ - **Merged-branch cleanup fails:** inspect the bounded PR/ref/SHA disposition. Never
180
+ delete an advanced or protected ref merely because its name matches an old PR.
181
+ - **Repository governance drifts:** run the read-only checker, review every reported
182
+ path, and use the confirmed apply mode only for an accepted mutable policy change.
183
+ - **Nightly documentation review fails:** inspect the reported paths and rules, identify the introducing merge from the nightly interval, and repair the invariant before unrelated work proceeds.
95
184
  - **Development publication fails:** fix the cause and rerun `npm run develop`; an npm version that already exists is never overwritten.
96
185
  - **Stable publication fails before npm accepts bytes:** no tag, release, or moved branch exists. Fix the cause and release the next version.
97
186
  - **Stable publication is uncertain after npm accepted bytes:** stop and inspect registry version, digest, tag, and release. Never republish immutable bytes.
@@ -104,6 +193,8 @@ and resolved review threads, but zero approving reviews. `master` and release ta
104
193
  are written only after publication and are protected from force-push, movement, and
105
194
  deletion.
106
195
 
107
- Do not add direct-push bypasses. Ruleset mutation remains a separate administrative
108
- operation: inspect with `node scripts/governance/check-github-rulesets.mjs`, and apply only
109
- with explicit maintainer confirmation.
196
+ Do not add direct-push bypasses. Approval count, strict-base validation, repository
197
+ merge methods, Actions allowance/SHA enforcement, Dependabot, and npm environment
198
+ protection remain explicit maintainer decisions; governance automation does not
199
+ silently change them. Repository mutation remains a separate confirmed administrative
200
+ operation through `check-github-repository-governance.mjs`.
@@ -4,6 +4,8 @@ This checkpoint is user-controlled. Tests and coding agents must not launch or f
4
4
 
5
5
  ## Prepare an exact candidate
6
6
 
7
+ For a checkout smoke test in Git Bash, build once and launch with `./scripts/dev` or `./scripts/dev pi`. Do not invoke `node scripts/development/dev-launch.mjs` or `node scripts/development/start-local.mjs` directly: that bypasses the color-preserving shell `exec` shape and can collapse the pinned 24-bit palette to Git Bash's 16 terminal colors. Run `node scripts/check-terminal-colour.mjs` first when color fidelity is in doubt; the first color block must differ from the terminal-palette cyan block.
8
+
7
9
  From the candidate checkout, pack once and install that tarball into a disposable prefix:
8
10
 
9
11
  ```powershell
@@ -46,6 +48,31 @@ Use the same terminal, working directory, dimensions, environment, and equivalen
46
48
  - [ ] Normal and error exits return a usable parent prompt.
47
49
  - [ ] Parent typing, cursor movement, Backspace, Delete, and submission work after exit.
48
50
 
49
- Report failures with bare-versus-comparison behavior, platform and terminal versions, exact command, reproducibility, and optional manually captured evidence.
51
+ ### Setting-controlled visual matrix
52
+
53
+ Use equivalent values in bare A1 and the Pi comparison profile. Exercise both a wide frame and a narrow frame that forces wrapping/clipping.
54
+
55
+ - [ ] `/settings`: selected cursor, label and value accent; unselected values muted; descriptions and hints dim; search uses `> ` with inverse cursor; scalar menus, numeric controls, warning-parts dialog, deferred notice, failed write, section spacing, clipping, and hidden-entry absence match their pinned semantic roles.
56
+ - [ ] Transcript: toggle images and image width, thinking level/visibility, Mermaid mode, output padding, cache notices, warnings, auto-compaction, queue modes, command autocomplete, provider timeout/retry/error, and changelog collapse. Existing and streaming blocks must re-render without focus, selection, scroll, or queued-work loss.
57
+ - [ ] Trust startup: from an undecided project, compare selected Trust/Do not trust rows, arrow navigation, Enter, Escape/Ctrl+C, clearing, cursor state, and restoration. No project extension/theme/prompt/skill may run before selection, and a fail-closed diagnostic must appear once on the restored parent rather than above a blank fullscreen frame.
58
+ - [ ] Terminal lifecycle: toggle hardware cursor, clear-on-shrink, and terminal progress; resize smaller/larger; open/close selectors; select and copy transcript text; verify no duplicate rows, stale OSC progress, leaked mouse mode, misplaced cursor, or broken parent input.
59
+ - [ ] Images: in Kitty or iTerm2 verify inline width and clipping; in Windows Terminal verify the textual fallback and absence of image protocol bytes without hiding `showImages`.
60
+ - [ ] Fullscreen exit `transcript`: verify the parent is restored before styled user, assistant Markdown, thinking, tool, notice, warning, error, and spacing rows are printed. No overlay, draft, animation, scrollbar, or inline-image payload may appear.
61
+ - [ ] Fullscreen exit `resume-hint`: verify only dim `To resume this session:` plus `a1 --session <compact-id>` is printed for the default directory. A custom directory must place quoted `--session-dir <dir>` before `--session`; the raw default `.jsonl` path must never print.
62
+
63
+ Record acceptance with:
64
+
65
+ ```text
66
+ A1 commit/tarball:
67
+ Pi package: @earendil-works/pi-coding-agent 0.84.2
68
+ OS/version:
69
+ Terminal/version:
70
+ Terminal dimensions (wide/narrow):
71
+ Image protocol result:
72
+ Settings values exercised:
73
+ Outcome and any declared substitutions:
74
+ ```
75
+
76
+ Report failures with bare-versus-comparison behavior, platform and terminal versions, exact command, dimensions, settings values, reproducibility, and optional manually captured evidence.
50
77
 
51
78
  For recovery, use `a1 pi`; do not use the removed `a1 ui` command. Manual acceptance can authorize an exact uncertified development preview after non-desktop gates pass. It does not certify stable presentation parity or platform support.
@@ -29,9 +29,10 @@ leave the launch without a terminal on its input; the launcher says so and stops
29
29
  rather than opening a session that closes as it starts. Running `scripts/dev`
30
30
  from the shell is the direct path.
31
31
 
32
- A directly launched `node scripts/development/start-local.mjs` under Git Bash does not, and
33
- neither does a directly launched `node …/pi/dist/cli.js`: the collapse belongs to
34
- the launch, not to either application.
32
+ A directly launched `node scripts/development/dev-launch.mjs` or
33
+ `node scripts/development/start-local.mjs` under Git Bash does not, and neither
34
+ does a directly launched `node …/pi/dist/cli.js`: the collapse belongs to the
35
+ launch, not to either application.
35
36
 
36
37
  ## Checking a terminal
37
38