job-forge 2.14.29 → 2.14.31

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.
@@ -0,0 +1,200 @@
1
+ {
2
+ "version": 1,
3
+ "sources": [
4
+ {
5
+ "name": "reports",
6
+ "include": ["reports/*.md"],
7
+ "format": "text",
8
+ "rules": [
9
+ {
10
+ "fact": "job.url",
11
+ "pattern": "^\\*\\*URL:\\*\\*\\s*(?<url>https?://\\S+)",
12
+ "flags": "i",
13
+ "key": "url:{url}",
14
+ "value": "{url}",
15
+ "fields": {
16
+ "url": "{url}",
17
+ "report": "{source}"
18
+ },
19
+ "tags": ["report", "url"]
20
+ },
21
+ {
22
+ "fact": "job.score",
23
+ "pattern": "^\\*\\*Score:\\*\\*\\s*(?<score>[0-9.]+/5)",
24
+ "flags": "i",
25
+ "key": "report:{source}:score",
26
+ "value": "{score}",
27
+ "fields": {
28
+ "score": "{score}",
29
+ "report": "{source}"
30
+ },
31
+ "tags": ["report", "score"]
32
+ }
33
+ ]
34
+ },
35
+ {
36
+ "name": "application-day-files",
37
+ "include": ["data/applications/*.md"],
38
+ "format": "markdown-table",
39
+ "records": [
40
+ {
41
+ "fact": "application.status",
42
+ "key": "company-role:{Company|slug}:{Role|slug}",
43
+ "value": "{Status}",
44
+ "fields": {
45
+ "num": "{#}",
46
+ "date": "{Date}",
47
+ "company": "{Company}",
48
+ "role": "{Role}",
49
+ "score": "{Score}",
50
+ "status": "{Status}",
51
+ "pdf": "{PDF}",
52
+ "report": "{Report}",
53
+ "notes": "{Notes}"
54
+ },
55
+ "tags": ["tracker", "application"]
56
+ }
57
+ ]
58
+ },
59
+ {
60
+ "name": "application-single-file",
61
+ "include": ["data/applications.md", "applications.md"],
62
+ "format": "markdown-table",
63
+ "records": [
64
+ {
65
+ "fact": "application.status",
66
+ "key": "company-role:{Company|slug}:{Role|slug}",
67
+ "value": "{Status}",
68
+ "fields": {
69
+ "num": "{#}",
70
+ "date": "{Date}",
71
+ "company": "{Company}",
72
+ "role": "{Role}",
73
+ "score": "{Score}",
74
+ "status": "{Status}",
75
+ "pdf": "{PDF}",
76
+ "report": "{Report}",
77
+ "notes": "{Notes}"
78
+ },
79
+ "tags": ["tracker", "application"]
80
+ }
81
+ ]
82
+ },
83
+ {
84
+ "name": "tracker-additions",
85
+ "include": ["batch/tracker-additions/*.tsv", "batch/tracker-additions/merged/*.tsv"],
86
+ "format": "tsv",
87
+ "header": false,
88
+ "columns": ["num", "date", "company", "role", "statusOrScore", "scoreOrStatus", "pdf", "report", "notes"],
89
+ "records": [
90
+ {
91
+ "fact": "tracker.addition",
92
+ "key": "company-role:{company|slug}:{role|slug}",
93
+ "value": "{source}",
94
+ "fields": ["num", "date", "company", "role", "statusOrScore", "scoreOrStatus", "pdf", "report", "notes"],
95
+ "tags": ["tracker", "tsv"]
96
+ }
97
+ ]
98
+ },
99
+ {
100
+ "name": "pipeline",
101
+ "include": ["data/pipeline.md"],
102
+ "format": "text",
103
+ "rules": [
104
+ {
105
+ "fact": "job.url",
106
+ "pattern": "^\\s*-\\s*\\[(?<state>[ xX])\\]\\s+(?<url>https?://\\S+)",
107
+ "key": "url:{url}",
108
+ "value": "{state}",
109
+ "fields": {
110
+ "url": "{url}",
111
+ "state": "{state}",
112
+ "pipeline": "{source}"
113
+ },
114
+ "tags": ["pipeline", "url"]
115
+ }
116
+ ]
117
+ },
118
+ {
119
+ "name": "scan-history",
120
+ "include": ["data/scan-history.tsv"],
121
+ "format": "tsv",
122
+ "records": [
123
+ {
124
+ "fact": "job.url",
125
+ "key": "url:{url}",
126
+ "value": "{url}",
127
+ "fields": ["date", "company", "role", "url", "ats"],
128
+ "tags": ["scan", "url"]
129
+ }
130
+ ]
131
+ },
132
+ {
133
+ "name": "preflight-candidates-object",
134
+ "include": ["batch/preflight-candidates.json"],
135
+ "format": "json",
136
+ "records": [
137
+ {
138
+ "fact": "candidate.ready",
139
+ "path": "$.candidates[]",
140
+ "key": "{companyRoleKey}",
141
+ "value": "{url}",
142
+ "fields": {
143
+ "id": "{id}",
144
+ "company": "{company}",
145
+ "role": "{role}",
146
+ "companyRoleKey": "{companyRoleKey}",
147
+ "url": "{url}",
148
+ "score": "{score}",
149
+ "gateStatus": "{gate.status}",
150
+ "locationStatus": "{location.status}"
151
+ },
152
+ "tags": ["candidate", "preflight"]
153
+ }
154
+ ]
155
+ },
156
+ {
157
+ "name": "preflight-candidates-array",
158
+ "include": ["batch/preflight-candidates.json"],
159
+ "format": "json",
160
+ "records": [
161
+ {
162
+ "fact": "candidate.ready",
163
+ "path": "$[]",
164
+ "key": "{companyRoleKey}",
165
+ "value": "{url}",
166
+ "fields": {
167
+ "id": "{id}",
168
+ "company": "{company}",
169
+ "role": "{role}",
170
+ "companyRoleKey": "{companyRoleKey}",
171
+ "url": "{url}",
172
+ "score": "{score}",
173
+ "gateStatus": "{gate.status}",
174
+ "locationStatus": "{location.status}"
175
+ },
176
+ "tags": ["candidate", "preflight"]
177
+ }
178
+ ]
179
+ },
180
+ {
181
+ "name": "ledger",
182
+ "include": [".jobforge-ledger/*.jsonl"],
183
+ "format": "jsonl",
184
+ "records": [
185
+ {
186
+ "fact": "ledger.event",
187
+ "key": "{key}",
188
+ "value": "{type}",
189
+ "fields": {
190
+ "type": "{type}",
191
+ "key": "{key}",
192
+ "status": "{data.status}",
193
+ "source": "{source}"
194
+ },
195
+ "tags": ["ledger"]
196
+ }
197
+ ]
198
+ }
199
+ ]
200
+ }
@@ -33,6 +33,13 @@
33
33
  "index:has": "job-forge index:has",
34
34
  "index:query": "job-forge index:query",
35
35
  "index:explain": "job-forge index:explain",
36
+ "facts:build": "job-forge facts:build",
37
+ "facts:status": "job-forge facts:status",
38
+ "facts:verify": "job-forge facts:verify",
39
+ "facts:check": "job-forge facts:check",
40
+ "facts:has": "job-forge facts:has",
41
+ "facts:query": "job-forge facts:query",
42
+ "facts:explain": "job-forge facts:explain",
36
43
  "canon:normalize": "job-forge canon:normalize",
37
44
  "canon:key": "job-forge canon:key",
38
45
  "canon:compare": "job-forge canon:compare",
@@ -43,6 +50,10 @@
43
50
  "postflight:status": "job-forge postflight:status",
44
51
  "postflight:check": "job-forge postflight:check",
45
52
  "postflight:explain": "job-forge postflight:explain",
53
+ "redact:scan": "job-forge redact:scan",
54
+ "redact:verify": "job-forge redact:verify",
55
+ "redact:apply": "job-forge redact:apply",
56
+ "redact:explain": "job-forge redact:explain",
46
57
  "migrate:plan": "job-forge migrate:plan",
47
58
  "migrate:apply": "job-forge migrate:apply",
48
59
  "migrate:check": "job-forge migrate:check",
@@ -65,6 +76,8 @@
65
76
  ".jobforge-ledger/",
66
77
  ".jobforge-cache/",
67
78
  ".jobforge-index.json",
79
+ ".jobforge-facts.json",
80
+ ".jobforge-redacted/",
68
81
  "batch/preflight-candidates.json",
69
82
  "batch/preflight-plan.json",
70
83
  "batch/postflight-outcomes.json"
@@ -0,0 +1,77 @@
1
+ {
2
+ "version": 1,
3
+ "defaults": {
4
+ "severity": "error",
5
+ "replacement": "[REDACTED:{id}]"
6
+ },
7
+ "builtins": [
8
+ {
9
+ "id": "email",
10
+ "severity": "warn"
11
+ },
12
+ {
13
+ "id": "phone",
14
+ "severity": "warn"
15
+ },
16
+ "openai-api-key",
17
+ "github-token",
18
+ "npm-token",
19
+ "aws-access-key-id",
20
+ "bearer-token",
21
+ "private-key",
22
+ "proxy-url-credentials"
23
+ ],
24
+ "fields": [
25
+ {
26
+ "id": "profile-contact",
27
+ "label": "Profile contact field",
28
+ "severity": "warn",
29
+ "names": [
30
+ "email",
31
+ "phone",
32
+ "address",
33
+ "street",
34
+ "city",
35
+ "postalCode",
36
+ "zip",
37
+ "linkedin"
38
+ ]
39
+ },
40
+ {
41
+ "id": "proxy-config",
42
+ "label": "Proxy configuration field",
43
+ "names": [
44
+ "proxy",
45
+ "proxyUrl",
46
+ "proxy_url",
47
+ "server",
48
+ "username",
49
+ "password",
50
+ "bypass"
51
+ ]
52
+ },
53
+ {
54
+ "id": "credential-field",
55
+ "label": "Credential field",
56
+ "names": [
57
+ "apiKey",
58
+ "api_key",
59
+ "access_token",
60
+ "refresh_token",
61
+ "client_secret",
62
+ "clientSecret",
63
+ "authorization",
64
+ "cookie",
65
+ "session",
66
+ "token"
67
+ ]
68
+ }
69
+ ],
70
+ "patterns": [
71
+ {
72
+ "id": "proxy-provider-account",
73
+ "label": "Proxy provider account identifier",
74
+ "pattern": "\\bbrd-customer-[A-Za-z0-9_.-]+\\b"
75
+ }
76
+ ]
77
+ }
@@ -18,6 +18,7 @@
18
18
  * 9. Drift warning if states.yml ids differ from the built-in fallback list
19
19
  * 10. Ledger file verifies if .jobforge-ledger/events.jsonl exists
20
20
  * 11. Artifact index verifies if .jobforge-index.json exists
21
+ * 12. Fact set verifies if .jobforge-facts.json exists
21
22
  *
22
23
  * Run: node verify-pipeline.mjs (from repo root; same as npm run verify)
23
24
  */
@@ -31,6 +32,7 @@ import {
31
32
  } from './tracker-lib.mjs';
32
33
  import { jobForgeLedgerPath, ledgerExists, verifyJobForgeLedger } from './lib/jobforge-ledger.mjs';
33
34
  import { indexExists, jobForgeIndexPath, verifyJobForgeIndex } from './lib/jobforge-index.mjs';
35
+ import { factsExist, jobForgeFactsPath, verifyJobForgeFacts } from './lib/jobforge-facts.mjs';
34
36
  import {
35
37
  canonicalStatusValues,
36
38
  formatContractIssues,
@@ -171,6 +173,22 @@ function verifyIndexIfPresent() {
171
173
  }
172
174
  }
173
175
 
176
+ function verifyFactsIfPresent() {
177
+ if (!factsExist(PROJECT_DIR)) {
178
+ ok('Fact set not initialized');
179
+ return;
180
+ }
181
+ const result = verifyJobForgeFacts({ rebuild: false }, PROJECT_DIR);
182
+ for (const issue of result.issues) {
183
+ const msg = `facts: ${issue.kind}: ${issue.message}`;
184
+ if (issue.severity === 'error') error(msg);
185
+ else warn(msg);
186
+ }
187
+ if (result.ok) {
188
+ ok(`Fact set valid (${result.facts} facts at ${relative(PROJECT_DIR, jobForgeFactsPath(PROJECT_DIR))})`);
189
+ }
190
+ }
191
+
174
192
  // --- Read entries ---
175
193
  const { entries, source } = readAllEntries();
176
194
 
@@ -181,6 +199,7 @@ if (entries.length === 0) {
181
199
  verifyStatesYamlDrift();
182
200
  verifyLedgerIfPresent();
183
201
  verifyIndexIfPresent();
202
+ verifyFactsIfPresent();
184
203
  console.log('\n' + '='.repeat(50));
185
204
  console.log(`📊 Pipeline Health: ${errors} errors, ${warnings} warnings`);
186
205
  if (errors === 0 && warnings === 0) console.log('🟢 Pipeline is clean!');
@@ -317,6 +336,7 @@ if (boldScores === 0) ok('No bold in scores');
317
336
  verifyStatesYamlDrift();
318
337
  verifyLedgerIfPresent();
319
338
  verifyIndexIfPresent();
339
+ verifyFactsIfPresent();
320
340
 
321
341
  console.log('\n' + '='.repeat(50));
322
342
  console.log(`📊 Pipeline Health: ${errors} errors, ${warnings} warnings`);