Skip to content

Job index: a job you can name, find and read back (plans/job-index.md) - #134

Merged
Evanfeenstra merged 2 commits into
mainfrom
feature/job-index
Oct 9, 2026
Merged

Evanfeenstra merged 2 commits into
mainfrom
feature/job-index

Conversation

@Evanfeenstra

Copy link
Copy Markdown
Contributor

Builds plans/job-index.md as its §9 lays out: two commits, the filesystem half standing alone, then the graph. Rulings 1–7 as proposed; StrutJob without StrutArtifact to start.

The record (src/jobs.ts): title, createdBy, createdAt and one line per run. The runner writes the line at run.start through ctx.services.jobs.recordRun — every launch site passes through there, children with parentRunId, a resume records nothing — carrying the launch's title: a launch-level field beside job and callback on POST …/run and strut.run (400 without a job; input would strip it, and strut has to write it without knowing the workflow). The sweep keeps a record that has runs. A session's turn line records the job it was made under (SessionInfo.jobs).

The index (src/job-index.ts, new): createJobIndex({ dataDir, store, sessions, sealed }) → list (newest-used first; q through src/search.ts over the title and current deliverables), get (runs with status, threads by the turn line's job or by name, holds, files, and the artifact FOLD — successful top-level runs' artifacts[] newest first, the first of each id winning, resolved like …/runs/:runId/artifacts; a sealed workflow's run shows its status and nothing more; text = the latest reply) and read (one file, text capped head+tail, an image as bytes). createStrut builds it into services.jobs; GET /jobs[?q=&limit=] and GET /jobs/:id are thin over it, as are three lib steps the seed can grant: job/list, job/get (adds dir), job/read (an image is shown to the model via withMedia).

The graph: job on StrutRun / StrutAgentSession; one StrutJob per job (exact key on the id; indexed title + summary of deliverables and latest reply), upserted from the index in the run-end hook and by graph/project, with IN_JOB from each run.

Deviations from the plan text (all simplifications, recorded in the plan's status): one write instead of two (recordRun carries the title; no describe); a folded ref carries path and job/get adds dir, instead of an absolute file; SessionInfo.jobs only when a turn carried one; last_used_at instead of updated_at (a generic node property the seed refuses).

Also: src/jobs.test.ts was never in the npm test list; it is now.

Tests: 1415 offline (29 in jobs.test.ts, 7 new), 243 live graph (3 new) — all green locally. Docs: specs/API.md, AGENTS.md, the plan.

Next, outside strut: the mcp seed's params.tools (+ a system line) and hive's title on the launch body beside job.

…x.md, fs half)

A job you can name, find and read back. The record (jobs.ts) gains a
title, who started it and one line per run; the RUNNER writes the line at
run.start through ctx.services.jobs.recordRun (children with parentRunId,
a resume records nothing), carrying the launch's `title` — a launch-level
field beside `job` and `callback` on POST …/run and strut.run, 400 without
a job. The sweep keeps a record that has runs. A session's turn line
records the job it was made under (SessionInfo.jobs).

The read side is new: src/job-index.ts (createJobIndex over the run store,
the session store and dataDir) — list (newest-used first, `q` through the
workflows' matcher over the title and current deliverables), get (runs
with status, threads by the turn line's job or by name, holds, files, and
the artifact FOLD: successful top-level runs' artifacts[] newest first,
the first of each id winning, resolved like …/runs/:runId/artifacts; a
sealed workflow's run shows its status and nothing more; `text` = the
latest reply) and read (one file, text capped head+tail, an image as
bytes). createStrut builds it into services.jobs; GET /jobs[?q=] and
GET /jobs/:id are thin over it, as are three lib steps the seed can grant:
job/list, job/get (adds `dir`), job/read (an image is shown to the model
via withMedia).

Also: src/jobs.test.ts was never in the npm test list; it is now (29
tests, 7 new). Specs: API.md, AGENTS.md, the plan's status.
… from each run (plans/job-index.md §6, graph half)

Two attributes and one node type. `job: ?string` on StrutRun and
StrutAgentSession, stamped by the projector from run.start.job, so
"everything in job X" is one match. `StrutJob` (exact_key on the job id —
ids hold `/` and `-`; indexed title + summary; last_used_at, since
updated_at is a generic node property) is upserted from the job index's
`get` on every run of the job — title from the record, summary = each
current deliverable as `title — summary` then the latest reply, capped at
2000 — with `IN_JOB` from the run. The run-end hook hands `services.jobs`
to projectRunOnce; `graph/project` builds a job index over its dataDir;
projectRun / projectRuns / projectAll take an optional JobReader, and a
bare caller writes no job node. No StrutArtifact node (ruling 5).

Live tests: a pure projection case, projectRun with a fake reader (one
node, two edges, nothing without an index or a record), and the hook
through createStrut (title, the fold's order, a plain run untouched);
the schema/ontology count pins move from 9 types / 15 edges to 10 / 16.
@Evanfeenstra
Evanfeenstra merged commit 58b7192 into main Oct 9, 2026
1 check passed
Evanfeenstra added a commit to stakwork/stakgraph that referenced this pull request Oct 9, 2026
…ad; bump strut (#1755)

Bump strut to 58b7192 (stakwork/strut#134: plans/job-index.md built — a
job you can name, find and read back; `title` on the launch body, one
run line per launch on the job's record, GET /jobs[/:id] with the
artifact fold, StrutJob in the graph). No dependency changes: the pin
alone.

The `job` seed grants the three reads in params.tools, between the
meta/* tools and pod/*: job/list (earlier jobs by words — a title, or
what one delivered), job/get (what one produced, its files with their
paths, its threads), job/read (one of its files; an image the agent then
sees). One paragraph in params.system says when to use them — a message
that refers to earlier work — and the `Job` Concept page carries the
same rule, kind-agnostic. The smoke expects the new tool list and now
checks every granted tool (bar a glob) is in the registry, so a stale
pin fails there instead of warning at run time. seed.ts's description
and lab AGENTS.md updated.

Not here: hive's `title` beside `job` on the launch body (it still rides
on `input`, which the seed strips) — a one-line hive change.
Evanfeenstra added a commit to stakwork/hive that referenced this pull request Oct 9, 2026
Strut's job index (stakwork/strut#134, plans/job-index.md) names a job by
`POST …/run { job, title }` — a launch-level field like `job`, recorded
on the job (its index, its StrutJob node), never on the run. Hive had the
title in hand on every job turn but sent it only on `input`, where the
`job` workflow's input block strips it, so every job on a swarm was
titled by its id.

`dispatchStrutRun` takes `title?` and puts it on the body only with
`job`; `launchJobTurn` passes it beside `job: jobId`, and keeps the copy
on `input` for the reply's header. Tests: the launch body carries it
with a job, never without one.
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