Skip to content

Begin Handshake CLI checkpoint - #4

Draft
isomorphisms wants to merge 14 commits into
clifrom
handshake-cli
Draft

Begin Handshake CLI checkpoint#4
isomorphisms wants to merge 14 commits into
clifrom
handshake-cli

Conversation

@isomorphisms

@isomorphisms isomorphisms commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Purpose

Start a small read-only Handshake command-line client in Idriç without confusing Handshake's authenticated web application with its documented APIs.

The first analytical target is the relationship between a job posting and Handshake's attached job-role-group classification, so the first two EDU API resources are deliberately kept as separate raw tables keyed by job_id:

  • /jobs
  • /job_role_groups

The public job-detail URL is exposed separately and is not treated as the EDU API.

Surface reconciliation

The initial draft named seven holes. Six are resolvable with surfaces already present in current Idriç or small client-local logic:

Draft hole Existing/current surface Treatment in this PR
TSV field cleanup ordinary String / pack / unpack implemented locally
fixture input System.File.readFile implemented directly
/jobs JSON decoding contrib Language.JSON.parse, JSON, lookup typed decoder over existing structural JSON
/job_role_groups JSON decoding same separate typed decoder over the same JSON surface
environment access current System.getEnv : ... → Maybe String direct wrapper for fixed HANDSHAKE_EDU_API_KEY
decimal job-id validation ordinary character/list operations nonempty ASCII decimal check
ICU GET with x-api-key merged ICU lacks the necessary request/capture surface; pending stack has the mechanics but not credential-safe x-api-key redirect semantics remains the genuine dependency/API gap

Idriç #67 is separately restoring the stricter project-level environment_value helper after the source-layout change. Handshake does not need to block on it because current System.getEnv already supplies the required Maybe String result for this one fixed valid variable name.

JSON boundary

Current Idriç already ships Language.JSON in contrib, so this PR does not carry a private JSON grammar. Language.JSON.parse produces structural JSON values (JNull, JBoolean, JNumber, JString, JArray, JObject) and object lookup is already public.

Only fields needed for the two raw output tables are decoded into Handshake records. Unrelated fields remain parsed JSON and are ignored rather than searched textually.

Language.JSON represents JSON numbers as Double. Required identifier fields are accepted only when the parsed number converts back to the same integral value. If Handshake ever emits identifiers beyond that surface's exactly representable integer range, that is a genuine decoding-surface limitation and should stay explicit rather than be hidden here.

Synthetic fixtures only; no private payloads are committed.

Genuine remaining network dependency

Current merged ICU/Idric-Net does not expose the combined caller-header + captured-response path this client needs.

The pending ICU stack is closer than that sentence alone suggests:

  • ICU #12 exposes file-backed raw response capture;
  • ICU #13 is stacked on #12 and adds generic checked caller-supplied request headers plus fetch_to_files_with_headers.

So header construction and response capture are not missing inventions anymore.

The remaining blocker is credential semantics on redirects. ICU #13 intentionally strips Authorization and Cookie when a redirect changes scheme, host, or port, while preserving unrelated custom headers. Current Idric-Net's classify_header makes the same name-based distinction. Handshake's credential is named x-api-key, so using that pending generic path as-is could forward the EDU key to a cross-origin redirect target.

That gap is tracked as dilapidated-shed/icu#19: allow a caller to declare an arbitrary header credential-sensitive (or provide an equivalent policy), so same-origin redirects may retain it while cross-origin redirects strip it.

edu_icu_get therefore remains an explicit fail-closed boundary. It does not substitute curl, Python, browser cookies, or a Handshake-specific socket path.

Commands

handshake url jobs
handshake url roles
handshake public JOB_ID
handshake fixture-jobs FILE
handshake fixture-roles FILE
handshake jobs
handshake roles

The two fixture commands are offline acceptance paths. jobs and roles require HANDSHAKE_EDU_API_KEY; missing and empty keys are separate refusal cases. A present key currently reaches the explicit ICU credential-policy dependency boundary.

Acceptance

checkpoints/handshake/check compiles an executable and checks:

  • source check and executable build;
  • both documented endpoint URLs;
  • decimal public job IDs and malformed-ID rejection;
  • both JSON fixtures byte-for-byte against TSV receipts;
  • missing versus empty API key;
  • a present synthetic key failing exactly at the live transport dependency boundary.

.github/workflows/handshake-idric.yml runs that checkpoint against the current isomorphisms/Idric Idriç branch and exposes the current contrib TTC path for Language.JSON.

Intentional limits

  • one page only; cursor pagination follows after a single live page works;
  • no join yet: raw /jobs and /job_role_groups observations stay separate;
  • no authenticated student-site scraping;
  • no browser-cookie replay;
  • no hidden curl/Python transport fallback;
  • no claim that a public job-detail URL implies a stable machine-readable public jobs API.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant