@sabaiway/agent-workflow-kit 12.0.0 → 12.2.0

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.
Files changed (36) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +2 -1
  3. package/SKILL.md +5 -1
  4. package/bridges/antigravity-cli-bridge/SKILL.md +16 -13
  5. package/bridges/antigravity-cli-bridge/bin/agy-review.sh +6 -5
  6. package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +5 -5
  7. package/bridges/antigravity-cli-bridge/bin/agy.sh +4 -4
  8. package/bridges/antigravity-cli-bridge/bin/agy.test.mjs +2 -2
  9. package/bridges/antigravity-cli-bridge/capability.json +3 -3
  10. package/bridges/antigravity-cli-bridge/references/driving-agy.md +6 -5
  11. package/bridges/antigravity-cli-bridge/references/models-and-flags.md +14 -11
  12. package/bridges/codex-cli-bridge/SKILL.md +14 -12
  13. package/bridges/codex-cli-bridge/bin/codex-exec.sh +16 -15
  14. package/bridges/codex-cli-bridge/bin/codex-exec.test.mjs +9 -9
  15. package/bridges/codex-cli-bridge/bin/codex-review.sh +14 -13
  16. package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +8 -8
  17. package/bridges/codex-cli-bridge/capability.json +4 -4
  18. package/bridges/codex-cli-bridge/references/driving-codex.md +2 -2
  19. package/bridges/codex-cli-bridge/references/sandbox-and-flags.md +6 -6
  20. package/capability.json +1 -1
  21. package/package.json +1 -1
  22. package/references/modes/bridge-settings.md +2 -2
  23. package/references/modes/now.md +30 -0
  24. package/tools/bridge-settings.mjs +1 -1
  25. package/tools/commands.mjs +7 -0
  26. package/tools/feedback-record.mjs +6 -2
  27. package/tools/now-cli.mjs +104 -0
  28. package/tools/now-facts.mjs +67 -0
  29. package/tools/now-plan-facts.mjs +129 -0
  30. package/tools/now-render.mjs +162 -0
  31. package/tools/now-status.mjs +145 -0
  32. package/tools/plan-shape-facts.mjs +1 -1
  33. package/tools/queue-audit-cli.mjs +17 -10
  34. package/tools/queue-audit-rows.mjs +98 -18
  35. package/tools/queue-audit.mjs +58 -12
  36. package/tools/queue-row-name.mjs +149 -0
@@ -194,10 +194,10 @@ describe('codex-exec.sh — quality-first model/effort guard (1.1)', { concurren
194
194
 
195
195
  it('refuses a non-default CODEX_EFFORT', async () => {
196
196
  const sb = makeSandbox();
197
- const r = await run(sb, { env: { CODEX_EFFORT: 'high' } });
197
+ const r = await run(sb, { env: { CODEX_EFFORT: 'xhigh' } });
198
198
  rmSync(sb.root, { recursive: true, force: true });
199
199
  assert.notEqual(r.status, 0);
200
- assert.match(r.stderr, /not the pinned max effort/);
200
+ assert.match(r.stderr, /not the pinned effort/);
201
201
  });
202
202
 
203
203
  it('CODEX_PROBE=1 allows a non-default model and warns loudly', async () => {
@@ -650,8 +650,8 @@ describe('codex-exec.sh — preflight (unchanged invariants)', { concurrency: 2
650
650
 
651
651
  describe('codex-exec.sh — resume entrypoint restates every invariant (3.1)', { concurrency: 2 }, () => {
652
652
  const RESUME_INVARIANTS = [
653
- /(^|\n)resume(\n|$)/, /(^|\n)--ignore-user-config(\n|$)/, /(^|\n)gpt-5\.6-sol(\n|$)/,
654
- /model_reasoning_effort=xhigh/, /sandbox_mode=workspace-write/,
653
+ /(^|\n)resume(\n|$)/, /(^|\n)--ignore-user-config(\n|$)/, /(^|\n)gpt-6-astra(\n|$)/,
654
+ /model_reasoning_effort=high/, /sandbox_mode=workspace-write/,
655
655
  /approval_policy=never/, /sandbox_workspace_write\.network_access=false/,
656
656
  ];
657
657
 
@@ -1546,7 +1546,7 @@ describe('codex-exec.sh — dispatch-posture labeling (D5, AD-061)', { concurren
1546
1546
  const lines = banners(r.stderr);
1547
1547
  assert.equal(lines.length, 1, 'EXACTLY ONE banner line per run');
1548
1548
  assert.equal(lines[0],
1549
- 'exec posture: model=gpt-5.6-sol effort=xhigh tier=standard sandbox=workspace-write session=fresh timeout=3600s');
1549
+ 'exec posture: model=gpt-6-astra effort=high tier=standard sandbox=workspace-write session=fresh timeout=3600s');
1550
1550
  });
1551
1551
 
1552
1552
  it('an ARMED Fast tier rides the banner (tier=priority)', async () => {
@@ -1642,11 +1642,11 @@ describe('codex-exec.sh — dispatch-posture labeling (D5, AD-061)', { concurren
1642
1642
 
1643
1643
  it('a banner field carrying CONTROL BYTES refuses pre-spend (model / effort / tier / timeout / DEL)', async () => {
1644
1644
  const cases = [
1645
- { CODEX_MODEL: `gpt-5.6-sol${String.fromCharCode(1)}` },
1646
- { CODEX_EFFORT: `xhigh${String.fromCharCode(2)}` },
1645
+ { CODEX_MODEL: `gpt-6-astra${String.fromCharCode(1)}` },
1646
+ { CODEX_EFFORT: `high${String.fromCharCode(2)}` },
1647
1647
  { CODEX_SERVICE_TIER: `priority${String.fromCharCode(3)}` },
1648
1648
  { CODEX_HARD_TIMEOUT: `3600${String.fromCharCode(4)}` },
1649
- { CODEX_MODEL: `gpt-5.6-sol${String.fromCharCode(127)}` },
1649
+ { CODEX_MODEL: `gpt-6-astra${String.fromCharCode(127)}` },
1650
1650
  ];
1651
1651
  for (const env of cases) {
1652
1652
  const sb = makeSandbox();
@@ -2047,7 +2047,7 @@ describe('codex-exec.sh — the pre-spend reservation (D1/D8)', { concurrency: 2
2047
2047
  assert.equal(r.status, 71, r.stderr);
2048
2048
  assert.equal(held.state, 'reserved');
2049
2049
  assert.equal(held.wrapperVersion, MANIFEST.version, 'the receipt stamps the bridge version version-sync bumps');
2050
- assert.deepEqual(held.posture, { model: 'gpt-5.6-sol', effort: 'xhigh', tier: null });
2050
+ assert.deepEqual(held.posture, { model: 'gpt-6-astra', effort: 'high', tier: null });
2051
2051
  assert.equal(held.capS, 3600);
2052
2052
  assert.equal(held.killGraceS, 15);
2053
2053
  assert.match(held.contractDigest, /^[0-9a-f]{64}$/);
@@ -21,7 +21,7 @@
21
21
  # back as a validated JSON object, with a raw-text fallback.
22
22
  #
23
23
  # Auth/policy: subscription-only, identical to codex-exec.sh. Quality-first: the
24
- # review runs on the PINNED model at max effort (advisory findings still bear on
24
+ # review runs on the PINNED model at the pinned effort (advisory findings still bear on
25
25
  # what ships, so the same pin-integrity guard applies; CODEX_PROBE=1 relaxes it for
26
26
  # a throwaway probe only).
27
27
  #
@@ -39,7 +39,7 @@ set -euo pipefail
39
39
  case "${1:-}" in
40
40
  --help|-h)
41
41
  cat <<'HELP'
42
- codex-review — read-only ADVISORY review by the OpenAI Codex CLI (subscription-only; the PINNED model at max effort).
42
+ codex-review — read-only ADVISORY review by the OpenAI Codex CLI (subscription-only; the PINNED model at the pinned effort).
43
43
 
44
44
  Usage:
45
45
  codex-review plan <plan-file> [--nonce <n>]
@@ -91,7 +91,7 @@ Receipt:
91
91
 
92
92
  Settings file (KEY=VALUE, parsed never sourced; env wins over file, file wins over built-in default):
93
93
  ${XDG_CONFIG_HOME:-~/.config}/agent-workflow/bridge-settings.conf
94
- CODEX_SERVICE_TIER — service tier: 'priority' (Fast — ~1.5x speed at a 2.5x credit rate on gpt-5.6-sol); a consented SPEND knob, default off (standard tier)
94
+ CODEX_SERVICE_TIER — service tier: 'priority' (Fast — "2x speed, increased usage", the codex catalog's own words for gpt-6-astra, cache fetched 2026-09-05; it states no credit-rate figure); a consented SPEND knob, default off (standard tier)
95
95
  CODEX_HARD_TIMEOUT — hard wall-clock cap, integer seconds 1..86400 (built-in default 1800)
96
96
  CODEX_REVIEW_MAX_TOTAL_BYTES — inline-payload cap, integer bytes 1..100000000 (default 1500000); above it the diff rides via a git-dir temp file
97
97
 
@@ -279,15 +279,15 @@ aw_resolve_timeout_bin() {
279
279
  printf '%s' "$bin"
280
280
  }
281
281
 
282
- DEFAULT_CODEX_MODEL="gpt-5.6-sol"
283
- DEFAULT_CODEX_EFFORT="xhigh"
282
+ DEFAULT_CODEX_MODEL="gpt-6-astra"
283
+ DEFAULT_CODEX_EFFORT="high"
284
284
  # Review-receipt identity (AD-038). AW_BRIDGE_VERSION mirrors this bridge's SKILL.md/capability.json
285
285
  # version (drift-guarded by codex-review.test.mjs against capability.json).
286
286
  AW_RECEIPT_BACKEND="codex"
287
- AW_BRIDGE_VERSION="3.7.0" # aw-version-anchor
287
+ AW_BRIDGE_VERSION="3.8.0" # aw-version-anchor
288
288
  CODEX_MODEL="${CODEX_MODEL:-$DEFAULT_CODEX_MODEL}"
289
289
  CODEX_EFFORT="${CODEX_EFFORT:-$DEFAULT_CODEX_EFFORT}"
290
- # Generous hard cap for a slow xhigh review (subscription latency varies).
290
+ # Generous hard cap for a slow review at the pinned effort (subscription latency varies).
291
291
  CODEX_HARD_TIMEOUT="${CODEX_HARD_TIMEOUT:-1800}"
292
292
  # Above this assembled-payload size (bytes), the diff goes via a git-dir-local temp
293
293
  # file instead of inline — never truncated.
@@ -298,11 +298,12 @@ CODEX_REVIEW_MAX_TOTAL_BYTES="${CODEX_REVIEW_MAX_TOTAL_BYTES:-1500000}"
298
298
  # file), so the budget is pinned at the DEFAULT inline-payload cap — the map can never outgrow the
299
299
  # whole payload, and every realistic repo's map assembles byte-unchanged.
300
300
  AW_REVIEW_MAP_BUDGET_BYTES=1500000
301
- # Codex service tier (quality-neutral speed knob; live-probed 2026-07-05): default EMPTY ⇒ no
301
+ # Codex service tier (quality-neutral speed knob): default EMPTY ⇒ no
302
302
  # service_tier flag (standard tier) — enabling Fast is a consented per-host SPEND act, never a
303
303
  # silent default. The only server-catalog tier id on this subscription is 'priority' (catalog
304
- # display name "Fast": ~1.5x token speed at a 2.5x credit rate on gpt-5.6-sol; quality-neutral —
305
- # same model). codex itself accepts ANY -c service_tier string silently (probe-verified), so
304
+ # display name "Fast": "2x speed, increased usage" in the catalog's own words for gpt-6-astra, cache
305
+ # fetched 2026-09-05; it states no credit-rate figure; quality-neutral — same model). codex itself
306
+ # accepts ANY -c service_tier string silently (probe-verified), so
306
307
  # the wrapper validates the effective value: an unsupported one warns and runs on the standard
307
308
  # tier — a typo can never silently masquerade as Fast.
308
309
  CODEX_SERVICE_TIER="${CODEX_SERVICE_TIER:-}"
@@ -365,13 +366,13 @@ if [[ "${CODEX_PROBE:-}" == "1" ]]; then
365
366
  else
366
367
  if [[ "$CODEX_MODEL" != "$DEFAULT_CODEX_MODEL" ]]; then
367
368
  echo "error: CODEX_MODEL='$CODEX_MODEL' is not the pinned model '$DEFAULT_CODEX_MODEL'." >&2
368
- echo " A delegated review must run on the pinned model at max effort (quality-first)." >&2
369
+ echo " A delegated review must run on the pinned model at the pinned effort (quality-first)." >&2
369
370
  echo " For a throwaway probe whose result is effort-independent, set CODEX_PROBE=1." >&2
370
371
  exit 2
371
372
  fi
372
373
  if [[ "$CODEX_EFFORT" != "$DEFAULT_CODEX_EFFORT" ]]; then
373
- echo "error: CODEX_EFFORT='$CODEX_EFFORT' is not the pinned max effort '$DEFAULT_CODEX_EFFORT'." >&2
374
- echo " A delegated review must run at max reasoning effort (quality-first)." >&2
374
+ echo "error: CODEX_EFFORT='$CODEX_EFFORT' is not the pinned effort '$DEFAULT_CODEX_EFFORT'." >&2
375
+ echo " A delegated review must run at the pinned reasoning effort (quality-first)." >&2
375
376
  echo " For a throwaway probe whose result is effort-independent, set CODEX_PROBE=1." >&2
376
377
  exit 2
377
378
  fi
@@ -172,10 +172,10 @@ describe('codex-review.sh — quality-first model/effort guard (1.1)', { concurr
172
172
 
173
173
  it('refuses a non-default CODEX_EFFORT', async () => {
174
174
  const sb = makeSandbox();
175
- const r = await run(sb, { env: { CODEX_EFFORT: 'high' } });
175
+ const r = await run(sb, { env: { CODEX_EFFORT: 'xhigh' } });
176
176
  rmSync(sb.root, { recursive: true, force: true });
177
177
  assert.notEqual(r.status, 0);
178
- assert.match(r.stderr, /not the pinned max effort/);
178
+ assert.match(r.stderr, /not the pinned effort/);
179
179
  });
180
180
 
181
181
  it('CODEX_PROBE=1 relaxes the guard and warns', async () => {
@@ -1646,8 +1646,8 @@ describe('codex-review.sh — dispatch-posture labeling (D5)', { concurrency: 2
1646
1646
  const receipts = readReceipts(sb.repo);
1647
1647
  rmSync(sb.root, { recursive: true, force: true });
1648
1648
  assert.equal(r.status, 0, r.stderr);
1649
- assert.match(r.stderr, /review posture: model=gpt-5\.6-sol effort=xhigh tier=standard/, 'the banner states the actual run posture');
1650
- assert.deepEqual(receipts[0].posture, { model: 'gpt-5.6-sol', effort: 'xhigh', tier: null }, 'banner ↔ receipt parity (standard tier = null)');
1649
+ assert.match(r.stderr, /review posture: model=gpt-6-astra effort=high tier=standard/, 'the banner states the actual run posture');
1650
+ assert.deepEqual(receipts[0].posture, { model: 'gpt-6-astra', effort: 'high', tier: null }, 'banner ↔ receipt parity (standard tier = null)');
1651
1651
  assert.deepEqual(Object.keys(receipts[0]), Object.keys(RECEIPT_FIXTURE), 'fixture key set + order');
1652
1652
  });
1653
1653
 
@@ -1657,7 +1657,7 @@ describe('codex-review.sh — dispatch-posture labeling (D5)', { concurrency: 2
1657
1657
  const receipts = readReceipts(sb.repo);
1658
1658
  rmSync(sb.root, { recursive: true, force: true });
1659
1659
  assert.equal(r.status, 0, r.stderr);
1660
- assert.match(r.stderr, /review posture: model=gpt-5\.6-sol effort=xhigh tier=priority/);
1660
+ assert.match(r.stderr, /review posture: model=gpt-6-astra effort=high tier=priority/);
1661
1661
  assert.equal(receipts[0].posture.tier, 'priority');
1662
1662
  });
1663
1663
 
@@ -1674,7 +1674,7 @@ describe('codex-review.sh — dispatch-posture labeling (D5)', { concurrency: 2
1674
1674
 
1675
1675
  it('a posture value carrying CONTROL BYTES refuses pre-spend, BEFORE the pinned-model guard', async () => {
1676
1676
  const sb = makeSandbox();
1677
- const r = await run(sb, { env: { CODEX_MODEL: `gpt-5.6-sol${String.fromCharCode(1)}` } });
1677
+ const r = await run(sb, { env: { CODEX_MODEL: `gpt-6-astra${String.fromCharCode(1)}` } });
1678
1678
  const receipts = readReceipts(sb.repo);
1679
1679
  rmSync(sb.root, { recursive: true, force: true });
1680
1680
  assert.notEqual(r.status, 0);
@@ -1689,7 +1689,7 @@ describe('codex-review.sh — dispatch-posture labeling (D5)', { concurrency: 2
1689
1689
  const receipts = readReceipts(sb.repo);
1690
1690
  rmSync(sb.root, { recursive: true, force: true });
1691
1691
  assert.equal(r.status, 0, r.stderr);
1692
- assert.match(r.stderr, /^review posture: model=gpt-5\.6-sol effort=xhigh tier=standard timeout=1800s$/m);
1692
+ assert.match(r.stderr, /^review posture: model=gpt-6-astra effort=high tier=standard timeout=1800s$/m);
1693
1693
  assert.deepEqual(Object.keys(receipts[0].posture), ['model', 'effort', 'tier'], 'timeout never enters the receipt posture');
1694
1694
  });
1695
1695
 
@@ -1715,7 +1715,7 @@ describe('codex-review.sh — dispatch-posture labeling (D5)', { concurrency: 2
1715
1715
 
1716
1716
  it('a DEL (0x7f) byte in a banner field refuses pre-spend like the C0 range', async () => {
1717
1717
  const sb = makeSandbox();
1718
- const r = await run(sb, { env: { CODEX_MODEL: `gpt-5.6-sol${String.fromCharCode(127)}` } });
1718
+ const r = await run(sb, { env: { CODEX_MODEL: `gpt-6-astra${String.fromCharCode(127)}` } });
1719
1719
  const receipts = readReceipts(sb.repo);
1720
1720
  rmSync(sb.root, { recursive: true, force: true });
1721
1721
  assert.notEqual(r.status, 0);
@@ -3,8 +3,8 @@
3
3
  "schema": 1,
4
4
  "name": "codex-cli-bridge",
5
5
  "kind": "execution-backend",
6
- "version": "3.7.0",
7
- "posture": { "model": "gpt-5.6-sol", "effort": "xhigh", "tier": null },
6
+ "version": "3.8.0",
7
+ "posture": { "model": "gpt-6-astra", "effort": "high", "tier": null },
8
8
  "provides": ["execute", "review"],
9
9
  "roles": {
10
10
  "execute": {
@@ -141,7 +141,7 @@
141
141
  ],
142
142
  "guardrails": [
143
143
  { "value": "read-only sandbox — codex cannot edit, create or delete a file", "enforcement": "enforced", "source": "bin/codex-review.sh" },
144
- { "value": "runs on the pinned model at max effort", "enforcement": "enforced", "condition": "unless CODEX_PROBE=1 relaxes the guard for a throwaway probe", "source": "bin/codex-review.sh" },
144
+ { "value": "runs on the pinned model at the pinned effort", "enforcement": "enforced", "condition": "unless CODEX_PROBE=1 relaxes the guard for a throwaway probe", "source": "bin/codex-review.sh" },
145
145
  { "value": "a successful review appends one receipt line the review-state gate reads", "enforcement": "enforced", "condition": "a receipt write failure warns and the review still succeeds — the tree then reads un-receipted", "source": "capability.json roles.review.contract.receipt" }
146
146
  ],
147
147
  "customHooks": ["CODEX_PROBE"]
@@ -193,7 +193,7 @@
193
193
  "values": ["priority"],
194
194
  "default": null,
195
195
  "appliesTo": ["codex-exec", "codex-review"],
196
- "effect": "codex service tier; 'priority' (catalog display name Fast) = ~1.5x token speed at a 2.5x credit rate on gpt-5.6-sol — quality-neutral (same model), live-probed 2026-07-05. Unset/empty ⇒ no service_tier flag (standard tier). SPEND KNOB: enabling it is a consented per-host act, never a default. codex itself accepts any -c service_tier string silently, so the wrapper validates the value."
196
+ "effect": "codex service tier; 'priority' (catalog display name Fast) = \"2x speed, increased usage\" in the codex catalog's own words for gpt-6-astra (cache fetched 2026-09-05); it states no credit-rate figure — quality-neutral (same model). Unset/empty ⇒ no service_tier flag (standard tier). SPEND KNOB: enabling it is a consented per-host act, never a default. codex itself accepts any -c service_tier string silently, so the wrapper validates the value."
197
197
  },
198
198
  {
199
199
  "key": "CODEX_HARD_TIMEOUT",
@@ -20,7 +20,7 @@ judgment.
20
20
 
21
21
  ## Quality-first: model & effort are pinned
22
22
 
23
- Delegated codex work ALWAYS runs on the PINNED model at max effort: `gpt-5.6-sol` / `xhigh` are **pinned**
23
+ Delegated codex work ALWAYS runs on the PINNED model at the pinned effort: `gpt-6-astra` / `high` are **pinned**
24
24
  and a non-default `CODEX_MODEL`/`CODEX_EFFORT` is **refused** (exit 2). That the pinned id is still the
25
25
  strongest selectable model is a hand-checked claim with no automated gate and no recorded date — see
26
26
  SKILL.md. Do not try to "tune down" the
@@ -92,7 +92,7 @@ codex-exec --resume <session-id> <file|-> # explicit session id
92
92
 
93
93
  Resume continues the SAME codex session, so you avoid re-sending the original context. It runs through
94
94
  the wrapper, which **re-establishes every invariant** (subscription-only `*_API_KEY` scrub,
95
- `--ignore-user-config`, the pinned `gpt-5.6-sol`/`xhigh`) and **restates the full posture via `-c`** —
95
+ `--ignore-user-config`, the pinned `gpt-6-astra`/`high`) and **restates the full posture via `-c`** —
96
96
  because `codex exec resume` resets the sandbox/approval/network posture and rejects `-s`/`--add-dir`/`-C`,
97
97
  the wrapper passes `-c sandbox_mode=workspace-write -c approval_policy=never -c
98
98
  sandbox_workspace_write.network_access=false` explicitly. A resume takes no passthrough flags and an
@@ -47,7 +47,7 @@ carries the session id) into the run trace, with stderr merged into it; `--color
47
47
  `--color`, which stays on the fresh lane), so the resume lane is no longer the odd one out: it used
48
48
  to print its final message straight to stdout with the event stream nowhere, which left the very
49
49
  mode a nested-sandbox incident fired on without a structured evidence surface. Net effect: the wrapper prints just the final answer. **Reasoning still runs at
50
- `xhigh`** — quality is unchanged; only the *noise* is dropped. On success `codex-exec` extracts the
50
+ the pinned effort** — quality is unchanged; only the *noise* is dropped. On success `codex-exec` extracts the
51
51
  session id from the trace and records it to `${CODEX_SESSION_FILE:-./.codex-last-session}` (so
52
52
  `--resume-last` can find it) and echoes `session: <id>` to stderr. On a missing/empty final-message
53
53
  file it falls back to the trace tail (loud, never silent).
@@ -63,8 +63,8 @@ failure is proven.
63
63
 
64
64
  ## Quality-first guard (pinned model & effort)
65
65
 
66
- The wrappers default `CODEX_MODEL=gpt-5.6-sol` and `CODEX_EFFORT=xhigh` and **refuse** (exit 2, loud) any
67
- non-default — delegated work always uses the PINNED model at max effort; quality is never traded for
66
+ The wrappers default `CODEX_MODEL=gpt-6-astra` and `CODEX_EFFORT=high` and **refuse** (exit 2, loud) any
67
+ non-default — delegated work always uses the PINNED model at the pinned effort; quality is never traded for
68
68
  quota. (Whether the pinned id is still the strongest selectable model is a hand-checked claim with no
69
69
  automated gate — see SKILL.md.) `CODEX_PROBE=1` relaxes this for a throwaway, effort-independent probe only (echoed loudly), and
70
70
  a probe still runs on the subscription, in the sandbox, with clean capture.
@@ -164,7 +164,7 @@ EVIDENCE posture as a fresh run — `-o` for the final message, `--json` into th
164
164
 
165
165
  A backgrounded/hung run survives otherwise, so both wrappers wrap codex in `timeout`/`gtimeout`
166
166
  (`--kill-after=15s`): `CODEX_HARD_TIMEOUT` defaults to **3600s (exec)** / **1800s (review)**, sized for a
167
- slow `xhigh` run. Exit 124/137 ⇒ "exceeded the hard cap" (raise the cap or narrow the task). If neither
167
+ slow run at the pinned effort. Exit 124/137 ⇒ "exceeded the hard cap" (raise the cap or narrow the task). If neither
168
168
  `timeout` nor `gtimeout` is on `PATH`, `codex-exec` **warns loudly and runs uncapped** — never a silent
169
169
  no-op — while `codex-review` **refuses pre-spend** (the fail-closed hard-timeout preflight).
170
170
 
@@ -205,8 +205,8 @@ exec` over the precomputed diff instead, keeping every invariant intact.
205
205
  | `-o, --output-last-message <f>` | write ONLY the final message (clean capture) |
206
206
  | `--json` | structured event stream (`thread.started` ⇒ session id) |
207
207
  | `--output-schema <f>` | constrain output to a JSON schema (`CODEX_REVIEW_SCHEMA=1`) |
208
- | `-m <model>` | model (wrapper default `gpt-5.6-sol`, pinned via `CODEX_MODEL`) |
209
- | `-c model_reasoning_effort=<effort>` | reasoning effort (wrapper default `xhigh`, pinned via `CODEX_EFFORT`) |
208
+ | `-m <model>` | model (wrapper default `gpt-6-astra`, pinned via `CODEX_MODEL`) |
209
+ | `-c model_reasoning_effort=<effort>` | reasoning effort (wrapper default `high`, pinned via `CODEX_EFFORT`) |
210
210
  | `--ignore-user-config` | do NOT load `$CODEX_HOME/config.toml`; auth still uses `CODEX_HOME` |
211
211
  | `--color never` | disable ANSI colour in output |
212
212
 
package/capability.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "agent-workflow-kit",
5
5
  "kind": "composition-root",
6
- "version": "12.0.0",
6
+ "version": "12.2.0",
7
7
  "provides": [],
8
8
  "roles": {},
9
9
  "detect": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sabaiway/agent-workflow-kit",
3
- "version": "12.0.0",
3
+ "version": "12.2.0",
4
4
  "description": "Portable, cross-agent memory & workflow for AI coding agents — Claude Code, Codex, Cursor, Devin Desktop. One command deploys an AGENTS.md entry point + docs/ai context with cap/archive/index enforcement into any repo.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -8,7 +8,7 @@ The reader + consent-gated **writer** for the **host-level** bridge settings fil
8
8
 
9
9
  | key | bridge | values | note |
10
10
  |---|---|---|---|
11
- | `CODEX_SERVICE_TIER` | codex | `priority` | **SPEND KNOB** — the "Fast" tier: ~1.5× token speed at a **2.5× credit rate** on gpt-5.6-sol, quality-neutral (same model). Default unset ⇒ standard tier. |
11
+ | `CODEX_SERVICE_TIER` | codex | `priority` | **SPEND KNOB** — the "Fast" tier: "2x speed, **increased usage**" in the codex catalog's own words for gpt-6-astra (cache fetched 2026-09-05); it states no credit-rate figure; quality-neutral (same model). Default unset ⇒ standard tier. |
12
12
  | `CODEX_HARD_TIMEOUT` | codex | integer `1..86400` | hard wall-clock cap (seconds) via `timeout(1)`. |
13
13
  | `CODEX_REVIEW_MAX_TOTAL_BYTES` | codex | integer `1..100000000` | codex-review payload size above which the diff rides a temp file (never truncated). |
14
14
  | `AGY_HARD_TIMEOUT` | agy | duration `5m`/`30m`/`90s` (unit required, nonzero) | hard wall-clock cap via `timeout(1)`. |
@@ -25,7 +25,7 @@ The reader + consent-gated **writer** for the **host-level** bridge settings fil
25
25
 
26
26
  **Refusals (the guarded contract):** an **unknown key** or an **invalid / out-of-range value** → exit `2` (nothing read of the file, nothing written). A file that already carries **duplicate keys** → exit `1`, named and left **byte-untouched** (fix the duplicates by hand first — the writer never edits blindly around them). A **symlinked / non-regular / unreadable** settings file → exit `1` (refuses to write through it). `--apply` with `--dry-run`, a duplicate op for one key, or a malformed `--set` → usage exit `2`.
27
27
 
28
- **Spend consent (D4):** enabling `CODEX_SERVICE_TIER=priority` costs a **2.5× credit rate** — a per-host, consented act, never a default. The preview shows the caveat (from the manifest `effect`); confirm with the user before `--apply`. The tool also warns when an env var currently shadows the key you are writing (the env value wins for that session until unset).
28
+ **Spend consent (D4):** enabling `CODEX_SERVICE_TIER=priority` costs **increased usage** (the catalog states no credit-rate figure) — a per-host, consented act, never a default. The preview shows the caveat (from the manifest `effect`); confirm with the user before `--apply`. The tool also warns when an env var currently shadows the key you are writing (the env value wins for that session until unset).
29
29
 
30
30
  Output is **English/structured** — **localize it to the user's conversational language** when you narrate.
31
31
 
@@ -0,0 +1,30 @@
1
+ ### Mode: now
2
+
3
+ <!-- opt-in-capability: none — an inspection surface that configures nothing -->
4
+
5
+ Read-only. The single answer to where the work stands. It writes no file, cache, receipt, lock or git state; it never commits and never runs a subscription CLI.
6
+
7
+ Run `node ${CLAUDE_SKILL_DIR}/tools/now-cli.mjs --dir <project> --json`. For a direct terminal render, use `--format=plain|ansi|json`; `--json` is strict sugar for `--format=json`.
8
+
9
+ Render the returned envelope compactly in the user's conversational language. Never paste the JSON and never expose an internal field name. Preserve every fact, evidence label, annotation and withheld cause while translating only the prose around source-language commands, paths and identifiers.
10
+
11
+ The four blocks keep this order:
12
+
13
+ 1. **NOW** — every plan in flight, its derived phase, current and next step, then the tree fingerprint, clean state, review verdict lines, final run and red-proof currency. No plan and several plans are rendered states; never pick one silently.
14
+ 2. **STEPS** — every ledger row with one of `landed`, `in progress`, `pending` or `unjudged`. The rows are the steps; invent no numbering.
15
+ 3. **QUEUE** — every work-carrying row (live, parked, ambiguous) under the full line-numbered heading path it carries, with per-class counts covering every row of the bucket, terminal and record included, and names rather than ids. Only a path through `## Pending / backlog` calls its position priority; every other bucket is archive and calls it position.
16
+ 4. **CAMPAIGN** — source-size config state (`absent`, `authored`, `incomplete`, `minted`), caps, roots, recorded over-cap files and aggregate lines. An absent opt-in config is a rendered state.
17
+
18
+ Evidence alone sets a ledger status: the row's path and verb, then the path fact, changed set and exclusion fact. The plan's prose never sets a status. A checked box or terminal marker is only a claim; when evidence contradicts it, print the claim beside the evidence-derived status. Keep anomaly and current red-proof annotations beside the same status too. Every status line names its evidence source.
19
+
20
+ A block that cannot be derived is `WITHHELD` and prints its cause on one line. NOW has two halves, plans and tree, each independently a value or `WITHHELD`; NOW itself is `WITHHELD` only when both are. Render every other block normally.
21
+
22
+ Exit codes:
23
+
24
+ | Code | Meaning |
25
+ |------|---------|
26
+ | 0 | Anything rendered, including default states, contradictions and a WITHHELD block. |
27
+ | 1 | Nothing rendered: every block withheld, each cause printed on stderr; the usual cause is a `--dir` that is not a git work tree. |
28
+ | 2 | Usage: an unknown flag, an invalid or missing value, conflicting `--json` / `--format`, or `--help` combined with anything. |
29
+
30
+ **Invariants:** read-only · the plan's prose never sets status · evidence is shown beside every status · compact user-language render with no JSON or internal field names · `process.exitCode`, never `process.exit()`.
@@ -273,7 +273,7 @@ export const main = (argv = [], ctx = {}) => {
273
273
  const body = joinLines(newLines);
274
274
 
275
275
  // The machine surface carries the SAME spend/credit-rate caveats the human preview prints (D4) — a
276
- // --json consumer must not miss the 2.5x-credit consent warning the human render shows.
276
+ // --json consumer must not miss the spend consent warning the human render shows.
277
277
  const jsonBody = (wrote) => ({ path, wrote, noop, changes: changes.map((c) => ({ key: c.key, kind: c.kind, before: c.before, after: c.after })), envShadows, caveats });
278
278
 
279
279
  if (!apply || noop) {
@@ -84,6 +84,13 @@ const CATALOG = [
84
84
  kind: READ_ONLY,
85
85
  oneLine: 'Show what is installed and at what version, your settings and backends, and what is deployed in this project.',
86
86
  },
87
+ {
88
+ key: 'now',
89
+ invocation: invocationOf('now'),
90
+ group: 'Inspect',
91
+ kind: READ_ONLY,
92
+ oneLine: 'Show where work stands: NOW answers the plan and tree, STEPS the evidence-derived ledger state, QUEUE its named positions, and CAMPAIGN its size-practice figures; the plan’s own text never sets a status.',
93
+ },
87
94
  {
88
95
  key: 'backends',
89
96
  invocation: invocationOf('backends'),
@@ -18,7 +18,11 @@ const NOTES_HEADING = '## Notes';
18
18
  const TABLE_HEADER = '| # | Claim | Evidence | Verdict | Disposition |';
19
19
  const OBJECT_ID = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/iu;
20
20
  const ANCHOR = /^`([^`,\s]+):([1-9][0-9]*)(?:-([1-9][0-9]*))?`$/u;
21
- const ROW_ID = '[A-Z0-9][A-Z0-9-]*';
21
+ // The row id a checked record hands over, and the ONE home of that grammar: the queue's name
22
+ // judge reads it back for the EXPLICITLY marked form (`— id <ID>`), so every id this writer may
23
+ // legally emit round-trips through the judge — including `FOO`, `TASK-123` and `1-TASK`, which
24
+ // the judge's own heuristic deliberately does not read unmarked.
25
+ export const ROW_ID = '[A-Z0-9][A-Z0-9-]*';
22
26
  const QUEUE = new RegExp(`^queue (${ROW_ID})$`, 'u');
23
27
  const ALREADY_QUEUED = new RegExp(`^already-queued (${ROW_ID})$`, 'u');
24
28
  const REASONED_DISPOSITION = /^(?:declined|folded): (.+)$/u;
@@ -227,7 +231,7 @@ export const renderRows = (record, { date, recordPath }) => {
227
231
  return ids.map((id) => {
228
232
  const claims = record.claims.filter((claim) => QUEUE.exec(claim.disposition)?.[1] === id);
229
233
  return [
230
- `- [ ] ${id}: ${claims[0].claim} (${date})`,
234
+ `- [ ] ${claims[0].claim} — id ${id} (${date})`,
231
235
  ...claims.map((claim) => ` - ${claim.claim}; verdict ${claim.verdict}; evidence ${claim.anchors.map(rendersAnchor).join(', ')}`),
232
236
  ` - record: ${recordPath}`,
233
237
  ` - head: ${record.head}`,
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env node
2
+ // The CLI half of `now`: argv, the io seam and the pipeline. The rules live in now-status.mjs, the
3
+ // facts in now-facts.mjs, the phrases in now-render.mjs.
4
+
5
+ import { resolve } from 'node:path';
6
+ import { fail } from '../references/scripts/markdown-blocks.mjs';
7
+ import { isDirectRun } from './direct-run.mjs';
8
+ import { FORMATS, detectSurface } from './surface.mjs';
9
+ import { gatherNowFacts } from './now-facts.mjs';
10
+ import { isWithheld } from './now-status.mjs';
11
+ import { renderNow, toNowViewModel } from './now-render.mjs';
12
+
13
+ const BLOCKS = Object.freeze(['now', 'steps', 'queue', 'campaign']);
14
+ const SCHEMA = 1;
15
+ const FORMAT_FLAG = '--format=';
16
+ const HELP = `now — read-only: where the work stands, in four blocks.
17
+
18
+ Usage:
19
+ node now-cli.mjs [--dir <project>] [--format=<${FORMATS.join('|')}>] [--json]
20
+
21
+ --dir the project to read (default: the current directory).
22
+ --format the output surface; --json is strict sugar for --format=json, and naming both with
23
+ different values is usage.
24
+ --help answered only when it is the whole invocation.
25
+
26
+ Blocks: NOW (the plans in flight, the phase, the current and the next step, the tree), STEPS (the
27
+ ledger rows, one status each), QUEUE (the backlog folded under each row's heading path), CAMPAIGN
28
+ (the size practice). A block that cannot be derived is WITHHELD with its cause on one line.
29
+
30
+ A status is a function of the row's (path, verb) pair and the evidence at that path; the row's prose
31
+ is printed as a claim beside a status it never moves.
32
+
33
+ Exit codes: 0 anything rendered, a withheld block included; 1 nothing rendered at all (a --dir that
34
+ is not a git work tree); 2 usage.
35
+
36
+ Reads only: no store, no receipt, no cache, no lock.`;
37
+
38
+ const valueAt = (argv, index, flag) => {
39
+ const value = argv[index + 1];
40
+ if (value === undefined || value === '' || value.startsWith('-')) throw fail(2, `${flag} takes a value`);
41
+ return value;
42
+ };
43
+
44
+ export const parseArgv = (argv) => {
45
+ if (argv.includes('--help') || argv.includes('-h')) {
46
+ if (argv.length !== 1) throw fail(2, '--help is answered only when it is the whole invocation');
47
+ return { help: true, dir: null, formats: [] };
48
+ }
49
+ const parsed = argv.reduce((state, arg, index) => {
50
+ if (state.skip) return { ...state, skip: false };
51
+ if (arg === '--dir') return { ...state, dir: valueAt(argv, index, arg), skip: true };
52
+ if (arg === '--format') return { ...state, formats: [...state.formats, valueAt(argv, index, arg)], skip: true };
53
+ if (arg.startsWith(FORMAT_FLAG)) return { ...state, formats: [...state.formats, arg.slice(FORMAT_FLAG.length)] };
54
+ if (arg === '--json') return { ...state, formats: [...state.formats, 'json'], json: true };
55
+ throw fail(2, `unknown argument "${arg}" — run with --help`);
56
+ }, { help: false, dir: null, formats: [], json: false, skip: false });
57
+ if (parsed.json && parsed.formats.some((format) => format !== 'json')) {
58
+ throw fail(2, '--json is strict sugar for --format=json — naming both with different values is usage');
59
+ }
60
+ return parsed;
61
+ };
62
+
63
+ const resolveSurface = (formats, env, io) => detectSurface({
64
+ argv: formats.map((format) => `${FORMAT_FLAG}${format}`),
65
+ env,
66
+ isTTY: io.isTTY ?? Boolean(process.stdout.isTTY),
67
+ columns: io.columns ?? process.stdout.columns,
68
+ platform: io.platform ?? process.platform,
69
+ });
70
+
71
+ const toEnvelope = (dir, vm) => ({ schema: SCHEMA, command: 'now', dir, ...vm });
72
+
73
+ export const main = (argv, io = {}) => {
74
+ const log = io.log ?? console.log;
75
+ const error = io.error ?? console.error;
76
+ const env = io.env ?? process.env;
77
+ const start = (() => {
78
+ try {
79
+ const options = parseArgv(argv);
80
+ return options.help ? { help: true } : { options, surface: resolveSurface(options.formats, env, io) };
81
+ } catch (err) {
82
+ error(err?.message ?? String(err));
83
+ return { code: err?.exitCode ?? 2 };
84
+ }
85
+ })();
86
+ if (start.code !== undefined) return start.code;
87
+ if (start.help) {
88
+ log(HELP);
89
+ return 0;
90
+ }
91
+ const cwd = resolve(io.cwd ?? process.cwd(), start.options.dir ?? '.');
92
+ // The composed readers spawn git under process.env alone, so the flag must reach them there too.
93
+ process.env.GIT_OPTIONAL_LOCKS = '0';
94
+ const vm = toNowViewModel(gatherNowFacts({ cwd, env: { ...env, GIT_OPTIONAL_LOCKS: '0' } }));
95
+ const blocks = BLOCKS.map((name) => vm[name]);
96
+ if (blocks.every(isWithheld)) {
97
+ for (const cause of new Set(blocks.map(({ withheld }) => withheld))) error(cause);
98
+ return 1;
99
+ }
100
+ log(start.surface.mode === 'json' ? JSON.stringify(toEnvelope(cwd, vm), null, 2) : renderNow(vm, start.surface));
101
+ return 0;
102
+ };
103
+
104
+ if (isDirectRun(import.meta.url)) process.exitCode = main(process.argv.slice(2));
@@ -0,0 +1,67 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { join } from 'node:path';
3
+ import { buildSummaryState, isTreeClean } from './core-evidence.mjs';
4
+ import { readRegularFileNoFollow } from './fs-read-nofollow.mjs';
5
+ import { resolveGitLocation } from './git-env.mjs';
6
+ import { auditQueue } from './queue-audit.mjs';
7
+ import { loadSourceSizeConfig, practiceFacts } from './source-size-config.mjs';
8
+ import { gatherPlanFacts, readSummaryFacts } from './now-plan-facts.mjs';
9
+ import { isWithheld } from './now-status.mjs';
10
+
11
+ const QUEUE_REL = 'docs/plans/queue.md';
12
+ const BLOCKS = Object.freeze(['plans', 'tree', 'queue', 'campaign']);
13
+
14
+ const attempt = (label, read) => {
15
+ try {
16
+ return read();
17
+ } catch (error) {
18
+ return { withheld: `${label}: ${error?.message ?? error}` };
19
+ }
20
+ };
21
+
22
+ const readQueueFacts = (cwd, deps) => {
23
+ const read = readRegularFileNoFollow(join(cwd, QUEUE_REL), deps.io ?? {});
24
+ if (read.outcome !== 'ok') throw new Error(`${QUEUE_REL} is not a readable regular file (${read.className ?? read.code ?? read.outcome})`);
25
+ const { rows, counts, total } = auditQueue(read.content, { label: QUEUE_REL });
26
+ return { path: QUEUE_REL, rows, counts, total };
27
+ };
28
+
29
+ // The practice is opt-in: an absent declaration is a rendered STATE, only an unreadable one is withheld.
30
+ const readCampaignFacts = (cwd, deps) => {
31
+ const loaded = loadSourceSizeConfig(cwd, deps);
32
+ return loaded.state === 'absent' ? { state: loaded.state } : { state: loaded.state, ...practiceFacts(loaded.config) };
33
+ };
34
+
35
+ // A store the summary cannot trust never attests: the tree is withheld with the store's own cause.
36
+ const describeUntrustedStore = (summary) => {
37
+ if (summary.evidenceUnavailable) return `the core-evidence store is malformed or unreadable (${summary.storeReadError ?? `${summary.storeMalformed} malformed line(s)`})`;
38
+ if (summary.receiptsUnavailable) return `the review receipts store is malformed or unreadable (${summary.receiptsReadError ?? `${summary.receiptsMalformed} malformed line(s)`})`;
39
+ return null;
40
+ };
41
+
42
+ const toTreeFacts = (summary, clean) => {
43
+ const untrusted = describeUntrustedStore(summary);
44
+ if (untrusted) return { withheld: untrusted };
45
+ const { redProofs, receipts } = readSummaryFacts(summary);
46
+ return { fingerprint: summary.fingerprint, clean, verdicts: receipts.verdicts, finalRun: receipts.finalRun, redProofs };
47
+ };
48
+
49
+ export const gatherNowFacts = ({ cwd, env: given = process.env, deps = {} }) => {
50
+ // Every reader honouring an env argument spawns under this one: a plain `git diff` rewrites .git/index without the flag.
51
+ const env = { ...given, GIT_OPTIONAL_LOCKS: '0' };
52
+ const location = resolveGitLocation(cwd, { spawn: deps.spawn ?? spawnSync, env });
53
+ if (location.state !== 'work-tree') {
54
+ const withheld = `${cwd} is not a git work tree (${location.state}: ${location.cause})`;
55
+ return Object.fromEntries(BLOCKS.map((block) => [block, { withheld }]));
56
+ }
57
+ const root = location.top;
58
+ const summary = attempt('the evidence summary', () => deps.summary ?? buildSummaryState({ cwd: root, env }));
59
+ return {
60
+ plans: attempt('the plans in flight', () => ({
61
+ entries: gatherPlanFacts({ cwd: root, env, deps }).plans,
62
+ })),
63
+ tree: isWithheld(summary) ? summary : attempt('the tree state', () => toTreeFacts(summary, isTreeClean(root, { env }))),
64
+ queue: attempt('the queue', () => readQueueFacts(root, deps)),
65
+ campaign: attempt('the practice', () => readCampaignFacts(root, deps)),
66
+ };
67
+ };