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
7 changes: 6 additions & 1 deletion .agents/skills/afk/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name: afk
description: >-
Enter the away posture when the captain invokes /afk, says they are going afk, `state/.afk-contract` or `state/.afk` exists, an incoming message starts with `FM_INJECT_MARK`, or any `state/.subsuper-*` marker is involved.
It reads the captain's away words back as a mandate, writes the durable away-posture record after their go, announces hold-for-return only at entry, keeps the one supervision session running in the away posture (no daemon on Pi; the daemon still delivers batched digests on the other harnesses for now), and on the first unmarked message renders the return brief from durable records before ordinary work resumes.
It reads the captain's away words back as a mandate, writes the durable away-posture record after their go, announces hold-for-return only at entry, keeps the one supervision session running in the away posture (on Pi the supervision branch takes every safe actionable wake with main parked; the daemon still delivers batched digests on the other harnesses for now), and on the first unmarked message renders the return brief from durable records before ordinary work resumes.
user-invocable: true
metadata:
internal: true
Expand Down Expand Up @@ -41,6 +41,8 @@ Hold-for-return is the default and the only reach profile this release records:
4. **Per harness, after the record exists:**
- **Pi and pi-signed**: stop here.
The away daemon is no longer launched on Pi; the ordinary supervision session (`docs/pi-supervision-branch.md`) keeps running with the record present, and `bin/fm-afk-launch.sh start` refuses on these harnesses.
With the record present main is parked: the supervision branch takes every safe actionable wake, captain outcomes accumulate for the return brief, and main's standing authority relocates to the branch through the guarded scripts (`docs/pi-supervision-branch.md` "Postures"); only a wake the branch declines (including a broken branch or unsafe scan) or a watcher failure wakes main.
`/quiet` needs nothing extra on Pi: the attended branch already keeps routine wakes out of this conversation, so quiet-while-present is the attended posture's own shape there.
- **Harness WITH a native in-pane tracked-background tool** (claude's background bash, grok's background tool): run `bin/fm-afk-launch.sh start-native`, then run `FM_AFK_STATE_PREPARED=1 bin/fm-afk-start.sh` through that native tool.
This is a deliberate no-separate-terminal exception because the harness-hosted job creates no terminal or layout mutation, and a shell launcher cannot invoke a harness-native background tool.
If the native launch fails, run `bin/fm-afk-launch.sh stop` to roll back the prepared lifecycle.
Expand All @@ -58,6 +60,8 @@ Hold-for-return is the default and the only reach profile this release records:
Declared external waits keep their condition-aware, hours-long recheck cadence (`bin/fm-watch.sh`, `bin/fm-classify-lib.sh`).
- Recorded clauses are not executed by this release.
Forbidden, destructive, irreversible, and security-sensitive actions are never pre-authorizable regardless of clause text, no recorded clause is authority by itself, and merge authority plus ask-user findings keep exactly the rules they have when attended (`AGENTS.md` section 7 and `ask-user-authority`); anything that needs the captain holds for their return.
- On Pi, main is parked and the supervision branch handles every safe actionable wake under main's standing authority plus the record's merge grants, through the same guarded scripts main would use: a granted or `yolo` task merges only green at its live head, already-queued work whose blockers cleared dispatches within the spend cap, and only a finding `ask-user-authority` lets firstmate decide is answered.
Anything else holds for the return, local-only landing always waits for the captain, and only a wake the branch declines (including a broken branch or unsafe scan) or a watcher failure wakes main (`docs/pi-supervision-branch.md` "Postures").
- The session-start digest reports the posture under its AFK subsection, so a restart re-enters the posture from the record, not from memory.

## How to exit: the return
Expand Down Expand Up @@ -88,6 +92,7 @@ While the away-posture record exists, a merge proceeds only when that task's rec
A merge grant never releases a captain hold, and it expires when the away record is archived.
`--allow-red` remains attended-only and is refused while the record exists.
A merge under away authority must be synchronous; `fm-pr-merge.sh` refuses auto-merge and any GitHub queue state that cannot prove an immediate merge while the record exists.
The same gates bind whichever actor performs the action: on Pi the parked main's standing authority relocates to the supervision branch, which meets exactly these rules, and the spend cap recorded at entry is enforced by `fm-spawn.sh` for both actors while the record exists.
A mandate clause is the captain's explicit instruction given before leaving, recorded with its named object and condition; a clause is never inferred, never applied by analogy, and expires at return.
Forbidden, destructive, irreversible, and security-sensitive actions are never pre-authorizable regardless of clause text, and no recorded clause is authority by itself.
This release records clauses and does not execute them.
Expand Down
48 changes: 23 additions & 25 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ concurrency:
group: ci-${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

# Timeout policy: docs/fm-test-portable-shards.md "Timeouts" owns the three
# tiers and their rationale; tests/fm-ci-workflow.test.sh guards this workflow.
# Each job comment identifies the tier implemented by its executable value.

jobs:
lint:
name: Lint ${{ matrix.partition }}
runs-on: ubuntu-latest
# Keep the hang tripwire separate from the measured performance target.
timeout-minutes: 25
# Normal tier (see the timeout policy above).
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -68,7 +72,7 @@ jobs:
test-coverage:
name: Test coverage guard
runs-on: ubuntu-latest
# Hang tripwire: the coverage guard is a seconds-long local computation.
# Fast tier: the coverage guard is a seconds-long local computation.
timeout-minutes: 5
steps:
- uses: actions/checkout@v6
Expand All @@ -80,14 +84,8 @@ jobs:
tests-portable-parallel-1:
name: Behavior portable parallel 1
runs-on: ubuntu-latest
# This cap is intended as a hang tripwire, but the previous lane 1 reached
# it; the former "~1 min of serial sum" estimate no longer applies.
# Compare it with the derived hints from fm-test-run.sh --check-coverage
# and completed job timings, allowing for setup and runner-speed spread.
# A packed hint sum is not a measured job wall time or proof of headroom.
# Evidence and refresh procedure: docs/fm-test-portable-shards.md.
# Changes to this cap or the lane count require a separate scope decision.
timeout-minutes: 10
# Normal tier (see the timeout policy above).
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -130,8 +128,8 @@ jobs:
tests-portable-parallel-2:
name: Behavior portable parallel 2
runs-on: ubuntu-latest
# Same timeout rationale as portable parallel shard 1 above.
timeout-minutes: 10
# Normal tier (see the timeout policy above).
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -174,9 +172,7 @@ jobs:
tests-portable-serial:
name: Behavior portable serial ${{ matrix.shard }}
runs-on: ubuntu-latest
# Refreshed weights put the longest modeled shard near 12 minutes across
# nine runners. Preserve the existing hang tripwire until complete Linux
# measurements establish the new healthy envelope; a model is not a timer.
# Normal tier (see the timeout policy above).
timeout-minutes: 30
strategy:
# Every shard reports so one failure never hides another shard's result.
Expand Down Expand Up @@ -248,10 +244,9 @@ jobs:
tests-herdr:
name: Behavior tests (Herdr)
runs-on: ubuntu-latest
# Healthy runs finish around 7 minutes. This job cap is a last-resort hang
# tripwire, not the expected end of the lane. The family-run step owns the
# tighter bound so a wedged suite fails fast with always() cleanup and
# timing artifacts still uploaded (docs/fm-test-portable-shards.md).
# Heavy tier (see the timeout policy above): the last-resort job backstop.
# The family-run step below owns the hang tripwire, so a wedged suite fails
# there with the always() cleanup and timing upload still running.
timeout-minutes: 75
steps:
- uses: actions/checkout@v6
Expand Down Expand Up @@ -332,8 +327,9 @@ jobs:
mkdir -p "$RUNNER_TEMP/fm-herdr"
bin/fm-herdr-ci-cleanup.sh snapshot "$RUNNER_TEMP/fm-herdr/sessions-before.json"
- name: Run real-Herdr family (serial, required)
# Comfortably above the ~7 min healthy wall and far below the 75 min
# job backstop. A hang must fail this step so cleanup still runs.
id: run-real-herdr-family
# Heavy tier step tripwire: above the healthy 7-10 minute wall and far
# below the job backstop, so a hang fails this step and cleanup runs.
timeout-minutes: 20
run: |
set -eu
Expand All @@ -344,6 +340,7 @@ jobs:
--fail-on-gate-skip 'herdr not found' \
--json "$RUNNER_TEMP/fm-test/fm-test-timing-herdr.json"
- name: Cleanup job-owned Herdr lab sessions
id: cleanup-herdr-lab-sessions
if: always()
run: |
set -eu
Expand All @@ -368,7 +365,7 @@ jobs:
tests-timing-aggregate:
name: Behavior timing aggregate
runs-on: ubuntu-latest
# Hang tripwire: aggregation is seconds of work over lane artifacts.
# Fast tier: aggregation is seconds of work over lane artifacts.
timeout-minutes: 5
needs:
- tests-portable-parallel-1
Expand Down Expand Up @@ -408,7 +405,8 @@ jobs:
macos-stock-bash:
name: Stock macOS Bash snapshot compatibility
runs-on: macos-latest
timeout-minutes: 10
# Normal tier (see the timeout policy above).
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- name: Run snapshot consumers with stock Bash
Expand Down Expand Up @@ -480,7 +478,7 @@ jobs:
invariants:
name: Repo invariants
runs-on: ubuntu-latest
# Hang tripwire: the invariant checks are seconds-long file comparisons.
# Fast tier: the invariant checks are seconds-long file comparisons.
timeout-minutes: 5
steps:
- uses: actions/checkout@v6
Expand Down
Loading
Loading