linkedin-automation-cli 1.0.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.
- package/.env.example +12 -0
- package/.github/workflows/ci.yml +66 -0
- package/.github/workflows/publish.yml +48 -0
- package/.husky/pre-commit +6 -0
- package/.prettierignore +4 -0
- package/.prettierrc +10 -0
- package/AGENTS.md +294 -0
- package/CHANGELOG.md +40 -0
- package/GIT_RELEASE.md +167 -0
- package/LICENSE +21 -0
- package/Makefile +30 -0
- package/NPM_PUBLISHING.md +230 -0
- package/PYEOF +0 -0
- package/README.md +295 -0
- package/TESTING-GUIDE.md +151 -0
- package/cmd/linkedin/main.go +9 -0
- package/dist/agent/action-executor.d.ts +81 -0
- package/dist/agent/action-executor.d.ts.map +1 -0
- package/dist/agent/action-executor.js +170 -0
- package/dist/agent/action-executor.js.map +1 -0
- package/dist/agent/action-executor.test.d.ts +2 -0
- package/dist/agent/action-executor.test.d.ts.map +1 -0
- package/dist/agent/action-executor.test.js +366 -0
- package/dist/agent/action-executor.test.js.map +1 -0
- package/dist/agent/claude-client.d.ts +74 -0
- package/dist/agent/claude-client.d.ts.map +1 -0
- package/dist/agent/claude-client.js +314 -0
- package/dist/agent/claude-client.js.map +1 -0
- package/dist/agent/claude-client.test.d.ts +2 -0
- package/dist/agent/claude-client.test.d.ts.map +1 -0
- package/dist/agent/claude-client.test.js +590 -0
- package/dist/agent/claude-client.test.js.map +1 -0
- package/dist/agent/dom-extractor.d.ts +50 -0
- package/dist/agent/dom-extractor.d.ts.map +1 -0
- package/dist/agent/dom-extractor.js +374 -0
- package/dist/agent/dom-extractor.js.map +1 -0
- package/dist/agent/dom-extractor.test.d.ts +7 -0
- package/dist/agent/dom-extractor.test.d.ts.map +1 -0
- package/dist/agent/dom-extractor.test.js +504 -0
- package/dist/agent/dom-extractor.test.js.map +1 -0
- package/dist/agent/extension-client.d.ts +75 -0
- package/dist/agent/extension-client.d.ts.map +1 -0
- package/dist/agent/extension-client.js +245 -0
- package/dist/agent/extension-client.js.map +1 -0
- package/dist/agent/index.d.ts +8 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +16 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/page-agent.d.ts +76 -0
- package/dist/agent/page-agent.d.ts.map +1 -0
- package/dist/agent/page-agent.js +236 -0
- package/dist/agent/page-agent.js.map +1 -0
- package/dist/agent/types.d.ts +236 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +37 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/cli/agent-commands.d.ts +3 -0
- package/dist/cli/agent-commands.d.ts.map +1 -0
- package/dist/cli/agent-commands.js +250 -0
- package/dist/cli/agent-commands.js.map +1 -0
- package/dist/cli/auth.d.ts +3 -0
- package/dist/cli/auth.d.ts.map +1 -0
- package/dist/cli/auth.js +288 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/company.d.ts +3 -0
- package/dist/cli/company.d.ts.map +1 -0
- package/dist/cli/company.js +55 -0
- package/dist/cli/company.js.map +1 -0
- package/dist/cli/connection.d.ts +3 -0
- package/dist/cli/connection.d.ts.map +1 -0
- package/dist/cli/connection.js +79 -0
- package/dist/cli/connection.js.map +1 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +17 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/messages.d.ts +3 -0
- package/dist/cli/messages.d.ts.map +1 -0
- package/dist/cli/messages.js +268 -0
- package/dist/cli/messages.js.map +1 -0
- package/dist/cli/profile.d.ts +3 -0
- package/dist/cli/profile.d.ts.map +1 -0
- package/dist/cli/profile.js +81 -0
- package/dist/cli/profile.js.map +1 -0
- package/dist/cli/profile.test.d.ts +2 -0
- package/dist/cli/profile.test.d.ts.map +1 -0
- package/dist/cli/profile.test.js +15 -0
- package/dist/cli/profile.test.js.map +1 -0
- package/dist/cli/reply.d.ts +3 -0
- package/dist/cli/reply.d.ts.map +1 -0
- package/dist/cli/reply.js +129 -0
- package/dist/cli/reply.js.map +1 -0
- package/dist/core/audit.d.ts +17 -0
- package/dist/core/audit.d.ts.map +1 -0
- package/dist/core/audit.js +121 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/audit.test.d.ts +2 -0
- package/dist/core/audit.test.d.ts.map +1 -0
- package/dist/core/audit.test.js +142 -0
- package/dist/core/audit.test.js.map +1 -0
- package/dist/core/browser-cookies.d.ts +19 -0
- package/dist/core/browser-cookies.d.ts.map +1 -0
- package/dist/core/browser-cookies.js +181 -0
- package/dist/core/browser-cookies.js.map +1 -0
- package/dist/core/browser.d.ts +50 -0
- package/dist/core/browser.d.ts.map +1 -0
- package/dist/core/browser.js +318 -0
- package/dist/core/browser.js.map +1 -0
- package/dist/core/config.d.ts +20 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +103 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/config.test.d.ts +2 -0
- package/dist/core/config.test.d.ts.map +1 -0
- package/dist/core/config.test.js +111 -0
- package/dist/core/config.test.js.map +1 -0
- package/dist/core/storage.d.ts +19 -0
- package/dist/core/storage.d.ts.map +1 -0
- package/dist/core/storage.js +124 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/core/storage.test.d.ts +2 -0
- package/dist/core/storage.test.d.ts.map +1 -0
- package/dist/core/storage.test.js +142 -0
- package/dist/core/storage.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/linkedin/auth.d.ts +22 -0
- package/dist/linkedin/auth.d.ts.map +1 -0
- package/dist/linkedin/auth.js +167 -0
- package/dist/linkedin/auth.js.map +1 -0
- package/dist/linkedin/company-extractor.d.ts +36 -0
- package/dist/linkedin/company-extractor.d.ts.map +1 -0
- package/dist/linkedin/company-extractor.js +211 -0
- package/dist/linkedin/company-extractor.js.map +1 -0
- package/dist/linkedin/company-extractor.test.d.ts +2 -0
- package/dist/linkedin/company-extractor.test.d.ts.map +1 -0
- package/dist/linkedin/company-extractor.test.js +52 -0
- package/dist/linkedin/company-extractor.test.js.map +1 -0
- package/dist/linkedin/connector.d.ts +45 -0
- package/dist/linkedin/connector.d.ts.map +1 -0
- package/dist/linkedin/connector.js +245 -0
- package/dist/linkedin/connector.js.map +1 -0
- package/dist/linkedin/message-sender.d.ts +32 -0
- package/dist/linkedin/message-sender.d.ts.map +1 -0
- package/dist/linkedin/message-sender.js +112 -0
- package/dist/linkedin/message-sender.js.map +1 -0
- package/dist/linkedin/messages.d.ts +78 -0
- package/dist/linkedin/messages.d.ts.map +1 -0
- package/dist/linkedin/messages.js +745 -0
- package/dist/linkedin/messages.js.map +1 -0
- package/dist/linkedin/profile.d.ts +37 -0
- package/dist/linkedin/profile.d.ts.map +1 -0
- package/dist/linkedin/profile.js +268 -0
- package/dist/linkedin/profile.js.map +1 -0
- package/dist/linkedin/profile.test.d.ts +2 -0
- package/dist/linkedin/profile.test.d.ts.map +1 -0
- package/dist/linkedin/profile.test.js +68 -0
- package/dist/linkedin/profile.test.js.map +1 -0
- package/dist/linkedin/reply.d.ts +21 -0
- package/dist/linkedin/reply.d.ts.map +1 -0
- package/dist/linkedin/reply.js +76 -0
- package/dist/linkedin/reply.js.map +1 -0
- package/dist/linkedin/selector-engine.d.ts +69 -0
- package/dist/linkedin/selector-engine.d.ts.map +1 -0
- package/dist/linkedin/selector-engine.js +339 -0
- package/dist/linkedin/selector-engine.js.map +1 -0
- package/dist/linkedin/selector-engine.test.d.ts +2 -0
- package/dist/linkedin/selector-engine.test.d.ts.map +1 -0
- package/dist/linkedin/selector-engine.test.js +135 -0
- package/dist/linkedin/selector-engine.test.js.map +1 -0
- package/dist/linkedin/selectors.d.ts +65 -0
- package/dist/linkedin/selectors.d.ts.map +1 -0
- package/dist/linkedin/selectors.js +261 -0
- package/dist/linkedin/selectors.js.map +1 -0
- package/dist/templates/engine.d.ts +37 -0
- package/dist/templates/engine.d.ts.map +1 -0
- package/dist/templates/engine.js +215 -0
- package/dist/templates/engine.js.map +1 -0
- package/dist/templates/engine.test.d.ts +2 -0
- package/dist/templates/engine.test.d.ts.map +1 -0
- package/dist/templates/engine.test.js +212 -0
- package/dist/templates/engine.test.js.map +1 -0
- package/dist/templates/index.d.ts +2 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +7 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/types/index.d.ts +113 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.test.d.ts +2 -0
- package/dist/types/index.test.d.ts.map +1 -0
- package/dist/types/index.test.js +90 -0
- package/dist/types/index.test.js.map +1 -0
- package/dist/utils/paths.d.ts +8 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +68 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +22 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +57 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/retry.d.ts +18 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +49 -0
- package/dist/utils/retry.js.map +1 -0
- package/docs/connection-command.md +52 -0
- package/docs/plans/2025-03-03-linkedin-cli-design.md +280 -0
- package/docs/plans/2025-03-03-linkedin-cli-implementation-plan.md +2087 -0
- package/docs/plans/2025-03-03-linkedin-cli-implementation.md +2420 -0
- package/docs/plans/2026-02-19-linkedin-connection-feature.md +596 -0
- package/docs/plans/2026-02-28-messages-send-feature.md +480 -0
- package/docs/plans/2026-02-28-messages-show-design.md +243 -0
- package/docs/plans/2026-03-03-linkedin-cli-oss-publishing-design.md +394 -0
- package/docs/plans/2026-03-03-linkedin-cli-oss-publishing-plan.md +1592 -0
- package/docs/superpowers/plans/2026-03-13-linkedin-automation-resilience-migration.md +425 -0
- package/docs/superpowers/plans/2026-03-13-playwright-fara-migration.md +1112 -0
- package/docs/superpowers/plans/2026-03-14-page-agent-plan.md +1598 -0
- package/docs/superpowers/plans/2026-03-15-company-profile-extraction.md +591 -0
- package/docs/superpowers/plans/2026-03-15-profile-extraction-plan.md +943 -0
- package/docs/superpowers/specs/2026-03-14-company-profile-extraction-design.md +371 -0
- package/docs/superpowers/specs/2026-03-14-page-agent-design.md +385 -0
- package/docs/superpowers/specs/2026-03-15-profile-extraction-design.md +409 -0
- package/eslint.config.mjs +58 -0
- package/go.mod +9 -0
- package/go.sum +10 -0
- package/import-cookies.js +376 -0
- package/internal/cmd/actions.go +123 -0
- package/internal/cmd/auth.go +108 -0
- package/internal/cmd/connect.go +42 -0
- package/internal/cmd/message.go +44 -0
- package/internal/cmd/people.go +454 -0
- package/internal/cmd/profiles.go +121 -0
- package/internal/cmd/root.go +89 -0
- package/internal/cmd/sequence.go +192 -0
- package/internal/config/config.go +187 -0
- package/internal/config/config_test.go +121 -0
- package/internal/config/profile.go +65 -0
- package/internal/linkedin/navigator.go +195 -0
- package/internal/linkedin/selectors.go +39 -0
- package/internal/linkedin/validator.go +69 -0
- package/internal/pinchtab/client.go +183 -0
- package/internal/pinchtab/client_test.go +67 -0
- package/internal/pinchtab/types.go +50 -0
- package/internal/ratelimit/limiter.go +115 -0
- package/internal/ratelimit/limits.go +32 -0
- package/package.json +67 -0
- package/release.sh +66 -0
- package/scripts/debug-linkedin.js +156 -0
- package/scripts/debug-login.js +193 -0
- package/scripts/extract-from-edge.js +96 -0
- package/scripts/import-cookies.js +101 -0
- package/scripts/poc-show-data.js +205 -0
- package/scripts/proof-of-access.js +87 -0
- package/scripts/prove-connection.js +110 -0
- package/scripts/show-linkedin-data.js +173 -0
- package/src/agent/action-executor.test.ts +464 -0
- package/src/agent/action-executor.ts +203 -0
- package/src/agent/claude-client.test.ts +707 -0
- package/src/agent/claude-client.ts +422 -0
- package/src/agent/dom-extractor.test.ts +574 -0
- package/src/agent/dom-extractor.ts +437 -0
- package/src/agent/extension-client.ts +306 -0
- package/src/agent/index.ts +28 -0
- package/src/agent/page-agent.ts +292 -0
- package/src/agent/types.ts +288 -0
- package/src/cli/agent-commands.ts +274 -0
- package/src/cli/auth.ts +343 -0
- package/src/cli/company.ts +66 -0
- package/src/cli/connection.ts +89 -0
- package/src/cli/index.ts +7 -0
- package/src/cli/messages.ts +338 -0
- package/src/cli/profile.test.ts +14 -0
- package/src/cli/profile.ts +95 -0
- package/src/cli/reply.ts +110 -0
- package/src/core/audit.test.ts +134 -0
- package/src/core/audit.ts +98 -0
- package/src/core/browser-cookies.ts +203 -0
- package/src/core/browser.ts +304 -0
- package/src/core/config.test.ts +90 -0
- package/src/core/config.ts +81 -0
- package/src/core/storage.test.ts +129 -0
- package/src/core/storage.ts +100 -0
- package/src/index.ts +70 -0
- package/src/linkedin/auth.ts +218 -0
- package/src/linkedin/company-extractor.test.ts +58 -0
- package/src/linkedin/company-extractor.ts +222 -0
- package/src/linkedin/connector.ts +336 -0
- package/src/linkedin/message-sender.ts +141 -0
- package/src/linkedin/messages.ts +894 -0
- package/src/linkedin/profile.test.ts +79 -0
- package/src/linkedin/profile.ts +314 -0
- package/src/linkedin/reply.ts +96 -0
- package/src/linkedin/selector-engine.test.ts +167 -0
- package/src/linkedin/selector-engine.ts +393 -0
- package/src/linkedin/selectors.ts +268 -0
- package/src/templates/defaults/followup.txt +14 -0
- package/src/templates/defaults/meeting.txt +16 -0
- package/src/templates/defaults/welcome.txt +14 -0
- package/src/templates/engine.test.ts +228 -0
- package/src/templates/engine.ts +208 -0
- package/src/templates/index.ts +1 -0
- package/src/types/index.test.ts +94 -0
- package/src/types/index.ts +143 -0
- package/src/types/sql.js.d.ts +23 -0
- package/src/utils/paths.ts +33 -0
- package/src/utils/rate-limiter.ts +75 -0
- package/src/utils/retry.ts +78 -0
- package/test-cli.sh +85 -0
- package/test-real-data.sh +97 -0
- package/tsconfig.json +23 -0
- package/vitest.config.ts +35 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
# LinkedIn Automation Resilience Migration Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** Build a resilient automation architecture that keeps the Go CLI thin while reducing breakage from LinkedIn UI changes for connect and message workflows.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Keep `internal/cmd` as orchestration-only, move interaction logic behind a provider interface, and add a detector/recovery layer with confidence scoring and fallback strategies. Preserve current PinchTab path first, then add optional AI fallback provider behind feature flags.
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Go 1.21+, Cobra CLI, PinchTab HTTP client, optional sidecar HTTP provider (Stagehand/AgentQL-compatible contract).
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Chunk 1: Core Boundaries (Keep CLI Thin)
|
|
14
|
+
|
|
15
|
+
### Task 1: Introduce automation contracts
|
|
16
|
+
|
|
17
|
+
**Files:**
|
|
18
|
+
- Create: `internal/automation/types.go`
|
|
19
|
+
- Create: `internal/automation/provider.go`
|
|
20
|
+
- Test: `internal/automation/types_test.go`
|
|
21
|
+
|
|
22
|
+
- [ ] **Step 1: Write failing tests for core types and result contracts**
|
|
23
|
+
|
|
24
|
+
```go
|
|
25
|
+
func TestConnectOutcomeReasonValues(t *testing.T) {
|
|
26
|
+
if ConnectUnavailableReasonAlreadyConnected != "already_connected" {
|
|
27
|
+
t.Fatalf("unexpected value")
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
33
|
+
|
|
34
|
+
Run: `go test ./internal/automation -run TestConnectOutcomeReasonValues -v`
|
|
35
|
+
Expected: FAIL with undefined type/constant.
|
|
36
|
+
|
|
37
|
+
- [ ] **Step 3: Implement minimal contracts**
|
|
38
|
+
|
|
39
|
+
Add:
|
|
40
|
+
- `type ConnectState string`
|
|
41
|
+
- `type ConnectOutcome struct { Success bool; Reason string; Confidence float64; DebugCandidates []string }`
|
|
42
|
+
- `type MessageOutcome struct { Success bool; Reason string }`
|
|
43
|
+
- `type Provider interface { Navigate(...); Connect(...); Message(...) }`
|
|
44
|
+
|
|
45
|
+
- [ ] **Step 4: Run test to verify it passes**
|
|
46
|
+
|
|
47
|
+
Run: `go test ./internal/automation -run TestConnectOutcomeReasonValues -v`
|
|
48
|
+
Expected: PASS.
|
|
49
|
+
|
|
50
|
+
- [ ] **Step 5: Commit**
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
git add internal/automation/types.go internal/automation/provider.go internal/automation/types_test.go
|
|
54
|
+
git commit -m "feat: add automation provider contracts and outcome types"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Task 2: Add PinchTab provider adapter
|
|
58
|
+
|
|
59
|
+
**Files:**
|
|
60
|
+
- Create: `internal/linkedin/provider_pinchtab.go`
|
|
61
|
+
- Modify: `internal/linkedin/navigator.go`
|
|
62
|
+
- Test: `internal/linkedin/provider_pinchtab_test.go`
|
|
63
|
+
|
|
64
|
+
- [ ] **Step 1: Write failing provider adapter tests**
|
|
65
|
+
|
|
66
|
+
```go
|
|
67
|
+
func TestPinchTabProviderConnectMapsUnavailableReason(t *testing.T) {
|
|
68
|
+
// arrange mock snapshot -> follow_only state
|
|
69
|
+
// act provider.Connect(...)
|
|
70
|
+
// assert outcome.Reason == "follow_only"
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
75
|
+
|
|
76
|
+
Run: `go test ./internal/linkedin -run TestPinchTabProviderConnectMapsUnavailableReason -v`
|
|
77
|
+
Expected: FAIL with missing provider implementation.
|
|
78
|
+
|
|
79
|
+
- [ ] **Step 3: Implement provider adapter**
|
|
80
|
+
|
|
81
|
+
Map current navigator errors to `automation.ConnectOutcome` without changing rate-limit behavior.
|
|
82
|
+
|
|
83
|
+
- [ ] **Step 4: Run tests to verify pass**
|
|
84
|
+
|
|
85
|
+
Run: `go test ./internal/linkedin -v`
|
|
86
|
+
Expected: PASS.
|
|
87
|
+
|
|
88
|
+
- [ ] **Step 5: Commit**
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git add internal/linkedin/provider_pinchtab.go internal/linkedin/navigator.go internal/linkedin/provider_pinchtab_test.go
|
|
92
|
+
git commit -m "feat: add pinchtab automation provider adapter"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Task 3: Refactor command actions to use automation service
|
|
96
|
+
|
|
97
|
+
**Files:**
|
|
98
|
+
- Create: `internal/automation/service.go`
|
|
99
|
+
- Modify: `internal/cmd/actions.go`
|
|
100
|
+
- Test: `internal/automation/service_test.go`
|
|
101
|
+
|
|
102
|
+
- [ ] **Step 1: Write failing service behavior tests**
|
|
103
|
+
|
|
104
|
+
```go
|
|
105
|
+
func TestServiceConnectPreservesLimiterOrdering(t *testing.T) {
|
|
106
|
+
// assert CheckConnection happens before Provider.Connect
|
|
107
|
+
// assert RecordConnection only on success
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
112
|
+
|
|
113
|
+
Run: `go test ./internal/automation -run TestServiceConnectPreservesLimiterOrdering -v`
|
|
114
|
+
Expected: FAIL with missing service implementation.
|
|
115
|
+
|
|
116
|
+
- [ ] **Step 3: Implement minimal service + wire actions.go**
|
|
117
|
+
|
|
118
|
+
Keep `internal/cmd` thin and delegate to `automation.Service`.
|
|
119
|
+
|
|
120
|
+
- [ ] **Step 4: Run tests to verify pass**
|
|
121
|
+
|
|
122
|
+
Run: `go test ./internal/automation ./internal/cmd -v`
|
|
123
|
+
Expected: PASS.
|
|
124
|
+
|
|
125
|
+
- [ ] **Step 5: Commit**
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
git add internal/automation/service.go internal/automation/service_test.go internal/cmd/actions.go
|
|
129
|
+
git commit -m "refactor: route cmd actions through automation service"
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Chunk 2: Resilience Engine (Detection + Recovery)
|
|
135
|
+
|
|
136
|
+
### Task 4: Add confidence-based detector strategies
|
|
137
|
+
|
|
138
|
+
**Files:**
|
|
139
|
+
- Create: `internal/linkedin/detector.go`
|
|
140
|
+
- Create: `internal/linkedin/strategy.go`
|
|
141
|
+
- Test: `internal/linkedin/detector_test.go`
|
|
142
|
+
|
|
143
|
+
- [ ] **Step 1: Write failing tests for strategy ordering and confidence**
|
|
144
|
+
|
|
145
|
+
```go
|
|
146
|
+
func TestDetectorPrefersHighConfidenceRoleMatch(t *testing.T) {
|
|
147
|
+
// ensure aria/role strategy beats weak text-only match
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
152
|
+
|
|
153
|
+
Run: `go test ./internal/linkedin -run TestDetectorPrefersHighConfidenceRoleMatch -v`
|
|
154
|
+
Expected: FAIL.
|
|
155
|
+
|
|
156
|
+
- [ ] **Step 3: Implement detector with strategy chain**
|
|
157
|
+
|
|
158
|
+
Strategies:
|
|
159
|
+
- role+name semantic match
|
|
160
|
+
- localized keyword match
|
|
161
|
+
- more-actions fallback discovery
|
|
162
|
+
- low-confidence rejection
|
|
163
|
+
|
|
164
|
+
- [ ] **Step 4: Run tests to verify pass**
|
|
165
|
+
|
|
166
|
+
Run: `go test ./internal/linkedin -v`
|
|
167
|
+
Expected: PASS.
|
|
168
|
+
|
|
169
|
+
- [ ] **Step 5: Commit**
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
git add internal/linkedin/detector.go internal/linkedin/strategy.go internal/linkedin/detector_test.go
|
|
173
|
+
git commit -m "feat: add confidence-based connect detector strategies"
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Task 5: Add snapshot fixture regression tests
|
|
177
|
+
|
|
178
|
+
**Files:**
|
|
179
|
+
- Create: `internal/linkedin/fixtures/*.json`
|
|
180
|
+
- Create: `internal/linkedin/fixture_loader_test.go`
|
|
181
|
+
- Modify: `internal/linkedin/navigator_test.go`
|
|
182
|
+
|
|
183
|
+
- [ ] **Step 1: Write failing fixture tests**
|
|
184
|
+
|
|
185
|
+
```go
|
|
186
|
+
func TestDetectorAgainstRealisticFixtures(t *testing.T) {
|
|
187
|
+
// table: connectable, pending, connected, follow_only, hidden-in-more
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
192
|
+
|
|
193
|
+
Run: `go test ./internal/linkedin -run TestDetectorAgainstRealisticFixtures -v`
|
|
194
|
+
Expected: FAIL until fixtures + loader exist.
|
|
195
|
+
|
|
196
|
+
- [ ] **Step 3: Implement fixture loader and test matrix**
|
|
197
|
+
|
|
198
|
+
Add at least five fixtures to cover common states.
|
|
199
|
+
|
|
200
|
+
- [ ] **Step 4: Run tests to verify pass**
|
|
201
|
+
|
|
202
|
+
Run: `go test ./internal/linkedin -v`
|
|
203
|
+
Expected: PASS.
|
|
204
|
+
|
|
205
|
+
- [ ] **Step 5: Commit**
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
git add internal/linkedin/fixtures internal/linkedin/fixture_loader_test.go internal/linkedin/navigator_test.go
|
|
209
|
+
git commit -m "test: add snapshot fixture regression suite for linkedin states"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Task 6: Add observability hooks for failure triage
|
|
213
|
+
|
|
214
|
+
**Files:**
|
|
215
|
+
- Create: `internal/automation/telemetry.go`
|
|
216
|
+
- Modify: `internal/automation/service.go`
|
|
217
|
+
- Test: `internal/automation/telemetry_test.go`
|
|
218
|
+
|
|
219
|
+
- [ ] **Step 1: Write failing telemetry tests**
|
|
220
|
+
|
|
221
|
+
```go
|
|
222
|
+
func TestServiceRecordsFailureContext(t *testing.T) {
|
|
223
|
+
// assert reason, confidence, and candidate nodes are logged
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
228
|
+
|
|
229
|
+
Run: `go test ./internal/automation -run TestServiceRecordsFailureContext -v`
|
|
230
|
+
Expected: FAIL.
|
|
231
|
+
|
|
232
|
+
- [ ] **Step 3: Implement minimal structured telemetry**
|
|
233
|
+
|
|
234
|
+
Record:
|
|
235
|
+
- action (`connect`/`message`)
|
|
236
|
+
- outcome reason
|
|
237
|
+
- confidence
|
|
238
|
+
- candidate nodes
|
|
239
|
+
- profile + url
|
|
240
|
+
|
|
241
|
+
- [ ] **Step 4: Run tests to verify pass**
|
|
242
|
+
|
|
243
|
+
Run: `go test ./internal/automation -v`
|
|
244
|
+
Expected: PASS.
|
|
245
|
+
|
|
246
|
+
- [ ] **Step 5: Commit**
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
git add internal/automation/telemetry.go internal/automation/telemetry_test.go internal/automation/service.go
|
|
250
|
+
git commit -m "feat: add structured telemetry for automation failures"
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Chunk 3: Optional AI Fallback Provider (Feature-Flagged)
|
|
256
|
+
|
|
257
|
+
### Task 7: Add fallback provider contract and policy
|
|
258
|
+
|
|
259
|
+
**Files:**
|
|
260
|
+
- Create: `internal/automation/fallback.go`
|
|
261
|
+
- Create: `internal/automation/policy.go`
|
|
262
|
+
- Test: `internal/automation/policy_test.go`
|
|
263
|
+
|
|
264
|
+
- [ ] **Step 1: Write failing policy tests**
|
|
265
|
+
|
|
266
|
+
```go
|
|
267
|
+
func TestFallbackPolicyTriggersOnlyLowConfidence(t *testing.T) {
|
|
268
|
+
// low confidence -> fallback true
|
|
269
|
+
// high confidence explicit unavailable -> fallback false
|
|
270
|
+
}
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
274
|
+
|
|
275
|
+
Run: `go test ./internal/automation -run TestFallbackPolicyTriggersOnlyLowConfidence -v`
|
|
276
|
+
Expected: FAIL.
|
|
277
|
+
|
|
278
|
+
- [ ] **Step 3: Implement policy with conservative defaults**
|
|
279
|
+
|
|
280
|
+
Defaults:
|
|
281
|
+
- fallback disabled
|
|
282
|
+
- only enabled by explicit config/env flag
|
|
283
|
+
|
|
284
|
+
- [ ] **Step 4: Run tests to verify pass**
|
|
285
|
+
|
|
286
|
+
Run: `go test ./internal/automation -v`
|
|
287
|
+
Expected: PASS.
|
|
288
|
+
|
|
289
|
+
- [ ] **Step 5: Commit**
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
git add internal/automation/fallback.go internal/automation/policy.go internal/automation/policy_test.go
|
|
293
|
+
git commit -m "feat: add conservative fallback policy contracts"
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Task 8: Implement generic HTTP fallback provider adapter
|
|
297
|
+
|
|
298
|
+
**Files:**
|
|
299
|
+
- Create: `internal/automation/http_provider.go`
|
|
300
|
+
- Create: `internal/automation/http_types.go`
|
|
301
|
+
- Test: `internal/automation/http_provider_test.go`
|
|
302
|
+
|
|
303
|
+
- [ ] **Step 1: Write failing adapter tests**
|
|
304
|
+
|
|
305
|
+
```go
|
|
306
|
+
func TestHTTPProviderMapsConnectIntentAndResponse(t *testing.T) {
|
|
307
|
+
// mock endpoint; assert request/response mapping
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
312
|
+
|
|
313
|
+
Run: `go test ./internal/automation -run TestHTTPProviderMapsConnectIntentAndResponse -v`
|
|
314
|
+
Expected: FAIL.
|
|
315
|
+
|
|
316
|
+
- [ ] **Step 3: Implement adapter (vendor-agnostic)**
|
|
317
|
+
|
|
318
|
+
No vendor lock-in: define neutral request/response schema.
|
|
319
|
+
|
|
320
|
+
- [ ] **Step 4: Run tests to verify pass**
|
|
321
|
+
|
|
322
|
+
Run: `go test ./internal/automation -v`
|
|
323
|
+
Expected: PASS.
|
|
324
|
+
|
|
325
|
+
- [ ] **Step 5: Commit**
|
|
326
|
+
|
|
327
|
+
```bash
|
|
328
|
+
git add internal/automation/http_provider.go internal/automation/http_types.go internal/automation/http_provider_test.go
|
|
329
|
+
git commit -m "feat: add vendor-agnostic http fallback provider adapter"
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Chunk 4: Rollout, Verification, and Safety
|
|
335
|
+
|
|
336
|
+
### Task 9: Keep dry-run parity and command behavior stable
|
|
337
|
+
|
|
338
|
+
**Files:**
|
|
339
|
+
- Modify: `internal/cmd/connect.go`
|
|
340
|
+
- Modify: `internal/cmd/sequence.go`
|
|
341
|
+
- Modify: `internal/cmd/people.go`
|
|
342
|
+
- Test: `internal/cmd/actions_test.go`
|
|
343
|
+
|
|
344
|
+
- [ ] **Step 1: Write failing dry-run parity tests**
|
|
345
|
+
|
|
346
|
+
```go
|
|
347
|
+
func TestDryRunStillSkipsExternalActions(t *testing.T) {
|
|
348
|
+
// no provider calls when dry-run=true
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
- [ ] **Step 2: Run test to verify it fails**
|
|
353
|
+
|
|
354
|
+
Run: `go test ./internal/cmd -run TestDryRunStillSkipsExternalActions -v`
|
|
355
|
+
Expected: FAIL.
|
|
356
|
+
|
|
357
|
+
- [ ] **Step 3: Implement minimal parity fixes**
|
|
358
|
+
|
|
359
|
+
Ensure no behavior drift in CLI UX.
|
|
360
|
+
|
|
361
|
+
- [ ] **Step 4: Run tests to verify pass**
|
|
362
|
+
|
|
363
|
+
Run: `go test ./internal/cmd -v`
|
|
364
|
+
Expected: PASS.
|
|
365
|
+
|
|
366
|
+
- [ ] **Step 5: Commit**
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
git add internal/cmd/connect.go internal/cmd/sequence.go internal/cmd/people.go internal/cmd/actions_test.go
|
|
370
|
+
git commit -m "test: preserve dry-run behavior across automation refactor"
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Task 10: Final verification gate
|
|
374
|
+
|
|
375
|
+
**Files:**
|
|
376
|
+
- Modify: `README.md` (optional short section for provider/fallback config)
|
|
377
|
+
|
|
378
|
+
- [ ] **Step 1: Run focused package tests**
|
|
379
|
+
|
|
380
|
+
Run: `go test ./internal/linkedin ./internal/automation ./internal/cmd -v`
|
|
381
|
+
Expected: PASS.
|
|
382
|
+
|
|
383
|
+
- [ ] **Step 2: Run full test suite**
|
|
384
|
+
|
|
385
|
+
Run: `go test ./...`
|
|
386
|
+
Expected: PASS.
|
|
387
|
+
|
|
388
|
+
- [ ] **Step 3: Build CLI**
|
|
389
|
+
|
|
390
|
+
Run: `go build ./cmd/linkedin`
|
|
391
|
+
Expected: exit 0.
|
|
392
|
+
|
|
393
|
+
- [ ] **Step 4: Validate dry-runs manually**
|
|
394
|
+
|
|
395
|
+
Run:
|
|
396
|
+
- `go run ./cmd/linkedin --profile thaddeus connect --dry-run --url "https://www.linkedin.com/in/example" --message "Hi"`
|
|
397
|
+
- `go run ./cmd/linkedin --profile thaddeus sequence --dry-run --url "https://www.linkedin.com/in/example" --step connect --message "Hi"`
|
|
398
|
+
- `go run ./cmd/linkedin --profile thaddeus people --dry-run --company-url "https://www.linkedin.com/company/example" --mode both --limit 3`
|
|
399
|
+
|
|
400
|
+
Expected: no action execution, only dry-run output.
|
|
401
|
+
|
|
402
|
+
- [ ] **Step 5: Commit**
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
git add README.md
|
|
406
|
+
git commit -m "docs: describe resilient automation architecture and fallback flags"
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## Decision Criteria During Execution
|
|
412
|
+
|
|
413
|
+
- Keep `internal/cmd` thin; no detection heuristics inside command handlers.
|
|
414
|
+
- Preserve rate limiter ordering and counters exactly.
|
|
415
|
+
- Feature-flag AI fallback; default disabled.
|
|
416
|
+
- Prefer deterministic strategies first, AI fallback only on low confidence.
|
|
417
|
+
- No provider-specific business logic in CLI layer.
|
|
418
|
+
|
|
419
|
+
## Expected Deliverables
|
|
420
|
+
|
|
421
|
+
1. Stable automation service boundary (`internal/automation/*`).
|
|
422
|
+
2. PinchTab provider compatible with existing behavior.
|
|
423
|
+
3. Confidence-based detection and fixture regression tests.
|
|
424
|
+
4. Optional vendor-agnostic HTTP fallback provider.
|
|
425
|
+
5. Verified test/build/dry-run parity.
|