☰ Contents
AISA v2.0 / Technical documentation / build_case_content_docs.py

build_case_content_docs.py

Python · 108 lines · 8,068 bytes · wiki path 10 Architecture/ui/build_case_content_docs.py · download the raw file · cited from Case protocol

Same folder: agent-catalog.json · agent_catalog.py · agent_map_svg.py · build_agent_map.py · build_case_journeys.py · build_case_traces.py · build_operations_examples.py · case-content.json · case-journeys.json · case-runtime-traces.json · case-ui-specs.json · case_content.py · case_ui_model.py · frequent-operations.json · memory-capabilities.json · validate_agent_map.py · validate_case_content.py

"""Keep the catalogue's sample content and current paths with its shared data source."""
from pathlib import Path
from html import escape
import re
import json

from _paths import ARCH,SITE,DOCS_DIRNAME
HERE=Path(__file__).resolve().parent


def current_path(j):
    lookup={s['uid']:s for s in j['paths']['first']}
    show=lambda ids:' → '.join(lookup[x]['stage']+' '+lookup[x]['label'] for x in ids)
    parts=[('['+' ∥ '.join(show(l) for l in g['lanes'])+']') if g['kind']=='parallel' else show(g['steps']) for g in j['groups']['first']]
    owner={'commercial':'Assessment & Offers inside the same Support case','support':'Core classification and communication in the same Support case','data':'Data & Manipulation in the same case','configuration':'accepted Configuration subcase','source':'accepted Development subcase'}[j['ui']['working_module']]
    extra=' Required S7 restoration precedes resolution.' if j['ui']['restoration_required'] else ''
    if j['ui']['working_module']=='data':extra+=' Investigation is conditional; a ready skill or checked answer uses its current preflight without an unnecessary S2 visit.'
    if j['ui']['outcome_type']=='referral':extra+=' The outcome is scoped request/referral communication; no implementation is claimed.'
    return 'Support intake/classification → route → '+owner+': '+' → '.join(parts)+' → Support closure.'+extra


def details(case,j):
    p=j['ui']['content'];list_html=lambda items:'<ol>'+''.join('<li>'+escape(x)+'</li>' for x in items)+'</ol>'
    sections=[('Customer request','<p>'+escape(j['ui']['request'])+'</p>'),
        ('Specific failure illustrated','<p>'+escape(p['failure'])+'</p>'),
        ('Initial solution take','<p>'+escape(p.get('take',p['failure']))+'</p><p>'+escape(p.get('take_decision','The related procedure is a candidate until its current preconditions are checked.'))+'</p>'),
        ('Evidence to establish — investigation or the known skill’s preflight',list_html(p['findings'])),
        ('Return to solution take','<p>'+escape(p['solution'])+'</p><p>Reject this shortcut: '+escape(p['falsifier'])+'</p>'),
        ('Confirmation and application',list_html(p['steps'])),
        ('Verification',list_html(p['checks'])),
        ('Recovery','<p>'+escape(p['recovery'])+'</p>'),
        ('What is retained','<p><b>Memory:</b> '+escape(p['memory'])+'</p><p><b>Skill:</b> '+escape(p['skill'])+'</p><p><b>Tool candidate:</b> '+escape(p['tool'])+'</p><p><b>Next request:</b> '+escape(p['similar'])+'</p>')]
    if p.get('archive_result'):sections.insert(6,('What the archive actually records','<p>'+escape(p['archive_result'])+'</p>'))
    content='<details class="case-content"><summary>Concrete sample content: request, evidence, action and learning [P]</summary><p>'+escape(p['basis'])+'</p>'
    content+=''.join('<p><strong>'+title+'</strong></p>'+body for title,body in sections)
    content+='<p><b>Source basis:</b> '+escape('; '.join(s['label']+' — '+s['path'] for s in p['sources']))+'</p></details>'
    return '<!-- CASE-CONTENT:'+case['id']+' -->\n'+content+'\n<!-- END CASE-CONTENT:'+case['id']+' -->'


def update_catalogues(cases,traces):
    for source in sorted({c['source'] for c in cases}):
        path=ARCH/source;text=path.read_text(encoding='utf-8')
        for c in [c for c in cases if c['source']==source]:
            j=traces[c['id']]['journey'];match=re.search(r'\*\*'+re.escape(c['id'])+r' — [^\n]+',text)
            if not match:raise ValueError('Missing catalogue entry '+c['id'])
            paragraph=re.sub(r'\*Current illustrated path \[P\]:\* .*?(?=\*Journey:\*)','*Current illustrated path [P]:* '+current_path(j)+' ',match.group(0))
            text=text[:match.start()]+paragraph+text[match.end():]
            block=details(c,j);pattern=r'<!-- CASE-CONTENT:'+c['id']+r' -->.*?<!-- END CASE-CONTENT:'+c['id']+r' -->'
            if re.search(pattern,text,re.S):text=re.sub(pattern,lambda _:block,text,flags=re.S)
            else:
                at=match.start()+len(paragraph);text=text[:at]+'\n\n'+block+text[at:]
        path.write_text(text,encoding='utf-8')


def update_studies(traces):
    studies=json.loads((HERE/'case-content.json').read_text(encoding='utf-8'))['studies']
    path=ARCH/'40 Support/Case Studies.md';text=path.read_text(encoding='utf-8')
    for number,entry in studies.items():
        if 'case_id' in entry:
            j=traces[entry['case_id']]['journey'];p={**j['ui']['content'],'request':j['ui']['request']}
        else:p=entry
        row=lambda title,value:'<p><strong>'+title+'</strong></p>'+('<ol>'+''.join('<li>'+escape(x)+'</li>' for x in value)+'</ol>' if isinstance(value,list) else '<p>'+escape(value)+'</p>')
        body=row('Request and first hypothesis',p['request']+' '+p['failure'])+row('Investigation / current preflight',p['findings'])+row('Return to solution take',p['solution'])+row('Confirmation and application',p['steps'])+row('Verification',p['checks'])+row('Learning',p['memory']+' '+p['skill']+' '+p['tool'])
        block='<!-- STUDY-CONTENT:'+number+' -->\n<details class="case-content"><summary>Content carried between the screens [P]</summary><p>The historical evidence above belongs to this case. These are the proposed v2 screen contents and checks; they do not claim a new execution.</p>'+body+'</details>\n<!-- END STUDY-CONTENT:'+number+' -->'
        pattern=r'<!-- STUDY-CONTENT:'+number+r' -->.*?<!-- END STUDY-CONTENT:'+number+r' -->'
        if re.search(pattern,text,re.S):text=re.sub(pattern,lambda _:block,text,flags=re.S)
        else:
            section=re.search(r'^## Case '+number+r'\b.*?(?=^## Case |^## Gap table|\Z)',text,re.M|re.S)
            if not section:raise ValueError('Missing case study '+number)
            at=text.index('**Walk through v2',section.start());text=text[:at]+block+'\n\n'+text[at:]
    path.write_text(text,encoding='utf-8')


def marketing_block(m,j,cid):
    """The goals-section example: one real case, its module detail and its screen."""
    li=lambda items:''.join('<li>'+escape(x)+'</li>' for x in items)
    return ('<article class="goal-example reveal" id="one-case" aria-labelledby="goal-example-title">\n'
      '<div class="goal-example-context">\n'
      '<p class="goal-example-label">'+escape(m['label'])+'</p>\n'
      '<h3 id="goal-example-title">'+'<br>'.join(escape(x) for x in m['heading'])+'</h3>\n'
      '<blockquote>'+escape(m['quote'])+'</blockquote>\n'
      '<p class="goal-example-origin">'+escape(m['origin'])+'</p>\n'
      '<p class="goal-example-today">'+escape(m['today'])+'</p>\n'
      '</div>\n'
      '<div class="goal-example-value">\n'
      '<div><h4>For this customer</h4><p>'+escape(m['customer'])+'</p></div>\n'
      '<div><h4>For the next request</h4><p>'+escape(m['next'])+'</p></div>\n'
      '</div>\n'
      '<div class="goal-example-mock">\n'
      '<h4>The screen the approver works in, with this case’s own content<span class="ge-zoom-tip">hover it for full size</span></h4>\n'
      '<div class="ge-row">'
      '<div class="ge-zoom">'
      '<iframe src="'+DOCS_DIRNAME+'/10-architecture/ui/operations_preview.html?view=decision&amp;compact=1" '
      'title="'+escape(cid)+' plan and decision screen" loading="lazy"></iframe>'
      '</div>\n'
      '<div class="ge-tech"><h4>What it touches in the Configuration module</h4><ul>'+li(m['touches'])+'</ul>'
      '<h4>What has to be proved before it is delivered</h4><ul>'+li(j['ui']['content']['checks'])+'</ul>'
      '<p class="ge-fail"><b>The variant that must fail:</b> '+escape(j['ui']['content']['falsifier'])+'</p></div>'
      '</div>\n'
      '</div>\n</article>')


def update_marketing_content(traces):
    p=SITE/'index.html';text=p.read_text(encoding='utf-8')
    m=json.loads((HERE/'case-content.json').read_text(encoding='utf-8'))['marketing']
    start=text.index('<article class="goal-example');end=text.index('</article>',start)+len('</article>')
    text=text[:start]+marketing_block(m,traces[m['case']]['journey'],m['case'])+text[end:]
    p.write_text(text,encoding='utf-8')