AGPL-3.0-or-later · forever.

showcase · tier 2 · research

Bookmark triage

One metadata fetch per URL (retry for blips, recover for dead links), then jq renders the triage table.

The pile becomes a sortable table. Dead links say dead instead of killing the batch. · conformance-gated in nika-spec ↗ · re-proven at every push

The whole file

3 tasks · 3 waves · the plan falls out of the bindings, nothing is scheduled by hand.

t2-bookmark-triage.nika.yamlsource
nika: v1workflow:  id: bookmark-triage  description: "URL list → per-page metadata fan-out (dead links survive) → one markdown triage table"vars:  bookmarks:    type: array    default:      - "https://example.com"      - "https://github.com/supernovae-st/nika"      - "https://this-domain-does-not-exist-0000.invalid" # the dead one · the recover path IS the demo    description: "The saved-links pile to triage"permits:  exec: false  net:    http: ["example.com", "github.com", "this-domain-does-not-exist-0000.invalid"]  fs:    write: ["out/**"]  tools: ["nika:fetch", "nika:jq", "nika:write"]tasks:  # The fan-out · one metadata fetch per URL · a dead link recovers to a  # marker object (the batch NEVER dies at bookmark 14).  pages:    for_each: ${{ vars.bookmarks }}    max_parallel: 3    fail_fast: false    retry: { max_attempts: 2, backoff_ms: 500 } # a transient blip retries BEFORE the dead verdict    on_error:      recover: { dead: true } # a YAML value, not a JSON string — the marker row    invoke:      tool: "nika:fetch"      args:        url: "${{ item }}"        mode: metadata  # The fan-in · zip results back to their URLs · split live from dead.  table:    with:      pages: ${{ tasks.pages.output }}    invoke:      tool: "nika:jq"      args:        input:          urls: ${{ vars.bookmarks }}          pages: ${{ with.pages }}        expression: >-          def esc: tostring | gsub("\\|"; "\\|") | gsub("\n"; " ");          ([.urls, .pages] | transpose | map({ url: .[0], page: .[1] })) as $rows |          {            live: ([$rows[] | select(.page.dead != true) |              "| \(.url) | \(.page.title // "—" | esc) | \((.page.description // "—" | esc) | .[0:80]) |"] | join("\n")),            dead: ([$rows[] | select(.page.dead == true) | "| \(.url) | DEAD | link rot |"] | join("\n"))          }  report:    with:      table_live: ${{ tasks.table.output.live }}      table_dead: ${{ tasks.table.output.dead }}    invoke:      tool: "nika:write"      args:        path: out/bookmarks.md        create_dirs: true        content: |          # Bookmark triage          | url | title | description |          | --- | --- | --- |          ${{ with.table_live }}          ${{ with.table_dead }}          *Generated by [nika](https://nika.sh) — dead links survived the batch (`on_error: recover:`).*outputs:  table:    value: ${{ tasks.table.output }}    description: "Live rows + dead rows · the triage split"

nika inspect · engine 0.105.0 · vendored graph, never re-derived

3 declared edges · 3 waves · the plan falls out of the bindings