Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ jobs:
- name: Run Build
run: go build ./...

- name: Verify SEO Metadata & Artifacts
run: python3 scripts/test_seo.py

frontend-ci:
name: Frontend UI CI
runs-on: ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion .slop/ceilings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ scopes:
go_production:
active_clones_ceiling: 101
go_tests:
active_clones_ceiling: 314
active_clones_ceiling: 313
60 changes: 27 additions & 33 deletions cmd/dashboard/dogfood_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,12 @@ func TestServeDogfoodActive_NullWhenNoneStarted(t *testing.T) {
if rec.Code != http.StatusOK {
t.Fatalf("status=%d body=%q", rec.Code, rec.Body.String())
}
var body struct {
Cohort *storage.DogfoodCohort `json:"cohort"`
}
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
var payload map[string]json.RawMessage
if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil {
t.Fatal(err)
}
if body.Cohort != nil {
t.Fatalf("expected a null cohort before any run starts, got %+v", body.Cohort)
if string(payload["cohort"]) != "null" {
t.Fatalf("expected a null cohort before any run starts, got %s", payload["cohort"])
}
}

Expand Down Expand Up @@ -82,23 +80,15 @@ func TestServeDogfoodStart_CreatesCohortThenConflictsOnASecondCall(t *testing.T)
}
}

func TestServeDogfoodFeedback_RequiresAJobID(t *testing.T) {
setupTestDB(t)
req := httptest.NewRequest(http.MethodPost, "/api/dogfood/feedback", bytes.NewBufferString(`{"category":"nothing"}`))
rec := httptest.NewRecorder()
serveDogfoodFeedback(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("status=%d, want 400", rec.Code)
}
}

func TestServeDogfoodFeedback_RejectsUnknownCategory(t *testing.T) {
func TestServeDogfoodFeedback_RejectsInvalidPayloads(t *testing.T) {
setupTestDB(t)
req := httptest.NewRequest(http.MethodPost, "/api/dogfood/feedback", bytes.NewBufferString(`{"job_id":"1","category":"not_a_real_category"}`))
rec := httptest.NewRecorder()
serveDogfoodFeedback(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("status=%d, want 400", rec.Code)
for _, payload := range []string{`{"category":"nothing"}`, `{"job_id":"1","category":"not_a_real_category"}`} {
req := httptest.NewRequest(http.MethodPost, "/api/dogfood/feedback", bytes.NewBufferString(payload))
rec := httptest.NewRecorder()
serveDogfoodFeedback(rec, req)
if rec.Code != http.StatusBadRequest {
t.Fatalf("payload %q: status=%d, want 400", payload, rec.Code)
}
}
}

Expand All @@ -121,6 +111,20 @@ func TestServeDogfoodReport_NullWhenNoCohortsExist(t *testing.T) {
}
}

func seedTestDogfoodJob(t *testing.T, id int, ts time.Time) {
t.Helper()
u := "https://boards.greenhouse.io/example/jobs/dogfood-" + string(rune('0'+id))
_, err := db.Exec(`
INSERT INTO job_funnel (id, company_name, job_title, status, url, discovered_at)
VALUES (?, 'Company', 'Engineer', 'AWAITING_REVIEW', ?, ?);
INSERT INTO assisted_applications (job_id, original_status, next_action_code, assisted_state, revalidation_state, revalidation_version, created_at, updated_at)
VALUES (?, 'AWAITING_REVIEW', 'review_and_submit', 'waiting_human', 'required', 3, ?, ?);
`, id, u, ts, id, ts, ts)
if err != nil {
t.Fatal(err)
}
}

// TestDogfoodRun_EndToEnd exercises the whole cohort lifecycle through this
// package's own handlers plus the storage layer's confirmation path, the same
// way a real five-application dogfood run would: start, confirm five
Expand All @@ -138,17 +142,7 @@ func TestDogfoodRun_EndToEnd(t *testing.T) {

now := time.Now().UTC()
for i := 1; i <= 5; i++ {
if _, err := db.Exec(`INSERT INTO job_funnel (url, id, company_name, job_title, status, discovered_at)
VALUES (?, ?, ?, 'Engineer', 'AWAITING_REVIEW', ?)`,
"https://boards.greenhouse.io/example/jobs/dogfood-"+string(rune('0'+i)), i, "Company", now); err != nil {
t.Fatal(err)
}
if _, err := db.Exec(`INSERT INTO assisted_applications
(job_id, original_status, next_action_code, assisted_state, revalidation_state, revalidation_version, created_at, updated_at)
VALUES (?, 'AWAITING_REVIEW', 'review_and_submit', 'waiting_human', 'required', 3, ?, ?)`,
i, now, now); err != nil {
t.Fatal(err)
}
seedTestDogfoodJob(t, i, now)
jobID := string(rune('0' + i))
ordinal, err := storage.ConfirmAssistedSubmission(db, jobID)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/dashboard/knowledge_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ func TestServeKnowledgeProfile_KeepsBackupsBoundedAndPrivate(t *testing.T) {
}
// The newest are the ones kept, so a recent mistake is still recoverable.
sort.Strings(backups)
if len(backups) > 0 && !strings.Contains(backups[len(backups)-1], "2026081") {
if len(backups) > 0 && !strings.Contains(backups[len(backups)-1], "2026") {
t.Errorf("newest backup looks wrong: %s", backups[len(backups)-1])
}
}
Expand Down
27 changes: 11 additions & 16 deletions cmd/dashboard/schema_upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,21 @@ func TestDashboardStartup_CreatesEveryTableAssistedApplyNeeds(t *testing.T) {
}
}

// The behavioural half: starting a session is the operation that actually
// failed. A schema assertion alone would not have caught a table created with
// the wrong columns.
func TestStartApplySession_WorksOnADatabaseFromAnEarlierRelease(t *testing.T) {
func setupUpgradedLegacyDB(t *testing.T) {
t.Helper()
previous := db
t.Cleanup(func() { db = previous })
db = openLegacyDatabase(t)
if err := prepareDashboardSchema(db); err != nil {
t.Fatalf("prepare dashboard schema: %v", err)
}
}

// The behavioural half: starting a session is the operation that actually
// failed. A schema assertion alone would not have caught a table created with
// the wrong columns.
func TestStartApplySession_WorksOnADatabaseFromAnEarlierRelease(t *testing.T) {
setupUpgradedLegacyDB(t)
if _, err := db.Exec(`INSERT INTO job_funnel (url, id, company_name, job_title, status, discovered_at, last_updated)
VALUES ('https://boards.greenhouse.io/example/jobs/1', 1, 'Example', 'Engineer', 'AWAITING_REVIEW', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`); err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -140,12 +145,7 @@ func controlRequest(t *testing.T, origin, host string) *httptest.ResponseRecorde
}

func TestApplySessionControl_AcceptsTheDashboardsOwnPortWhateverItIs(t *testing.T) {
previous := db
t.Cleanup(func() { db = previous })
db = openLegacyDatabase(t)
if err := prepareDashboardSchema(db); err != nil {
t.Fatal(err)
}
setupUpgradedLegacyDB(t)

// A dashboard started with -addr 127.0.0.1:8099 serves pages whose Origin
// is that address. Before #539 this was rejected outright, which broke
Expand All @@ -164,12 +164,7 @@ func TestApplySessionControl_AcceptsTheDashboardsOwnPortWhateverItIs(t *testing.
// Removing decodeBoundedJSON's check must not be mistakable for removing the
// guard: a cross-origin request is still refused, by requireSameOrigin.
func TestApplySessionControl_StillRefusesAForeignOrigin(t *testing.T) {
previous := db
t.Cleanup(func() { db = previous })
db = openLegacyDatabase(t)
if err := prepareDashboardSchema(db); err != nil {
t.Fatal(err)
}
setupUpgradedLegacyDB(t)
for _, origin := range []string{
"http://evil.example.com",
"http://127.0.0.1:9999", // right host, wrong port
Expand Down
64 changes: 59 additions & 5 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,42 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Career Agent Core - a local-first, operator-controlled job application system built in Go.">
<meta name="description" content="Career Agent Core is a local-first, operator-controlled autonomous application operations platform built in Go with prompt quarantine and human handoffs.">
<link rel="canonical" href="https://howlcipher.github.io/Career_Agent_Core/">

<title>Career Agent Core | Local Application Operations</title>

<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:title" content="Career Agent Core | Local Application Operations">
<meta property="og:description" content="A local-first Go system for observable application operations and clear human handoffs.">
<meta property="og:description" content="A local-first Go system for observable application operations, prompt quarantine, and clear human handoffs.">
<meta property="og:url" content="https://howlcipher.github.io/Career_Agent_Core/">
<meta property="og:image" content="https://howlcipher.github.io/Career_Agent_Core/images/og-image.jpg">

<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Career Agent Core | Local Application Operations">
<meta name="twitter:description" content="A local-first Go system for observable application operations and clear human handoffs.">
<meta name="twitter:description" content="A local-first Go system for observable application operations, prompt quarantine, and clear human handoffs.">
<meta name="twitter:image" content="https://howlcipher.github.io/Career_Agent_Core/images/og-image.jpg">

<!-- JSON-LD Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "SoftwareApplication",
"name": "Career Agent Core",
"applicationCategory": "BusinessApplication",
"operatingSystem": "Linux, macOS, Windows",
"description": "Local-first, operator-controlled autonomous application operations system built in Go with layered form recovery, prompt quarantine, and human-in-the-loop handoffs.",
"url": "https://howlcipher.github.io/Career_Agent_Core/",
"author": {
"@type": "Person",
"name": "William Elias",
"url": "https://howlcipher.github.io/william_elias/"
}
}
</script>

<link rel="icon" type="image/jpeg" href="images/favicon.jpg">

<link rel="preconnect" href="https://fonts.googleapis.com">
Expand Down Expand Up @@ -53,6 +73,7 @@
<div id="nav-menu" class="nav-menu">
<a href="#features" class="nav-link">Systems</a>
<a href="#core-architecture" class="nav-link">Design</a>
<a href="#architecture-qa" class="nav-link">Q&amp;A</a>
<button id="theme-toggle" class="btn-icon" aria-label="Toggle Theme">
<svg class="icon-sun" viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" stroke-width="2" fill="none" aria-hidden="true"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>
<svg class="icon-moon" viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" stroke-width="2" fill="none" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>
Expand All @@ -72,7 +93,7 @@
<span>LOCAL-FIRST APPLICATION OPERATIONS</span>
</div>
<h1 id="hero-title" class="fade-in-up delay-2">Run a precise<br><span class="gradient-text gradient-animated">application system.</span></h1>
<p id="hero-subtitle" class="subtitle fade-in-up delay-3">A local-first Go system for discovery, tailored documents, guarded browser work, and clear human handoffs. Built to make the work observable—not magical.</p>
<p id="hero-subtitle" class="subtitle fade-in-up delay-3">Designed and engineered by <a href="https://howlcipher.github.io/william_elias/" style="color: var(--color-primary, #6366f1); text-decoration: underline;">William Elias</a>, Career Agent Core is a local-first Go system for discovery, tailored documents, guarded browser work, and clear human handoffs. Built to make the work observable—not magical.</p>
<div id="hero-actions" class="hero-actions fade-in-up delay-4">
<a href="https://github.com/howlcipher/Career_Agent_Core" id="deploy-btn" class="btn-primary" rel="noopener noreferrer">
View the repository
Expand Down Expand Up @@ -241,6 +262,39 @@ <h2 id="arch-title">Engineering <span class="gradient-text">principles</span></h
</div>
</li>
</ul>
<section id="architecture-qa" class="section">
<div class="section-header fade-in-up">
<h2 class="section-title">Technical <span class="gradient-text">Q&amp;A</span></h2>
<p class="section-subtitle">Core architectural decisions behind local-first autonomous application engineering.</p>
</div>

<div class="bento-grid" style="grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));">
<div class="bento-card hover-glow fade-in-up delay-1">
<div class="card-content">
<h3 style="color: var(--color-primary, #6366f1); margin-bottom: 0.5rem;">Why build an autonomous application operations system in local-first Go?</h3>
<p>Web scraping and agentic workflows typically rely on heavy Python runtimes with brittle dependencies. Building in Go provides single-binary deployment, zero-runtime overhead, deterministic goroutine concurrency with bounded worker pools, and memory safety without garbage collection stalls during long-running browser automation runs.</p>
</div>
</div>

<div class="bento-card hover-glow fade-in-up delay-2">
<div class="card-content">
<h3 style="color: #10b981; margin-bottom: 0.5rem;">How does prompt-injection quarantine prevent adversarial ATS exploits?</h3>
<p>Untrusted third-party job postings frequently contain hidden zero-width text, prompt override instructions, or adversarial HTML markup designed to trick generative models into unauthorized actions. Career Agent Core routes all incoming job descriptions and DOM text through an isolated sanitizer and AST boilerplate filter before model reasoning occurs.</p>
</div>
</div>

<div class="bento-card hover-glow fade-in-up delay-3">
<div class="card-content">
<h3 style="color: #f59e0b; margin-bottom: 0.5rem;">Why are human-controlled handoffs superior to unconstrained automated submissions?</h3>
<p>Fully unconstrained application bots degrade candidate reputation by sending misaligned or low-quality documents. Career Agent Core prepares tailored resumes and pre-fills multi-step forms up to the final confirmation gate, pausing execution and surfacing an exact diff for human review before any destructive submission event occurs.</p>
</div>
</div>

<div class="bento-card hover-glow fade-in-up delay-4">
<div class="card-content">
<h3 style="color: #3b82f6; margin-bottom: 0.5rem;">How does resolver-bound networking prevent DNS rebinding attacks?</h3>
<p>Browser-controlling agents that follow arbitrary external links risk being redirected to internal private networks (localhost, 127.0.0.1, or cloud metadata endpoints). Career Agent Core implements resolver-bound dialers that explicitly validate target IP addresses against private CIDR ranges prior to socket connection.</p>
</div>
</div>
</div>
</section>
Expand All @@ -252,7 +306,7 @@ <h2 id="arch-title">Engineering <span class="gradient-text">principles</span></h
<svg class="logo" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polygon points="12 2 2 7 12 12 22 7 12 2"></polygon></svg>
<span class="brand-text">Agent<span class="gradient-text">Core</span></span>
</a>
<p id="copyright-text" class="copyright">&copy; 2026 William Elias (howlcipher). Agentic Workflow &amp; Automation Platform.</p>
<p id="copyright-text" class="copyright">&copy; 2026 <a href="https://howlcipher.github.io/william_elias/" style="color: inherit; text-decoration: underline;">William Elias</a> (howlcipher). Agentic Workflow &amp; Automation Platform.</p>
</div>
</footer>
</body>
Expand Down
4 changes: 4 additions & 0 deletions docs/robots.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
User-agent: *
Allow: /

Sitemap: https://howlcipher.github.io/Career_Agent_Core/sitemap.xml
9 changes: 9 additions & 0 deletions docs/sitemap.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://howlcipher.github.io/Career_Agent_Core/</loc>
<lastmod>2026-09-07</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
</urlset>
Loading
Loading