Why: systemd's default KillMode=control-group kills every process in
the unit's cgroup when the oneshot finishes -- the daemon recover just
started died seconds after the recover ok event (observed live on the
host: revived at 22:13:40, gone by 22:13:42).
Evidence: unit text now carries KillMode=process (asserted in
test_watchdog_print_plan); suite 10/10 and make test-governance rerun
recorded on the card; live re-demo on the host after merge.
Impact: scripts/svc.sh wd_service_text only.
Refs: .task/feature-svc-autorecover.md
Why: include the current integration baseline before verification.
Evidence: fetched origin/main=3de5791e0841e9c7c40c3dc236be6d95b3969827.
Impact: integrate upstream changes without rewriting task history.
Refs: task-sync
Why: check_port_free consulted port_owner (ss -ltnp) only, so the
/dev/tcp fallback added to port_listening never ran on the start path;
CI (no iproute2) still started onto a thief-held port.
Evidence: no-ss simulation refuses a bound port and passes a free one;
full suite 10/10 and make test-governance rerun recorded on the card;
CI on the new head is authoritative.
Impact: scripts/svc.sh check_port_free only.
Refs: .task/feature-svc-autorecover.md
Why: the CI runner container has no iproute2, so port_listening went
blind: recover started a service onto a thief-held port and the health
probe hit the thief (HTTP 200), reporting a bogus start success.
Evidence: no-ss PATH simulation shows the fallback detects a bound port
and passes free ports; make test-governance rerun recorded on the task
card; CI on the new head is authoritative.
Impact: scripts/svc.sh port_listening only (ss stays primary when
present).
Refs: .task/feature-svc-autorecover.md
Why: RDM capabilities (two-trees-one-net, rdm.* trace-audit command face)
only exist in the downstream Agent-System-RDM composite binary; svc's three
existing sources all produce the Dev binary, so none of it could be deployed
or lifecycle-managed. This adds --from rdm[@<sha>] with the same isolated
source-repo semantics as the dev source.
Evidence: make test-governance 138/138 (two new stub cases: deploy E2E and
commits/update track); real deployment rdm@2aef3fc recorded in the task card
(pins materialized, pure-Go build, web workbench to htdocs, serve health OK,
rdm.ping/rdm.inspect.capabilities committed, update --check reports latest);
make test full suite exit=0. All segments written by make evidence.
Impact: scripts/svc.sh (rdm source resolution/materialization/deploy/update/
commits, probe timeouts, rdm seed skip, ELF env-DSN evidence fallback),
docs/svc.md 5.4, tests/test_svc.py. RDM-side CLI alignment landed as RDM
PR #228; no base submodules touched.
Refs: task card .task/feature-svc-rdm-source.md; RDM PR #228/#229.
Why: include the current integration baseline before verification.
Evidence: fetched origin/main=acc8bf124c04b89ed05093467ebce865934bdddc.
Impact: integrate upstream changes without rewriting task history.
Refs: task-sync
Why: svc services are bare nohup'd processes with persisted registration
but no supervision -- a crash, an OOM kill, or a reboot left them dead
until manual restart (2026-10-11 outage: dev service lost, runtime/ also
wiped by hand, nothing brought services back).
Evidence: tests/test_svc_recover.py 10 cases green (start records
autostart, stop clears it incl. dead-process stop, SIGKILL then recover
revives with persisted exec=, lock-held skips, port-stolen fails with
recover fail event, watchdog unit text via --print); make test-governance
146/146 OK locally; real-machine watchdog install + kill -9 revival to be
recorded on the task card.
Impact: svc.sh gains autostart key in svc.env, `recover [name]
[--quiet]` (postgres-first, per-service .start.lock, recover history
events), `watchdog install|remove|status` (systemd user timer, cron
fallback hint), lifecycle lock across start/stop/restart/deploy/update;
spawned services no longer inherit the lock fd (9>&-), and lock release
no longer clobbers script stderr. Makefile adds svc-recover/svc-watchdog;
docs/svc.md documents semantics and boundaries.
Refs: task card .task/feature-svc-autorecover.md; docs/svc.md 6.5/6.6;
parallel planning card feature/service-restart-recovery-plan (same
problem space, plan-stage).
Why: task-sync advanced the Dev backend gitlink past the RDM pin; a newer-schema user-create CLI migrates the fresh store beyond what the RDM binary (schema ceiling of its pin) can open.
Evidence: reproduce on 20261011-180439 run (v25 store vs v24 binary); rerun green recorded after this change.
Impact: scripts/e2e.sh only; seeding and serving now share one base version.
Why: git submodule clone over the flaky Gitea SSH endpoint hangs the whole run; local archive + pinned-checkout snapshot removes the network dependency and cuts the build stage to seconds.
Evidence: this exact tree ran scripts/e2e.sh to green (4 passed) twice, recorded in the task card.
Impact: scripts/e2e.sh only; behavior unchanged when the network is healthy.
Why: U4 acceptance needs a reproducible real-browser run: build the
RDM binary from its origin/main, seed projects/ingest via the command
API, serve with auth on, and drive Playwright against vite preview.
Evidence: scripts/e2e.sh --seed-only smoke (in progress this task);
frontend pin points at the Playwright harness commit.
Impact: dev tooling only (.tmp artifacts ignored; no runtime changes).
Refs: task feature/rdm-e2e-acceptance; frontend feature branch of
the same name.
Why: seed_settings PUTs /api/settings/user without credentials, so under the PG tier's always-on auth the seed always 401s and model config silently never lands (reproduced on the dev instance 2026-10-11; settings had to be written by hand).
Evidence: make test-governance && make test exit=0 (receipt 8ffc3a89); tests/test_svc.py adds three stub-based cases - auth-on seeds with a Bearer header and create/revoke around the PUT, --seed-owner skips account lookup, auth-off stays credential-free; 14/14 svc tests green.
Impact: deployments that pass SVC_MODEL_*/SVC_TAVILY_KEYS now seed reliably regardless of auth mode; a temporary apikey (name=svc-seed) is minted for the first admin (or --seed-owner/SVC_SEED_OWNER) and revoked right after; auth-off path unchanged.
Refs: task card feature-svc-seed-auth.
Why: main landed fix-svc-dsn-reuse-dbref (reuse-path dbref restore, env-DSN
aware precheck, DSN_ENV scoping) for the same restart-DSN incident; the
approved recovery plan is its superset and replaces those mechanisms with
resolve_db_config + recoverable svc.env keys + refuse-on-invalid semantics.
Evidence: tests/test_svc_precheck.py ported to the new units (18 cases green,
same scenario matrix; broken-dbref expectation updated to refusal per the
approved plan); tests/test_svc.py 11 cases green; bash -n OK.
Impact: conflict hunks resolved toward the unified resolver; main's source
guard at the script tail restored; agent_listen_rejected kept as a pure
predicate (used by the start precheck and the governance tests).
Refs: main 7acf318 (PR #317); task card .task/feature-svc-db-config-recover.md.
Why: the exec= reuse path stripped --dsn into the process environment (G1-7)
and record_state then persisted a DSN-less exec=, while the dbref fallback only
existed in the explicit-rebuild branch - the second bare restart after a
rebind silently dropped the database (SQLite fallback on loopback). Auth
preflight also matched --dsn on the command line only, so env-delivered DSN
with auth=auto was misjudged as unauthenticated.
Evidence: bash -n OK; make test-governance 115 tests OK (new tests/test_svc.py
11 cases; 10/11 fail against the pre-fix script); make test green; make build
OK; isolated real PostgreSQL 18.6 + real binary validation 27/27 (consecutive
restarts/deploy keep binding, psql-verified database identity, DB-down restart
fails without SQLite fallback or config loss, no credential leakage) - all
recorded via make evidence in the task card.
Impact: svc.env gains db=/dbref=/dsn= recovery keys (atomic 0600 writes);
start/restart/deploy/update/db share one resolver; invalid bindings, config
conflicts, mock coexistence and env-DSN-incapable binaries now refuse to
start instead of silently downgrading; legacy exec= DSN migrates idempotently;
docs/svc.md and script help updated.
Refs: task card .task/feature-svc-db-config-recover.md; user-approved plan
(2026-10-11 session message); AGENTS.md evidence gates.
Why: G1-7 moved the DSN into AGENT_SYSTEM_DSN, but the precheck only read
the arg string, so a non-loopback restart with auth auto was wrongly
rejected (2026-10-10 dev incident); the reuse path no longer restored the
dbref-bound DSN and would silently fall back to SQLite; and DSN_ENV leaked
across same-process starts (update --all) into other services' env.
Evidence: tests/test_svc_precheck.py 19 cases green (auth matrix vs
config.go AuthEnabled, reuse migration/dbref restore, scope isolation);
full governance suite 123 green; incident-scenario replay allowed + DSN
carried. Recorded via make evidence.
Impact: scripts/svc.sh precheck and reuse-start DSN handling only; no
binary change, no persistence-format change.
Refs: .task/fix-svc-dsn-reuse-dbref.md; config.go AuthEnabled (on || (not
off && DSN non-empty), env DSN counts).
Why: the plan section 3.4 observed the local dev service carrying the
credential-bearing DSN as a serve --dsn argument, visible to any
process on the host (ps / /proc/<pid>/cmdline) — including scripts
generated by agents. The base config layer already supports
AGENT_SYSTEM_DSN, so the launcher can move the secret out of the
argument vector without touching the binary.
Evidence: bash -n clean; make test-governance 104/104 green; an
end-to-end probe with a stub binary and a legacy exec= carrying
--dsn ...secret... shows the migration stripping the flag
(MIGRATED:yes), the process args free of the secret
(NO-LEAK-IN-CMDLINE), and AGENT_SYSTEM_DSN present in the process
environment (DSN_IN_ENV:yes). New/rebuilt launches never put the DSN
in argv; legacy persisted configs are migrated on first reuse.
Impact: scripts/svc.sh only — rebuild path sets DSN_ENV instead of
--dsn, reuse path strips --dsn from stored exec= once and logs the
migration, launch injects AGENT_SYSTEM_DSN alongside the existing
guard vars. CLI --dsn still wins inside the binary (config
precedence), so manual invocations are unchanged.
Refs: plan G1-7 (line 356) and section 3.4 (2026-10-09 ps
observation); backend config AGENT_SYSTEM_DSN.
Why: include the current integration baseline before verification.
Evidence: fetched origin/main=878e0e7ceba3bb6b78c36c5113eacc846a7f017b.
Impact: integrate upstream changes without rewriting task history.
Refs: task-sync
Why: backend #83 merged the slow-lane wiring (intentsem scorer/
generator, gate SetSemantic/SetGenerator, settings flags default off);
the Dev pins and boundary rules must move with it.
Evidence: backend CI green on 9d29371; check-pins consistent
(agent-intent 343b1e6 across Dev and the backend nested copy);
lint-boundaries registers intentsem as an agent-intent adapter and the
architecture baseline gains backend/app->backend/intentsem.
Impact: product behavior unchanged by default; the two
runtime.intent* flags opt into the slow lane.
Refs: Agent-System-Backend#83, I2 increment 3
Why: preserve project direction across tasks and require grounded next-task recommendations.
Evidence: the generated task-card regression fails before the template update and passes afterward; original SELF receipts are in the task card.
Impact: document the planning entrypoint and extend task records without granting new-task execution rights.
Refs: .task/feature-goal-continuity.md; policy/delivery.md
Why: archived task cards change directory and must retain an unambiguous policy reference.
Evidence: review finding 1, focused red-green proof and 104 passing governance tests are preserved in the task card.
Impact: use a repository-root text reference; record the completed review and its resolved finding.
Refs: .task/feature-iterative-delivery.md; .task/reviews/iterative-delivery.json
Why: main advanced with merged-task-name and legacy evidence guards during this task.
Evidence: SELF 44 lifecycle regressions pass after resolving both hunks; independent review repairs are recorded in the task card.
Impact: retains both task-start protections and the stronger JSON/legacy receipt reader, without rewriting either branch history.
Refs: Dev PR #211 and #212; .task/feature-governance-reliability.md
Why: independent review found shared submodule metadata risk, quoted header leakage, stale JSON receipt blindness and missing abandon coverage.
Evidence: SELF 92 governance regressions pass; the original review receipt and replay output are recorded in the task card.
Impact: preserves external component worktrees, handles both evidence formats and adds targeted failure-path coverage; no product behavior changes.
Refs: .task/feature-governance-reliability.md; .task/reviews/governance-reliability.json
Why: cleanup, component synchronization, receipt recording and credential handling could contradict the documented development boundaries.
Evidence: SELF 75 isolated governance tests and source checks are captured in .task/feature-governance-reliability.md; combination and CI verification follow.
Impact: hardens local task and CI tooling, aligns policy and component documentation, and adds continuous regression tests without changing product behavior.
Refs: .task/feature-governance-reliability.md; policy/governance.md; policy/platform-admission.md
Why: evidence rows are written before their containing card commit, so a naive
HEAD-diff would flag every task's evidence as stale and train people to ignore
the warning.
Evidence: bash -n clean; the live task-sync for this task exercises the new
path (warning fires only when a non-.task diff follows an evidence row's HEAD).
Impact: scripts/task.sh cmd_sync now excludes .task from the staleness diff;
AGENTS.md wording aligned (three-output evidence via make evidence).
Refs: task card evidence table; guidelines verification.md tool-written rule.
Why: the make-variable pass-through re-quoted CMD and broke any command
containing quotes (caught by the redaction self-test before it could run).
Evidence: redaction self-test now records 'token <redacted> hello' with quoting
intact (row sha256 aae6a895); negative 'false' case earlier recorded exit=1
row and propagated rc — both in the task card evidence table.
Impact: CMD now travels via environment (CMD="..." make evidence); SRC/TAIL
stay make variables; usage strings updated in Makefile/AGENTS.md/task.sh
template comment.
Refs: task card evidence table; guidelines draft.10 tool-written-evidence rule.
Why: declarations were passing as capability; hand-transcribed outputs flowed
through cards and PR bodies as if they were execution evidence, and stale
external facts (e.g. the dead git-panel host) kept being trusted until burned.
Evidence: guidelines draft.10 (PR Development-Guidelines#4, merged af199ee)
pinned here; policy/facts.md F1-F4 each re-verified on 2026-10-05 before
registration (endpoint version, token HTTP 200 via header only, SSH ls-remote,
dead-host negative probe); scripts syntax-checked (bash -n); tool self-tests
and make test recorded in the task card via the new make evidence.
Impact: new AGENTS.md section (SELF/REPLAY/GATE, tool-written evidence,
replay-on-review, zero-trust facts, honest-empty); make evidence/facts-verify;
precheck gains [hooks]/[facts], task-sync gains stale-evidence-HEAD warning;
task-start cards now embed the evidence table; gitlink advances to draft.10.
Refs: task card .task/feature-evidence-driven-gates.md; guidelines CHANGELOG
0.1.0-draft.10.
Why: adjust_args_for_bin strips the leading space of its return while the
rebuilt args keep it, so every rebuild carrying --dsn/--auth compared
unequal and warned that the legacy binary had the flags dropped even when
nothing was dropped — misleading during incident triage.
Evidence: bash -n clean; against the real dev binary the normalized
comparison reports no drop and the flags survive intact; make test green.
Impact: warning now fires only when flags are actually filtered; no
behavioral change to start/deploy.
Refs: PR #188
Why: the 2026-10-05 dev outage — a restart with an explicit --addr rebuilt
the start args, and the rebuild branch dropped the persisted --dsn for the
dev env, so the binary resolved auth=auto to off on 0.0.0.0 and the new
listen guard refused to spawn; record_state then persisted the broken exec=
before the health probe, so bare starts kept failing.
Evidence: bash -n clean; sandbox registry with stub binaries — dbref DSN is
re-derived on rebuild (priority CLI > env > dbref), the pre-flight blocks
non-loopback without auth with actionable remedies, immediate-death starts
leave the previous exec= intact, and DSN masking covers all start-path log
lines; make test green.
Impact: dev/prod agent starts keep their DB binding across explicit-arg
rebuilds; failed starts no longer corrupt the registry; static/pg paths get
the same persist-after-probe ordering (pg already had it); no change to
deploy materialization or the binary guard semantics.
Refs: backend 8eda35f (listen guard), runtime/dev history.jsonl 01:23 fail
Why: The research record, HTML cleaning, listen guard, secret store, and context controls land together, and the parent pointers have to name those commits.
Evidence: Child commits are core bd63223, agent-native 3a110a7, ui d76a8e6, frontend 36b92ed, documents f3f823e, backend 42cca8f. svc.sh forwards the listen switches and info prints them.
Impact: A reverse proxy or a LAN hostname needs allowedHosts or an unauthenticated host check returns 403. Startup moves raw API keys into the secret table after a settings snapshot.
Refs: .task/feature-sillytavern-review.md
Co-authored-by: Cursor <cursoragent@cursor.com>
Why: beyond database binding, services needed a way to switch the JSON config
file they run with and have the new config take effect promptly; hand-editing
exec= risks restarting a service into an invalid config.
Evidence: in the task worktree with a real agent binary -- a config with an
unknown key is rejected by the binary's strict schema check before any state
change; a valid file binds via --config in exec= + conffile= (survives
record_state rewrites), restarts the running service, and the config's dsn
verifiably drives the process (wrong-password variant failed PG auth in the
log; correct variant connected and created 21 tables); editing the file and
re-running 'config <same file>' re-validates and restarts (pid changes);
'--clear' removes both the flag and conffile; db and config coexist in exec=;
view mode shows masked JSON content plus 'config explain' effective values
with masked dsn; list DB column also resolves dsns that live only inside the
config file. Full log in the task card.
Impact: new service-level action 'svc.sh <name> config [file | --clear]
[--no-restart]' for agent services; config-file content snapshots (masked,
--reveal to show) in info; a config-file line in status; conffile persisted
in svc.env. No agent binary changes.
Refs: task card .task/feature-svc-db-bind.md (same doc as PR #182);
docs/svc.md sections 2/4.5/4.6/6/10.1
Why: info showed services and postgres instances but no service-to-database
correspondence, and switching an agent service to another database required
redeploying with a hand-built DSN. While wiring this up, adjust_args_for_bin
was found matching Go flag help with '--dsn' although Go prints '-dsn', so
deploy-built --dsn/--auth args were silently dropped on current binaries.
Evidence: full E2E in the task worktree with a real agent binary and a real
PostgreSQL 18 instance -- 't-app db bpg' persisted exec=/dbref, auto-restarted
(dbref survives record_state rewrites), and the agent created 21 tables in the
bound database; 'db bpg --db t2' auto-created and switched databases (21
tables in t2); '--clear' returned the service to embedded SQLite; --no-restart
and error paths behave; list gains a DB column, status a database line, info a
reverse bound-services line; legacy deployments without dbref resolve by DSN
host:port; single-dash detection proven with positive and negative cases;
regressions list/db dsn/db ls/info/history/--help pass; make test exit 0.
Full log in the task card.
Impact: new service-level action 'svc.sh <name> db [pg [--db X] | --clear]
[--no-restart]' for agent services (bind/switch/unbind/view), DB column in
list, binding lines in status/info, dbref persisted in svc.env, auto database
creation when the instance runs, and the adjust_args_for_bin fix restoring
deploy --dsn/--auth on current binaries. No agent binary changes.
Refs: task card .task/feature-svc-db-bind.md (same doc as the PR); docs/svc.md
sections 4.5/6.1/6.2/10.1
Why: svc.sh could show a service table (list), per-instance DB names one at a
time (db ls), and per-service status, but had no single view answering "what
is running, which databases exist, and what is configured" -- the questions
asked when inspecting or handing over a machine.
Evidence: exercised in the task worktree runtime -- empty-state report; fake
agent/static/postgres registrations proving masking (exec DSN password,
password=, SVC_TAVILY_KEYS -> ***, non-secret values verbatim; --reveal shows
plaintext); a real postgres instance (db create r-pg --port 18901, start,
extra orders db, one backup) whose info section lists inner databases with
sizes, pgdata size and backup stats; regressions list/history/--help/help
lifecycle/make svc-info; info stdout carries the full report (stderr 0 bytes)
so it can be redirected as a snapshot. Full log in the task card.
Impact: new read-only `scripts/svc.sh info [name] [--tag T] [--reveal]`
command (also `make svc-info`): service table + running summary, postgres
instances incl. inner databases, per-service svc.env content snapshots plus
global .svc.env / --config / exported SVC_* vars, and recent events. Secrets
are masked by default. No deploy/lifecycle/db-ops behavior changes; `info`
becomes a reserved service name; docs/svc.md gains section 6.2 (former
6.2-6.4 shifted to 6.3-6.5).
Refs: task card .task/feature-svc-info.md (same doc as the PR); docs/svc.md
Why: New cross-layer imports and unused exports could land unnoticed, and confirm-waiting reused the success color.
Evidence: bash scripts/lint-boundaries.sh passed, including a probe that a new edge fails. Both design-guard tests passed. Knip counts stay at the baselines.
Impact: make test-boundaries now checks architecture edges. Frontend and ui test targets also run the knip ratchet.
Refs: feature/architecture-baseline
Co-authored-by: Cursor <cursoragent@cursor.com>
Why: Dev has to test the split modules and refuse a core import of a sibling library.
Evidence: bash scripts/lint-boundaries.sh printed boundaries ok. Full make test follows on this pin.
Impact: test-libs runs the intent, native, sqlite, and command modules. The sibling allowlist is empty.
Refs: D1
Co-authored-by: Cursor <cursoragent@cursor.com>
Why: Merge commits share a tree with the pinned feature commit, the import ratchet downloaded modules during CI, and task sync wrote the checkpoint onto the wrong card.
Evidence: check-pins accepts 6c0f1e8 (same tree as Backend squad 8e987bf) and rejects empty-tree 8db069d; both objects stayed local. An empty GOMODCACHE run of lint-boundaries matches the cached go list, 14 pairs. bash -n scripts/task.sh.
Impact: make pin no longer checks out Backend nested core or agent-command, and a failing pin check does not fail the target. task sync writes the branch card or warns. precheck reports completed-but-unarchived cards and card-close branches older than 24 hours.
Refs: feature/agent-stack-r0-r1
Co-authored-by: Cursor <cursoragent@cursor.com>
Why: Dev could ship a combination whose nested gitlinks differed from the top-level pins, and make test never ran the three agent libraries.
Evidence: A go.work was measured and not adopted: workspace MVS selected newer golang.org/x crypto, exp, net, term, and text than backend with GOWORK=off. check-pins passes on the new pins and fails when backend's squad pin is 5cbac00.
Impact: make test adds pin checks and GOWORK=off vet/test for intent, native, and squad. make build still compiles inside backend; the pin check requires those gitlinks to match Dev.
Refs: R0
Co-authored-by: Cursor <cursoragent@cursor.com>
Why: Downstream products need a published extension surface, contract checks, and a sample plugin that does not modify the base.
Evidence: scripts/lint-boundaries.sh printed boundaries ok. examples/downstream-rdm go test passed. Submodule pointers record the core, documents, backend, ui, and frontend commits for this roadmap.
Impact: make test runs the boundary lint first. make plugin-new scaffolds a declaration plugin. Version tags and remote downstream repositories are not created.
Refs: .task/feature-base-extension-roadmap.md
Co-authored-by: Cursor <cursoragent@cursor.com>
Why: svc.sh update skipped services with a local@<sha> source whenever the
main checkout was dirty, and bare deploy built the current workspace. The
owner requires updates/deploys to be independent of the working tree
state, defaulting to the latest remote commit built outside the checkout.
Evidence: bash -n passes; commits dev lists from the source clone; bare
deploy --dry-run plans dev@<origin/main>; end-to-end "svc.sh update
svc-selftest" with a dirty tree updated a local@d64742c service to
dev@0be23f9 (built in runtime/.worktrees from
~/.cache/agent-system-svc/src, started healthy), then stopped and purged.
Full make test output recorded on the task card.
Impact: deploy now defaults to dev@<origin/main> resolved in the
script-owned clone (SVC_SRC_DIR, default ~/.cache/agent-system-svc/src);
--from local stays the only workspace-dependent source; update no longer
pulls the main checkout - local@/dev@ services redeploy from remote
latest, ahead/diverged/missing-on-remote still decline to act.
docs/svc.md and in-script help updated to match.
Refs: .task/svc-remote-source.md
Why: two recurring papercuts from 2026-09-30 sessions — (1) task-start treated a zero-exit submodule update as complete, but a worktree could still carry 12 drifted/uninitialized nested submodules that only surfaced at build time; (2) close ran a single git push, so a tunnel hiccup left the card-close branch unpushed and the auto-PR never created (hand-patched twice that day).
Evidence: bash -n clean; smoke start of a throwaway task measured nested drift count explicitly and abandon-path finish recycled worktree and branch cleanly (both PASS); fresh-clone commit identity probe also PASS after setting the global identity outside this repo.
Impact: scripts/task.sh cmd_start (+3-line status check) and cmd_close (one retry on push); no interface change.
Refs: .task/fix-followup-hardening.md
Why: cmd_close posted the PR with a single curl wrapped in
2>/dev/null || true, so any transient TLS/timeout on the git-home
HTTPS surface left the card-close branch pushed with no PR and no
diagnostics; this hit three times on 2026-09-30 and each needed a
manual API call to recover.
Evidence: bash -n clean; stub-server harness running the block
verbatim (sed-extracted from this file, executed inside the worktree
branch context): ok mode recovers the PR URL on first POST; exists
mode (POST always 500) retries 4x with visible stderr then recovers
the pre-existing open PR via GET ?state=open matching head ref;
fail mode against the retired git-panel host shows four visible
retry notices and falls through to the existing [next] hint.
Impact: scripts/task.sh cmd_close only; precheck/start/sync/finish
untouched; card-close merge authority unchanged (owner merges).
post-merge hook and task-finish auto-close inherit the fix through
the same function.
Refs: task card fix-close-pr-retry
Why: cmd_finish ran git submodule deinit --all, but submodule.<name>.url entries live in the shared .git/config — finishing any task silently marked every other checkout and worktree uninitialized (observed twice on 2026-09-30: main checkout uninit=12 after each finish; a parallel session hit it minutes before this fix landed). The deinit serves no purpose: worktree removal with gitlinks is refused regardless, so cleanup already goes through rm -rf + prune. Also aligns AGENTS.md with observed hook behavior (post-merge fires on ff-only pulls too, and its inline close cannot see cards merged remotely until they are pulled), and ignores the ZCode-generated .zcodeignore that tripped the BUSY gate in every session.
Evidence: bash -n clean; abandon-path smoke of the patched script — start + FORCE_ABANDON=1 finish of a throwaway branch left shared submodule url config at 12 entries (pre-fix: 0), main checkout submodule status uninit=0, smoke worktree and branch fully recycled; the smoke close also backfilled a parallel session card left open (gitea-access-facts) via a card-close branch, proving the fallback path.
Impact: scripts/task.sh cmd_finish only (removes one line, updates comment), AGENTS.md card-close paragraph, .gitignore one entry. No product or gitlink change; make test not applicable (repo-level tooling/docs only). web_base default fix intentionally withdrawn from this PR — landed via parallel PR #73.
Refs: .task/fix-task-finish-close-followups.md
Why: the TASK_CARD_WEB_BASE default in scripts/task.sh still pointed at
git-panel.u1680832.nyat.app:30319, which now fails the TLS handshake
(curl exit 35), breaking card-close PR links and API calls; agents also
kept rediscovering hosts and credential locations from scratch each
session.
Evidence: curl https://git-panel...:30319/api/v1/version -> exit 35; curl
https://git-home...:30319/api/v1/version with token from ~/.secrets/gitea
-> {"version":"1.27.3"}; ssh -p 31411 git@git-ssh... -> 'Hi there,
weichen!' with key ~/.ssh/home_gitea (ED25519, fingerprint
SHA256:GYXaOXIMeloQ1fKSfdJ/ndgPQzs/Ix7Hk62ekfkfQ20, key name GMKtec).
Impact: task.sh default web base now resolves to the live host; AGENTS.md
gains a Gitea access-facts section recording hosts plus credential
locations only (no secret values, per baseline hard boundary 2). Follow-up
PRs propagate the same section to the other project repos.
Refs: task card feature-gitea-access-facts