feat(scene): auto-detect + scenes.yaml persistence (P2)#116
Merged
Conversation
P1 (#114) shipped scene awareness but kept resolution opt-in: scenes fired only via --scene or DITING_SCENE, defaulting to home otherwise. User feedback in real use: "I'm clearly in an office (many same-name APs, enterprise auth), why does diting still show [家]?" That's the P1 contract working as designed, but not the experience the user wants. P2 closes the gap with two pieces that work together: 1. scenes.yaml per-network persistence -- same idea as aps.yaml. Optional file in cwd (DITING_SCENES_FILE overrides path), git-ignored, human-curated only. Maps SSID -> scene (with gateway_mac as fallback for SSID-collision cases like eduroam). 2. Auto-detect heuristic -- when no CLI / env / yaml decides, diting inspects the active Wi-Fi connection synchronously at startup and classifies. Rules: - security contains "Enterprise" (case-insensitive) -> office - visible_bssid_count >= 30 -> office - otherwise -> home public stays opt-in (captive-portal detection needs active probing we don't want to do). Resolution precedence is now 5-tier (was 3): 1. --scene CLI flag 2. DITING_SCENE env var 3. scenes.yaml (SSID or gateway_mac match) 4. classify_environment heuristic 5. default home scene_source field extends from {cli, env, default} to {cli, env, yaml, auto, default} -- session_meta records exactly which tier resolved. Analyzer and LLM bundle pick up the new sources automatically (scene_summary already handles arbitrary source strings). Startup banner explains the choice when source is yaml or auto; cli / env are silent (user knows what they asked for). One line to stderr (so `diting monitor > log.jsonl` shells stay clean). DITING_SCENE_QUIET=1 silences the banner. Behavioural change: a fresh diting launch on a corp Wi-Fi now shows [office] / [公司] without the user passing any flag. Same on home Wi-Fi -> [home] / [家]. Upgrade-safe because the heuristic falls to `home` whenever it can't classify, matching the v1.5.0 / P1 default exactly. Tests: +30 (10 heuristic, 12 scenes_config, 8 startup resolution). 856 pytest passed. Snapshot regression green. Both openspec validates clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 tasks
chenchaoyi
added a commit
that referenced
this pull request
May 22, 2026
Applies the spec deltas from #116 into the canonical openspec/specs/ tree: - MODIFIED specs/scenes/spec.md — resolution precedence expanded from 3 tiers (cli / env / default) to 5 tiers (cli / env / yaml / auto / default); 6 new scenarios; new documentation of fall-through behaviour for invalid env. - ADDED specs/scenes/spec.md — `scenes.yaml SHALL map networks to scenes` requirement (4 scenarios covering missing file, SSID match, gateway_mac precedence, invalid entry handling). - ADDED specs/scenes/spec.md — `Auto-detect heuristic SHALL classify from observable network signals` requirement (5 scenarios covering Enterprise auth, BSSID density, sparse network, open network not auto-classifying as public). - MODIFIED specs/event-log/spec.md — session_meta `scene_source` field enum extended from {cli, env, default} to {cli, env, yaml, auto, default}; 2 new scenarios for the yaml + auto sources. - ADDED specs/tui-shell/spec.md — `Scene classification SHALL print a one-line banner at startup` requirement (4 scenarios covering auto banner, yaml banner, explicit-flag silence, DITING_SCENE_QUIET). Moves the change dir to openspec/changes/archive/2026-05-22-add-scene-autodetect-and-persistence/. All artifacts done, all tasks complete, validate --specs --strict green (22/22). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chenchaoyi
added a commit
that referenced
this pull request
May 22, 2026
Scene awareness. diting now carries an explicit notion of where
the user is right now, threaded through pollers + the JSONL
session header + the LLM prompt bundle.
Four named scenes (home / office / public / audit), each with:
- per-scene BLE presence_gate_s default (home=5s, office=15s,
public=30s, audit=0s)
- per-scene LLM prior injected into --for-llm prompt template
- chip in the TUI title bar showing the active scene
Resolution precedence (5 tiers, highest first):
1. --scene SCENE CLI flag
2. DITING_SCENE env var
3. scenes.yaml SSID / gateway_mac match (per-network pinning,
mirrors aps.yaml pattern, git-ignored, human-curated only)
4. classify_environment heuristic on the active connection
(WPA-Enterprise auth -> office; >= 30 BSSIDs visible ->
office; otherwise home; public stays opt-in because
captive-portal detection without active probing is
unreliable)
5. home default
scene_source field on session_meta records the tier that won:
{cli, env, yaml, auto, default}. session_meta is a new JSONL
event type written as line 1 of every diting session, carrying
scene + scene_source + diting_version + ssid + gateway_ip +
hostname (BSSID intentionally left out for PII reasons).
PRs bundled: #114 (P1 + P4 scene awareness), #115 (archive),
#116 (P2 auto-detect + scenes.yaml), #117 (archive).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
P1 (#114) shipped scene awareness but kept resolution opt-in — scenes fired only via
--sceneorDITING_SCENE, defaulting tohomeotherwise. Real-world feedback the moment P1 merged: "I'm clearly in an office (many same-name APs, enterprise auth), why does diting still show[家]?" That's the P1 contract working as designed, but not the experience the user wants.P2 closes the gap. Default behaviour flips from "always home unless told otherwise" → "diting figures it out."
Two pieces working together
1.
scenes.yamlper-network persistenceSame idea as
aps.yaml: optional file in cwd (DITING_SCENES_FILEoverrides path), git-ignored, human-curated only. Diting NEVER writes back. Maps SSID → scene, withgateway_macas fallback for SSID-collision cases (eduroameverywhere, multiple homes with the same SSID).2. Auto-detect heuristic
When no CLI / env / yaml decides, diting inspects the active Wi-Fi connection synchronously at startup. Pure-function classifier, no probes, no phone-home:
securitycontains "Enterprise" (case-insensitive)officevisible_bssid_count >= 30(CoreWLAN scan cache)officehomepublicstays opt-in (captive-portal detection without active probing is unreliable).Resolution precedence (5-tier, was 3)
scene_sourceJSONL field extends from{cli, env, default}to{cli, env, yaml, auto, default}—session_metarecords exactly which tier resolved. Analyzer + LLM bundle pick up new sources automatically (the existingscene_summaryhandles arbitrary source strings).Banner
When source is
yamlorauto, one line to stderr explains the choice:CLI / env are silent (user knows what they asked for).
DITING_SCENE_QUIET=1silences for scripts.What's in this PR
openspec/changes/add-scene-autodetect-and-persistence/— proposal / design / tasks + 3 spec deltas (scenes / event-log / tui-shell)src/diting/scenes_config.py(new) —SceneAssignment,SceneRegistry,load_scenes_registry, lookup helperssrc/diting/scene.py—SOURCE_YAML/SOURCE_AUTOconstants,classify_environmentheuristicsrc/diting/cli.py—_resolve_scene_at_startup(5-tier resolver),_gateway_mac_for_router_ip(ARP lookup),_emit_scene_bannersrc/diting/i18n.py— EN + ZH banner stringsscenes.example.yaml(new, repo root).gitignore— addsscenes.yamltests/test_scenes_config.py(new) — 12 teststests/test_scene.py— 10 new heuristic teststests/test_cli.py— 8 new startup + banner testsREADME.md+docs/zh/README.md— extended## Sceneswith auto-detect / yaml subsectionsCHANGELOG.md+docs/zh/CHANGELOG.md—## [Unreleased]→### Addedtests/TESTING.md+docs/zh/TESTING.md— 3 new rows underscenesTest plan
uv run pytest— 856 passed (was 826 in P1; +30 new)uv run python scripts/tui_snapshot.py --mode regression— greenopenspec validate --specs --strict— 22/22 passedopenspec validate add-scene-autodetect-and-persistence --strict— validditingon Meituan corp Wi-Fi without flags, confirm banner readsauto-detected scene: office (WPA2 Enterprise auth)and chip shows[公司]/[office]scenes.yamlpinning Meituan → office, confirm banner switches topinned scene: office (matched "Meituan" in scenes.yaml)What this PR does NOT do
--scene public.Generated with Claude Code