@nomos-arc/arc 0.1.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 (160) hide show
  1. package/.claude/settings.local.json +10 -0
  2. package/.nomos-config.json +5 -0
  3. package/CLAUDE.md +108 -0
  4. package/LICENSE +190 -0
  5. package/README.md +569 -0
  6. package/dist/cli.js +21120 -0
  7. package/docs/auth/googel_plan.yaml +1093 -0
  8. package/docs/auth/google_task.md +235 -0
  9. package/docs/auth/hardened_blueprint.yaml +1658 -0
  10. package/docs/auth/red_team_report.yaml +336 -0
  11. package/docs/auth/session_state.yaml +162 -0
  12. package/docs/certificate/cer_enhance_plan.md +605 -0
  13. package/docs/certificate/certificate_report.md +338 -0
  14. package/docs/dev_overview.md +419 -0
  15. package/docs/feature_assessment.md +156 -0
  16. package/docs/how_it_works.md +78 -0
  17. package/docs/infrastructure/map.md +867 -0
  18. package/docs/init/master_plan.md +3581 -0
  19. package/docs/init/red_team_report.md +215 -0
  20. package/docs/init/report_phase_1a.md +304 -0
  21. package/docs/integrity-gate/enhance_drift.md +703 -0
  22. package/docs/integrity-gate/overview.md +108 -0
  23. package/docs/management/manger-task.md +99 -0
  24. package/docs/management/scafffold.md +76 -0
  25. package/docs/map/ATOMIC_BLUEPRINT.md +1349 -0
  26. package/docs/map/RED_TEAM_REPORT.md +159 -0
  27. package/docs/map/map_task.md +147 -0
  28. package/docs/map/semantic_graph_task.md +792 -0
  29. package/docs/map/semantic_master_plan.md +705 -0
  30. package/docs/phase7/TEAM_RED.md +249 -0
  31. package/docs/phase7/plan.md +1682 -0
  32. package/docs/phase7/task.md +275 -0
  33. package/docs/prompts/USAGE.md +312 -0
  34. package/docs/prompts/architect.md +165 -0
  35. package/docs/prompts/executer.md +190 -0
  36. package/docs/prompts/hardener.md +190 -0
  37. package/docs/prompts/red_team.md +146 -0
  38. package/docs/verification/goveranance-overview.md +396 -0
  39. package/docs/verification/governance-overview.md +245 -0
  40. package/docs/verification/verification-arc-ar.md +560 -0
  41. package/docs/verification/verification-architecture.md +560 -0
  42. package/docs/very_next.md +52 -0
  43. package/docs/whitepaper.md +89 -0
  44. package/overview.md +1469 -0
  45. package/package.json +63 -0
  46. package/src/adapters/__tests__/git.test.ts +296 -0
  47. package/src/adapters/__tests__/stdio.test.ts +70 -0
  48. package/src/adapters/git.ts +226 -0
  49. package/src/adapters/pty.ts +159 -0
  50. package/src/adapters/stdio.ts +113 -0
  51. package/src/cli.ts +83 -0
  52. package/src/commands/apply.ts +47 -0
  53. package/src/commands/auth.ts +301 -0
  54. package/src/commands/certificate.ts +89 -0
  55. package/src/commands/discard.ts +24 -0
  56. package/src/commands/drift.ts +116 -0
  57. package/src/commands/index.ts +78 -0
  58. package/src/commands/init.ts +121 -0
  59. package/src/commands/list.ts +75 -0
  60. package/src/commands/map.ts +55 -0
  61. package/src/commands/plan.ts +30 -0
  62. package/src/commands/review.ts +58 -0
  63. package/src/commands/run.ts +63 -0
  64. package/src/commands/search.ts +147 -0
  65. package/src/commands/show.ts +63 -0
  66. package/src/commands/status.ts +59 -0
  67. package/src/core/__tests__/budget.test.ts +213 -0
  68. package/src/core/__tests__/certificate.test.ts +385 -0
  69. package/src/core/__tests__/config.test.ts +191 -0
  70. package/src/core/__tests__/preflight.test.ts +24 -0
  71. package/src/core/__tests__/prompt.test.ts +358 -0
  72. package/src/core/__tests__/review.test.ts +161 -0
  73. package/src/core/__tests__/state.test.ts +362 -0
  74. package/src/core/auth/__tests__/manager.test.ts +166 -0
  75. package/src/core/auth/__tests__/server.test.ts +220 -0
  76. package/src/core/auth/gcp-projects.ts +160 -0
  77. package/src/core/auth/manager.ts +114 -0
  78. package/src/core/auth/server.ts +141 -0
  79. package/src/core/budget.ts +119 -0
  80. package/src/core/certificate.ts +502 -0
  81. package/src/core/config.ts +212 -0
  82. package/src/core/errors.ts +54 -0
  83. package/src/core/factory.ts +49 -0
  84. package/src/core/graph/__tests__/builder.test.ts +272 -0
  85. package/src/core/graph/__tests__/contract-writer.test.ts +175 -0
  86. package/src/core/graph/__tests__/enricher.test.ts +299 -0
  87. package/src/core/graph/__tests__/parser.test.ts +200 -0
  88. package/src/core/graph/__tests__/pipeline.test.ts +202 -0
  89. package/src/core/graph/__tests__/renderer.test.ts +128 -0
  90. package/src/core/graph/__tests__/resolver.test.ts +185 -0
  91. package/src/core/graph/__tests__/scanner.test.ts +231 -0
  92. package/src/core/graph/__tests__/show.test.ts +134 -0
  93. package/src/core/graph/builder.ts +303 -0
  94. package/src/core/graph/constraints.ts +94 -0
  95. package/src/core/graph/contract-writer.ts +93 -0
  96. package/src/core/graph/drift/__tests__/classifier.test.ts +215 -0
  97. package/src/core/graph/drift/__tests__/comparator.test.ts +335 -0
  98. package/src/core/graph/drift/__tests__/drift.test.ts +453 -0
  99. package/src/core/graph/drift/__tests__/reporter.test.ts +203 -0
  100. package/src/core/graph/drift/classifier.ts +165 -0
  101. package/src/core/graph/drift/comparator.ts +205 -0
  102. package/src/core/graph/drift/reporter.ts +77 -0
  103. package/src/core/graph/enricher.ts +251 -0
  104. package/src/core/graph/grammar-paths.ts +30 -0
  105. package/src/core/graph/html-template.ts +493 -0
  106. package/src/core/graph/map-schema.ts +137 -0
  107. package/src/core/graph/parser.ts +336 -0
  108. package/src/core/graph/pipeline.ts +209 -0
  109. package/src/core/graph/renderer.ts +92 -0
  110. package/src/core/graph/resolver.ts +195 -0
  111. package/src/core/graph/scanner.ts +145 -0
  112. package/src/core/logger.ts +46 -0
  113. package/src/core/orchestrator.ts +792 -0
  114. package/src/core/plan-file-manager.ts +66 -0
  115. package/src/core/preflight.ts +64 -0
  116. package/src/core/prompt.ts +173 -0
  117. package/src/core/review.ts +95 -0
  118. package/src/core/state.ts +294 -0
  119. package/src/core/worktree-coordinator.ts +77 -0
  120. package/src/search/__tests__/chunk-extractor.test.ts +339 -0
  121. package/src/search/__tests__/embedder-auth.test.ts +124 -0
  122. package/src/search/__tests__/embedder.test.ts +267 -0
  123. package/src/search/__tests__/graph-enricher.test.ts +178 -0
  124. package/src/search/__tests__/indexer.test.ts +518 -0
  125. package/src/search/__tests__/integration.test.ts +649 -0
  126. package/src/search/__tests__/query-engine.test.ts +334 -0
  127. package/src/search/__tests__/similarity.test.ts +78 -0
  128. package/src/search/__tests__/vector-store.test.ts +281 -0
  129. package/src/search/chunk-extractor.ts +167 -0
  130. package/src/search/embedder.ts +209 -0
  131. package/src/search/graph-enricher.ts +95 -0
  132. package/src/search/indexer.ts +483 -0
  133. package/src/search/lexical-searcher.ts +190 -0
  134. package/src/search/query-engine.ts +225 -0
  135. package/src/search/vector-store.ts +311 -0
  136. package/src/types/index.ts +572 -0
  137. package/src/utils/__tests__/ansi.test.ts +54 -0
  138. package/src/utils/__tests__/frontmatter.test.ts +79 -0
  139. package/src/utils/__tests__/sanitize.test.ts +229 -0
  140. package/src/utils/ansi.ts +19 -0
  141. package/src/utils/context.ts +44 -0
  142. package/src/utils/frontmatter.ts +27 -0
  143. package/src/utils/sanitize.ts +78 -0
  144. package/test/e2e/lifecycle.test.ts +330 -0
  145. package/test/fixtures/mock-planner-hang.ts +5 -0
  146. package/test/fixtures/mock-planner.ts +26 -0
  147. package/test/fixtures/mock-reviewer-bad.ts +8 -0
  148. package/test/fixtures/mock-reviewer-retry.ts +34 -0
  149. package/test/fixtures/mock-reviewer.ts +18 -0
  150. package/test/fixtures/sample-project/src/circular-a.ts +6 -0
  151. package/test/fixtures/sample-project/src/circular-b.ts +6 -0
  152. package/test/fixtures/sample-project/src/config.ts +15 -0
  153. package/test/fixtures/sample-project/src/main.ts +19 -0
  154. package/test/fixtures/sample-project/src/services/product-service.ts +20 -0
  155. package/test/fixtures/sample-project/src/services/user-service.ts +18 -0
  156. package/test/fixtures/sample-project/src/types.ts +14 -0
  157. package/test/fixtures/sample-project/src/utils/index.ts +14 -0
  158. package/test/fixtures/sample-project/src/utils/validate.ts +12 -0
  159. package/tsconfig.json +20 -0
  160. package/vitest.config.ts +12 -0
@@ -0,0 +1,336 @@
1
+ audit:
2
+ task_id: "auth-oauth-login"
3
+ task_title: "Frictionless OAuth 2.0 Google Login for arc CLI"
4
+ audited_at: "2026-04-06T12:00:00Z"
5
+
6
+ verdict: "REVISE"
7
+ system_integrity_score: 62
8
+ total_findings: 9
9
+ findings_by_severity: { critical: 1, high: 3, medium: 4, low: 1 }
10
+
11
+ findings:
12
+ - finding_id: "F-001"
13
+ domain: "security"
14
+ severity: "critical"
15
+ step_id: "3.1"
16
+ title: "Client secret exposed via CLI flag, shell history, and process list"
17
+ description: |
18
+ The plan specifies `--client-secret <secret>` as a Commander.js option for
19
+ `arc auth login`. Any value passed via CLI flag is:
20
+ 1. Stored in shell history (~/.bash_history, ~/.zsh_history)
21
+ 2. Visible in `ps aux` process list to all users on the system
22
+ 3. Potentially logged by corporate endpoint monitoring tools
23
+ client_secret is a long-lived credential that cannot be rotated easily.
24
+ attack_vector: |
25
+ Any user or process with read access to shell history or /proc can
26
+ extract the client_secret. On shared servers this is trivial:
27
+ `grep 'client-secret' ~/.bash_history`
28
+ evidence: |
29
+ Step 3.1, lines 397-398: `.option('--client-id <id>', ...)` and
30
+ `.option('--client-secret <secret>', ...)`. No mention of stdin/prompt
31
+ fallback or env var alternative for the secret.
32
+ recommendation: |
33
+ Remove `--client-secret` CLI flag entirely. Instead:
34
+ 1. Primary: Read from config file (`auth.client_secret` in .nomos-config.json)
35
+ 2. Fallback: Read from env var `NOMOS_GOOGLE_CLIENT_SECRET`
36
+ 3. Last resort: Interactive prompt via `readline` (masked input)
37
+ Keep `--client-id` as a flag (not sensitive). Update step 3.1 description
38
+ and the priority resolution logic accordingly.
39
+
40
+ - finding_id: "F-002"
41
+ domain: "security"
42
+ severity: "high"
43
+ step_id: "2.2"
44
+ title: "Loopback server has no CSRF protection on OAuth callback"
45
+ description: |
46
+ The loopback server at step 2.2 accepts any GET request with a `code`
47
+ parameter and exchanges it for tokens. There is no `state` parameter
48
+ validation. RFC 6749 Section 10.12 requires a `state` parameter to
49
+ prevent CSRF attacks on the OAuth callback.
50
+ attack_vector: |
51
+ An attacker who knows the user is running `arc auth login` can craft a
52
+ URL like `http://localhost:3000/?code=ATTACKER_CODE` and trick the user
53
+ into visiting it (e.g., via a malicious page with an img tag). The CLI
54
+ would exchange the attacker's code, potentially linking the user's CLI
55
+ to an attacker-controlled Google account.
56
+ evidence: |
57
+ Step 2.2, lines 328-330: Callback handler parses `code` from `req.url`
58
+ but makes no mention of generating, sending, or validating a `state`
59
+ parameter. The `generateAuthUrl` call (lines 316-320) does not include
60
+ a `state` field.
61
+ recommendation: |
62
+ In step 2.2, add to the auth URL generation:
63
+ ```
64
+ const state = crypto.randomBytes(32).toString('hex');
65
+ oauth2Client.generateAuthUrl({ ..., state });
66
+ ```
67
+ In the callback handler, verify `req.url` contains the matching `state`
68
+ value before exchanging the code. Reject with 403 if mismatched.
69
+ Add `crypto` to the imports list.
70
+
71
+ - finding_id: "F-003"
72
+ domain: "architecture"
73
+ severity: "high"
74
+ step_id: "4.1"
75
+ title: "OAuth access_token used as API key may not authenticate Gemini API"
76
+ description: |
77
+ The plan passes the OAuth access_token directly to `new GoogleGenerativeAI(key)`
78
+ as if it were an API key. The @google/generative-ai SDK (^0.24.1) uses this
79
+ value as `x-goog-api-key` header. OAuth access tokens are Bearer tokens and
80
+ must be sent as `Authorization: Bearer <token>`. These are fundamentally
81
+ different authentication mechanisms.
82
+ attack_vector: |
83
+ Not a security attack — this is a functional failure. The entire OAuth
84
+ credential chain (steps 4.1, 4.2, 4.3, 4.4, 4.5, 4.6) is non-functional
85
+ if the SDK rejects bearer tokens passed as API keys.
86
+ evidence: |
87
+ Step 4.1, lines 531-533: `return new Embedder(config, logger, token)` where
88
+ `token` is from `authManager.getAccessToken()`. The Embedder constructor
89
+ (src/search/embedder.ts:36) passes this to `new GoogleGenerativeAI(key)`.
90
+ The plan acknowledges this risk in `risk_assessment.failure_scenarios[0]`
91
+ (lines 1033-1041) but proposes no concrete fallback step — only a vague
92
+ "fall back to custom Authorization header injection."
93
+ recommendation: |
94
+ Add a concrete step between 2.1 and 4.1: a spike/validation step that
95
+ tests whether `new GoogleGenerativeAI(accessToken)` succeeds with an
96
+ OAuth token. If it fails, the plan MUST include the alternative path
97
+ (custom request headers or `GoogleAuth` credential injection) as actual
98
+ steps, not just a mitigation note. Without this, steps 4.1-4.6 are
99
+ speculative.
100
+
101
+ - finding_id: "F-004"
102
+ domain: "ai_divergence"
103
+ severity: "high"
104
+ step_id: "3.1"
105
+ title: "Action handlers contain `...` placeholders — executor will improvise"
106
+ description: |
107
+ The code blocks for `arc auth login`, `arc auth logout`, and
108
+ `arc auth status` actions all contain `.action(async (opts) => { ... })`
109
+ with literal `...` as the implementation body. The detailed flow is
110
+ described in prose below the code block, but an AI executor seeing
111
+ `{ ... }` in a code template is likely to either:
112
+ 1. Generate a minimal stub that doesn't match the prose spec
113
+ 2. Invent its own flow that diverges from the described steps
114
+ evidence: |
115
+ Step 3.1, line 398: `.action(async (opts) => { ... });`
116
+ Step 3.1, line 404: `.action(async () => { ... });`
117
+ Step 3.1, line 410: `.action(async () => { ... });`
118
+ The actual implementation details are in prose paragraphs starting
119
+ at lines 414, 426, and 431 — separated from the code template.
120
+ recommendation: |
121
+ Replace `{ ... }` with full implementation code in the code block,
122
+ or at minimum replace with explicit pseudocode comments:
123
+ ```typescript
124
+ .action(async (opts) => {
125
+ // 1. loadConfig()
126
+ // 2. resolve clientId/clientSecret (flag > config > throw)
127
+ // 3. new OAuth2Client({ clientId, clientSecret })
128
+ // 4. new AuthManager(config.auth, logger)
129
+ // 5. startLoopbackServer(...)
130
+ // 6. authManager.saveCredentials(tokens)
131
+ // 7. console.log success
132
+ });
133
+ ```
134
+
135
+ - finding_id: "F-005"
136
+ domain: "ai_divergence"
137
+ severity: "medium"
138
+ step_id: "4.6"
139
+ title: "Single step modifies 3 files with 'same pattern' shorthand"
140
+ description: |
141
+ Step 4.6 modifies src/commands/index.ts, src/commands/search.ts, and
142
+ src/commands/map.ts. Only index.ts gets detailed code changes. The other
143
+ two files get "Same pattern" one-liners. An AI executor may:
144
+ 1. Apply changes only to index.ts and skip the other two
145
+ 2. Apply literally identical code without adapting to each file's
146
+ specific constructor (QueryEngine vs MapPipeline)
147
+ evidence: |
148
+ Step 4.6, lines 816-818: "Also update src/commands/search.ts: Same pattern"
149
+ Step 4.6, lines 820-822: "Also update src/commands/map.ts: Same pattern"
150
+ Meanwhile src/commands/search.ts instantiates `QueryEngine` (not SearchIndexer)
151
+ and src/commands/map.ts instantiates `MapPipeline` with a different constructor
152
+ signature: `new MapPipeline(config, projectRoot, logger)` (config first,
153
+ projectRoot second — reversed from SearchIndexer).
154
+ recommendation: |
155
+ Split step 4.6 into three separate steps (4.6a, 4.6b, 4.6c) — one per
156
+ file — each with explicit code showing the exact constructor call change.
157
+ At minimum, show the MapPipeline change explicitly since its constructor
158
+ parameter order differs from SearchIndexer.
159
+
160
+ - finding_id: "F-006"
161
+ domain: "ai_divergence"
162
+ severity: "medium"
163
+ step_id: "5.1"
164
+ title: "Ambiguous action — 'No modification expected unless a gap is found'"
165
+ description: |
166
+ Step 5.1 says the action is MODIFY but the description concludes with
167
+ "No modification expected unless a gap is found." This gives the AI
168
+ executor discretion to either modify or not modify the file based on
169
+ its own judgment. The executor could:
170
+ 1. Skip the step entirely (interpreting "no change needed")
171
+ 2. Add unnecessary patterns to ALWAYS_DENY
172
+ 3. Misidentify a non-gap as a gap and make harmful changes
173
+ evidence: |
174
+ Step 5.1, lines 853-863: "No code change needed if the existing patterns
175
+ already cover... Action: Read the file, verify coverage, document finding.
176
+ No modification expected unless a gap is found."
177
+ Current ALWAYS_DENY (src/utils/sanitize.ts:57-61) already covers
178
+ `GOOGLE_.*?(KEY|SECRET|TOKEN|PASSWORD|CREDENTIAL)` which matches
179
+ Google OAuth env vars.
180
+ recommendation: |
181
+ Change step 5.1 action from "MODIFY" to "VERIFY". Remove the conditional
182
+ language. State explicitly: "Verify that the regex
183
+ `GOOGLE_.*?(KEY|SECRET|TOKEN|...)` in ALWAYS_DENY covers Google OAuth
184
+ env vars. Expected result: no changes needed. If the regex does NOT
185
+ match `GOOGLE_CLIENT_SECRET`, add it and report the gap."
186
+
187
+ - finding_id: "F-007"
188
+ domain: "rollback"
189
+ severity: "medium"
190
+ step_id: "4.3"
191
+ title: "Sync-to-async accessor change breaks rollback of downstream steps"
192
+ description: |
193
+ Step 4.3 changes `private get embedder(): Embedder` (synchronous accessor)
194
+ to `private async getEmbedder(): Promise<Embedder>` (async method). This
195
+ changes every call site from `this.embedder.X()` to
196
+ `(await this.getEmbedder()).X()`. If step 4.3 is rolled back but step 4.6
197
+ was already applied, `src/commands/index.ts` will pass `authManager` to a
198
+ constructor that no longer accepts it.
199
+ More critically, if 4.3 is partially applied (accessor changed but not all
200
+ call sites updated), the file won't compile.
201
+ evidence: |
202
+ Step 4.3 depends_on: ["4.1"]. Step 4.6 depends_on: ["4.3", "4.4", "4.5"].
203
+ Rollback of 4.3 says "Revert the lazy accessor and constructor changes"
204
+ but does not mention that 4.6 must also be reverted first.
205
+ recommendation: |
206
+ Add explicit rollback ordering: "Rollback 4.6 BEFORE rolling back 4.3."
207
+ Add a `rollback_depends_on: ["4.6"]` field or equivalent note to step 4.3.
208
+ Same applies to steps 4.1, 4.2, 4.4, 4.5.
209
+
210
+ - finding_id: "F-008"
211
+ domain: "architecture"
212
+ severity: "medium"
213
+ step_id: "2.1"
214
+ title: "loadCredentials() uses synchronous fs.readFileSync in async-capable class"
215
+ description: |
216
+ Step 2.1 specifies `loadCredentials(): AuthCredentials | null` as a
217
+ synchronous method using `fs.readFileSync`. This blocks the Node.js event
218
+ loop during file I/O. While credentials.json is small, this breaks the
219
+ project's pattern — the codebase consistently uses async fs operations
220
+ (e.g., indexer.ts writeMeta uses `fs.writeFile` from `node:fs/promises`).
221
+ The method is called from `isLoggedIn()` (also sync) and
222
+ `getAuthenticatedClient()` (async), so making it async would only require
223
+ updating `getAuthenticatedClient`.
224
+ evidence: |
225
+ Step 2.1, lines 242-244: "Synchronous read. Return null if file doesn't
226
+ exist or is invalid JSON."
227
+ Contrast with src/search/indexer.ts lines 439-444 which uses async
228
+ `fs.writeFile` + `fs.rename` for atomic writes.
229
+ `isLoggedIn()` (line 256) calls `loadCredentials()` synchronously.
230
+ recommendation: |
231
+ Accept the sync design for `loadCredentials()` and `isLoggedIn()` since
232
+ they are called in non-hot-path contexts (CLI startup, not per-request).
233
+ However, add a negative constraint: "DO NOT use readFileSync in any
234
+ method that could be called in a loop or hot path." This is a MEDIUM
235
+ concern — not blocking, but worth noting for the planner.
236
+
237
+ - finding_id: "F-009"
238
+ domain: "security"
239
+ severity: "low"
240
+ step_id: "2.2"
241
+ title: "Success HTML response could be used for phishing if server lingers"
242
+ description: |
243
+ The loopback server responds with a success HTML page. If the cleanup
244
+ is delayed or the server is somehow kept alive, a local attacker could
245
+ serve content on localhost:3000. This is low risk because the server
246
+ has a 120s timeout and socket tracking.
247
+ attack_vector: |
248
+ Theoretical: if socket cleanup fails, server continues to serve the
249
+ success HTML page, which could be replaced by a MITM on localhost
250
+ (extremely unlikely on modern systems).
251
+ evidence: |
252
+ Step 2.2, lines 331-332: "Respond with success HTML: simple page saying
253
+ 'Login successful! You can close this tab.'"
254
+ Timeout: 120 seconds (line 334).
255
+ recommendation: |
256
+ Ensure the success response includes `Connection: close` header and
257
+ that `server.close()` is called immediately after sending the response,
258
+ not just on timeout. The plan describes this at line 343-344 but should
259
+ make it explicit in the code template.
260
+
261
+ hardening_instructions:
262
+ - step_id: "3.1"
263
+ instruction: "Remove --client-secret CLI flag. Support config file and env var NOMOS_GOOGLE_CLIENT_SECRET only. Add interactive masked prompt as last-resort fallback."
264
+ reason: "CLI flags expose secrets in shell history and process lists."
265
+ priority: "critical"
266
+
267
+ - step_id: "2.2"
268
+ instruction: "Add cryptographic state parameter to OAuth flow. Generate with crypto.randomBytes(32), pass in generateAuthUrl, validate in callback handler before token exchange."
269
+ reason: "RFC 6749 Section 10.12 requires state parameter to prevent CSRF on OAuth callbacks."
270
+ priority: "high"
271
+
272
+ - step_id: "4.1"
273
+ instruction: "Add a validation step before 4.1 that tests GoogleGenerativeAI SDK with an OAuth access_token. If it fails, provide concrete alternative steps using Authorization header injection."
274
+ reason: "The entire credential chain (6 steps) depends on an unverified assumption about SDK behavior."
275
+ priority: "high"
276
+
277
+ - step_id: "3.1"
278
+ instruction: "Replace `{ ... }` placeholders in code blocks with full implementation or detailed pseudocode comments."
279
+ reason: "Reduces AI executor improvisation risk."
280
+ priority: "high"
281
+
282
+ - step_id: "4.6"
283
+ instruction: "Split into 3 separate steps with explicit code for each file, especially showing MapPipeline's different constructor parameter order."
284
+ reason: "Prevents executor from applying incorrect 'same pattern' changes."
285
+ priority: "medium"
286
+
287
+ - step_id: "5.1"
288
+ instruction: "Change action from MODIFY to VERIFY. Remove conditional modification language."
289
+ reason: "Removes ambiguity about whether the executor should change the file."
290
+ priority: "medium"
291
+
292
+ negative_constraints:
293
+ - "DO NOT pass client_secret via CLI flags — it leaks to shell history and process lists"
294
+ - "DO NOT exchange OAuth authorization codes without validating the state parameter"
295
+ - "DO NOT modify src/core/state.ts — it manages task state and is unrelated to auth"
296
+ - "DO NOT modify src/core/orchestrator.ts — orchestration loop must remain unchanged"
297
+ - "DO NOT modify src/core/budget.ts — budget tracking is unrelated to auth"
298
+ - "DO NOT modify src/search/vector-store.ts or src/search/chunk-extractor.ts"
299
+ - "DO NOT add new dependencies beyond google-auth-library — open is already installed"
300
+ - "DO NOT store tokens in environment variables — use file-based storage only"
301
+ - "DO NOT log access_token, refresh_token, or client_secret values at any log level"
302
+ - "DO NOT use readFileSync in any method that could be called in a loop or hot path"
303
+
304
+ rollback_assessment:
305
+ is_fully_reversible: false
306
+ weak_points: ["4.3", "4.4", "4.5"]
307
+ cascade_risks:
308
+ - "Steps 4.3-4.6 form a tight dependency chain. Rolling back 4.3 (sync-to-async accessor in indexer.ts) without also rolling back 4.6 (command wiring) leaves index.ts passing authManager to a constructor that doesn't accept it. Rollback must proceed in reverse order: 4.6 -> 4.5 -> 4.4 -> 4.3."
309
+ - "Step 1.1 (npm install) modifies package-lock.json. npm uninstall may not produce a byte-identical package-lock.json, creating noise in git diff."
310
+ - "If step 6.2 (test suite) discovers failures after steps 4.1-4.6 are applied, selective rollback of individual steps is unsafe — all of Phase 4 must be rolled back together."
311
+
312
+ summary:
313
+ strengths:
314
+ - "Detailed step-by-step with explicit code blocks, file paths, and line numbers"
315
+ - "Additive factory method pattern preserves backward compatibility"
316
+ - "Clear dependency graph with can_parallel flags"
317
+ - "Comprehensive risk assessment with failure scenarios and mitigations"
318
+ - "Fragile zone identification correctly protects state.ts, orchestrator.ts, budget.ts"
319
+ - "Credential chain priority (env var -> OAuth -> error) is well-designed"
320
+ - "Socket tracking and timeout on loopback server prevents dangling processes"
321
+ critical_gaps:
322
+ - "Client secret exposed via CLI flag (F-001) — CRITICAL security issue"
323
+ - "No OAuth state parameter for CSRF protection (F-002)"
324
+ - "Unverified assumption that OAuth tokens work as GoogleGenerativeAI API keys (F-003)"
325
+ - "Placeholder code blocks risk AI executor divergence (F-004)"
326
+ recommendation: |
327
+ The plan is well-structured and follows existing codebase conventions closely.
328
+ However, it must be revised before execution due to one critical and three high
329
+ findings. The most urgent fix is removing the --client-secret CLI flag (F-001)
330
+ and replacing it with config/env/prompt-based secret injection. Second, add
331
+ OAuth state parameter validation to the loopback server (F-002) per RFC 6749.
332
+ Third, add a concrete validation step for the OAuth-token-as-API-key assumption
333
+ (F-003) — without this, 6 steps in Phase 4 are built on speculation. Finally,
334
+ replace the `{ ... }` placeholders in step 3.1 with actual implementation code
335
+ to prevent executor divergence (F-004). With these four fixes, the plan moves
336
+ from REVISE to APPROVE_WITH_NOTES.
@@ -0,0 +1,162 @@
1
+ execution_report:
2
+ task_id: "auth-oauth-login"
3
+ task_title: "Frictionless OAuth 2.0 Google Login for arc CLI"
4
+ phase_executed: 6
5
+ session_id: "2026-04-06T10:27:00Z"
6
+ status: "SUCCESS"
7
+
8
+ pre_execution_gates:
9
+ blueprint_integrity: "PASSED"
10
+ phase_exists: "PASSED"
11
+ dependencies_satisfied: "PASSED"
12
+ idempotency_check: "PASSED (no phase 6 steps in prior state)"
13
+ gate_result: "ALL_PASSED"
14
+ abort_reason: ""
15
+
16
+ steps_log:
17
+ - step_id: "6.1"
18
+ status: "SUCCESS"
19
+ pre_condition: "PASSED"
20
+ constraints_checked: true
21
+ execution_note: |
22
+ ROLLBACK INVOKED then RE-EXECUTED.
23
+ Initial run: `npm run lint` (tsc --noEmit) produced 2 TypeScript errors:
24
+ - src/search/__tests__/indexer.test.ts(22,16): error TS2339: Property 'create'
25
+ does not exist on type 'Mock<Procedure>'
26
+ - src/search/__tests__/query-engine.test.ts(26,16): same error
27
+ Root cause: both test files assigned `EmbedderMock.create = vi.fn()...` after
28
+ constructing with `vi.fn()`, which TypeScript does not recognize as a valid
29
+ property assignment on `Mock<Procedure>`.
30
+ Fix: replaced assignment pattern with `Object.assign(vi.fn()..., { create: vi.fn()... })`
31
+ in both test files. Re-run: `npm run lint` exits 0.
32
+ validation_result: "PASSED — tsc --noEmit exits 0"
33
+ rollback_executed: true
34
+ rollback_result: "Fixed TS2339 errors in indexer.test.ts and query-engine.test.ts"
35
+ duration_ms: 0
36
+
37
+ - step_id: "6.2"
38
+ status: "SUCCESS"
39
+ pre_condition: "PASSED"
40
+ constraints_checked: true
41
+ execution_note: |
42
+ `npm test` (vitest run) completed: 29 test files, 345 tests passed, 6 skipped.
43
+ No unexpected failures. All existing tests remain green after auth changes.
44
+ validation_result: "PASSED — npm test exits 0"
45
+ rollback_executed: false
46
+ rollback_result: ""
47
+ duration_ms: 0
48
+
49
+ - step_id: "6.3"
50
+ status: "SUCCESS"
51
+ pre_condition: "PASSED — AuthManager (step 2.1) exists at src/core/auth/manager.ts"
52
+ constraints_checked: true
53
+ execution_note: |
54
+ Created src/core/auth/__tests__/manager.test.ts with 13 unit tests covering:
55
+ - saveCredentials: writes JSON, creates parent dirs, sets 0600 permissions
56
+ - loadCredentials: returns parsed data, null on missing file, null on invalid JSON,
57
+ null on missing required fields
58
+ - isLoggedIn: true with refresh_token, false without file, false on empty token
59
+ - clearCredentials: deletes file, no-throw when missing
60
+ - getAccessToken: throws auth_not_logged_in when no credentials
61
+ Uses real temp directory (os.tmpdir()) with afterEach cleanup — no fs mocking.
62
+ validation_result: "PASSED — npx vitest run src/core/auth/__tests__/manager.test.ts exits 0 (13/13)"
63
+ rollback_executed: false
64
+ rollback_result: ""
65
+ duration_ms: 0
66
+
67
+ - step_id: "6.4"
68
+ status: "SUCCESS"
69
+ pre_condition: "PASSED — startLoopbackServer (step 2.2) exists at src/core/auth/server.ts"
70
+ constraints_checked: true
71
+ execution_note: |
72
+ Created src/core/auth/__tests__/server.test.ts with 5 unit tests covering:
73
+ - Port binding: server starts and resolves with credentials
74
+ - Timeout: server rejects after 120s (fake timers via vi.useFakeTimers + vi.advanceTimersByTimeAsync)
75
+ - Callback: resolves with credentials for valid code + state
76
+ - CSRF state validation (F-002): 403 returned for wrong state, getToken NOT called
77
+ - Cleanup: server is closed after successful callback
78
+ Mocked node:crypto randomBytes to yield deterministic FIXED_STATE for tests.
79
+ Mocked `open` to prevent browser launch.
80
+ Fixed unhandled-rejection warning in timeout test by attaching .catch() before timer advance.
81
+ validation_result: "PASSED — npx vitest run src/core/auth/__tests__/server.test.ts exits 0 (5/5)"
82
+ rollback_executed: false
83
+ rollback_result: ""
84
+ duration_ms: 0
85
+
86
+ - step_id: "6.5"
87
+ status: "SUCCESS"
88
+ pre_condition: "PASSED — Embedder.create() (step 4.1) exists at src/search/embedder.ts"
89
+ constraints_checked: true
90
+ execution_note: |
91
+ Created src/search/__tests__/embedder-auth.test.ts with 4 integration tests covering:
92
+ - API key priority: GEMINI_API_KEY set → authManager not consulted
93
+ - OAuth fallback: env key unset, isLoggedIn=true → getAccessToken called
94
+ - Neither available: env key unset, isLoggedIn=false → throws search_api_key_missing
95
+ with message mentioning both GEMINI_API_KEY and arc auth login
96
+ - No authManager: passes null → throws search_api_key_missing
97
+ Fixed @google/generative-ai mock to use regular function (not arrow) as constructor.
98
+ validation_result: "PASSED — npx vitest run src/search/__tests__/embedder-auth.test.ts exits 0 (4/4)"
99
+ rollback_executed: false
100
+ rollback_result: ""
101
+ duration_ms: 0
102
+
103
+ session_state:
104
+ blueprint_version: "2.0-HARDENED"
105
+ last_executed_phase: 6
106
+ completed_steps:
107
+ - "1.1"
108
+ - "1.2"
109
+ - "1.3"
110
+ - "1.4"
111
+ - "2.1"
112
+ - "2.2"
113
+ - "3.1"
114
+ - "3.2"
115
+ - "3.5"
116
+ - "4.1"
117
+ - "4.2"
118
+ - "4.3"
119
+ - "4.4"
120
+ - "4.5"
121
+ - "4.6a"
122
+ - "4.6b"
123
+ - "4.6c"
124
+ - "5.1"
125
+ - "6.1"
126
+ - "6.2"
127
+ - "6.3"
128
+ - "6.4"
129
+ - "6.5"
130
+ failed_step: ""
131
+ is_phase_complete: true
132
+ next_phase: null
133
+
134
+ state_delta:
135
+ files_created:
136
+ - "src/core/auth/__tests__/manager.test.ts"
137
+ - "src/core/auth/__tests__/server.test.ts"
138
+ - "src/search/__tests__/embedder-auth.test.ts"
139
+ files_modified:
140
+ - "src/search/__tests__/indexer.test.ts"
141
+ - "src/search/__tests__/query-engine.test.ts"
142
+ files_deleted: []
143
+ configs_changed: []
144
+ other_changes: []
145
+
146
+ post_mortem: {}
147
+
148
+ phase_summary:
149
+ total_steps_in_phase: 5
150
+ executed: 5
151
+ skipped: 0
152
+ succeeded: 5
153
+ failed: 0
154
+ system_stable: true
155
+ ready_for_next_phase: false
156
+ next_phase_number: null
157
+ handoff_message: |
158
+ Phase 6 complete — all validation and testing steps succeeded.
159
+ TypeScript compiles clean (0 errors). Full suite: 32 files, 367 tests pass.
160
+ New test coverage: AuthManager (13 tests), loopback server (5 tests, CSRF F-002 covered),
161
+ Embedder credential chain (4 tests). Blueprint task auth-oauth-login is fully complete.
162
+ No further phases remain.